好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

1.2.从任意长度的迭代中解压元素

问题 想从iterable中解压N个元素,但iterable元素可能多于N个,会导致“to many values to unpack”的异常。 解决方法 Python的 表达式(star expression)可被用来解决这个问题。举例,假如你课程并决定在学期结束时放弃最高和最低分,只取剩下的平均分,如

问题

想从iterable中解压N个元素,但iterable元素可能多于N个,会导致“to many values to unpack”的异常。

解决方法

Python的 表达式(star expression)可被用来解决这个问题。举例,假如你课程并决定在学期结束时放弃最高和最低分,只取剩下的平均分,如果只有4门课程,你可以简单的加压所有的4门,但是如果有24门课程呢? 表达式可以容易的做到:

 def   drop_first_last  (  grades  ): 
     first  ,   *  middle  ,   last   =   grades 
     return   avg  (  middle  ) 
 

另一个用例,假如你有包含姓名、电子邮件和电话号码的用户记录,你可以这样加压记录:

 >>>   record   =   (  'Dave'  ,   'dave@example测试数据'  ,   '773-555-1212'  ,   '847-555-1212'  ) 
 >>>   name  ,   email  ,   *  phone_numbers   =   user_record 
 >>>   name 
 'Dave' 
 >>>   email 
 'dave@example测试数据' 
 >>>   phone_numbers 
 [  '773-555-1212'  ,   '847-555-1212'  ] 
 >>> 
 

值得注意的是,变量 phone_numbers 将永远是个列表,不管解压多少电话号码(包括None),因此任何使用 phone_numbers 的代码不会考虑是否是列表或者任何额外的类型检查。

*变量也可以在列表的开始。举例,假如你有一个反映公司过去8个季度的销售记录的序列,如果你想看最近一个季度的记录是否比前7个季度的平均值高,你可以这么做:

 *  trailing_qtrs  ,   current_qtr   =   sales_record 
 trailing_avg   =   sum  (  trailing_qtrs  )   /   len  (  trailing_qtrs  ) 
 return   avg_comparison  (  trailing_avg  ,   current_qtr  ) 
 

在Python的解释器里,可以看到以下结果:

 >>>   *  trailing  ,   current   =   [  10  ,   8  ,   7  ,   1  ,   9  ,   5  ,   10  ,   3  ] 
 >>>   trailing 
 [  10  ,   8  ,   7  ,   1  ,   9  ,   5  ,   10  ] 
 >>>   current 
 3 
 

讨论

扩展的迭代解包是专为位置或任意长度的迭代序列解包的,通常情况下,这些迭代序列在结构上有已知的组件或者模式(比如数字1后面的会是电话号码)*表达式可以让开发者很容易解压这些模式,而不是想杂耍那样得到迭代序列中的元素。

值得注意的是,*语法对未知长度的元组序列尤其有用。假如有一个标记过的元组序列:

 records   =   [ 
      (  'foo'  ,   1  ,   2  ), 
      (  'bar'  ,   'hello'  ), 
      (  'foo'  ,   3  ,   4  ), 
 ] 

 def   do_foo  (  x  ,   y  ): 
     print  (  'foo'  ,   x  ,   y  ) 

 def   do_bar  (  s  ): 
     print  (  'bar'  ,   s  ) 

 for   tag  ,   *  args   in   records  : 
     if   tag   ==   'foo'  : 
         do_foo  (  *  args  ) 
     elif   tag   ==   'bar'  : 
         do_bar  (  *  args  ) 
 

*解压在和处理特定字符串结合使用时也很有用,比如:

 >>>   line   =   'nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false' 
 >>>   uname  ,   *  fields  ,   homedir  ,   sh   =   line  .  split  (  ':'  ) 
 >>>   uname 
 'nobody' 
 >>>   homedir 
 '/var/empty' 
 >>>   sh 
 '/usr/bin/false' 
 >>> 
 

有时你可能会想解压一些值然后抛弃掉。你不能在解压时只指定一个*,你可以使用普通的要丢弃掉的变量名,比如 _ 或者 ign (ignore)。举例:

 >>>   record   =   (  'ACME'  ,   50  ,   123.45  ,   (  12  ,   18  ,   2012  )) 
 >>>   name  ,   *  _  ,   (  *  _  ,   year  )   =   record 
 >>>   name 
 'ACME' 
 >>>   year 
 2012 
 >>> 
 

*解压和列表处理在不同的功能语言上有一定相似性。例如,你有一个列表,你可以很容易的将其分解为头尾部件:

 >>   items   =   [  1  ,   10  ,   7  ,   4  ,   5  ,   9  ] 
 >>>   head  ,   *  tail   =   items 
 >>>   head 
 1 
 >>>   tail 
 [  10  ,   7  ,   4  ,   5  ,   9  ] 
 >>> 
 

有人可能会想写这么一个函数,为了完成某种聪明的递归算法而执行这样的分割:

 >>>   def   sum  (  items  ): 
 ...       head  ,   *  tail   =   items 
 ...       return   head   +   sum  (  tail  )   if   tail   else   head 
 ... 
 >>>   sum  (  items  ) 
 36 
 >>> 
 

然而,要注意的是,因为固有的限制,递归真的不是Python的强项。因此上述代码在实现中只能算是有学术价值。

查看更多关于1.2.从任意长度的迭代中解压元素的详细内容...

  阅读:44次