欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  数据库

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

程序员文章站 2024-02-08 20:32:52
...

问题 想从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.com', '773-555-1212', '847-555-1212')
>>> name, email, *phone_numbers = user_record
>>> name
'Dave'
>>> email
'dave@example.com'
>>> 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的强项。因此上述代码在实现中只能算是有学术价值。