【重温Python】1.2-数据结构与算法-解压可迭代对象赋值给多个变量

问题:

如果一个可迭代对象的元素个数超过变量个数时,会抛出一个”ValueError”,那么怎样才能从这个可迭代对象中解压出 N 个元素出来?

解决方案

在 Python 中可以使用星号表达式来解决上述问题。

考虑下面一个情况,在学期末的时候,想统计一下学期末的平均成绩,但是排除掉第一个和最后一个分数。
如果一共只有四个分数,可能就直接去简单的手动赋值,但是如果有 24 个呢?
这个时候想好表达式就派上用场了。

def drop_first_last(grades):
    first, *middle, last = grades
    return sum(middle) / len(middle)

上述代码为伪代码,其中 grades 为一个 list 对象。grades 中存储成绩,我们使用星号表达式截取除第一个和最后一个的成绩列表。

考虑另外一种情况,假设现在有一些用户的记录列表,每条记录包含一个名字、邮件,接着就是不确定数量的电话号码。
可以像下面这样分解这些记录:

record = ('Dave', 'dave@example.com', '773-555-1212', '847-555-1212')
name, email, *phone_numbers = record
  1. phone_numbers 变量永远都是列表类型,不管解压的电话号码数量是多少(包含 0 个)。
  2. 任何使用到 phone_numbers 变量的代码就不需要做多余的类型检查(它一定是 list 类型)

扩展

  1. 扩展的迭代解压语法是专门为解压不确定个数或任意个数元素的可迭代对象而设计的。通常,这些可迭代对象的元素结构有确定的规则,星号表达式让开发人员很容易的利用这些规则来解压出元素来。
  2. 值得注意的是,星号表达式在迭代元素为可变长元组的序列时时很有用的。
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(':')
print(uname)
print(fields)
print(homedir)
print(sh)

有时候,想解压一些元素之后丢弃他们,不能简单地就使用*,但是可以简单的使用一个普通的废弃名称
例如’_’或者’ign’等

record = (‘ACME’, 50, 123.45, (12,18,2012))
name, *_, (*_, year) = record
print(name)
print(year)

在很多函数式语言中,星号解压语法跟列表处理有许多相似之处。比如,如果你有一个列表, 你可以很容易的将它分割成前后两部分:

items = [1, 10, 7, 4, 5, 9]
head, *tail = items
print(items)
print(head)