需要从某个可迭代对象中分解出 N 个元素,但该对象的长度可能超过 N,这会导致抛出“分解的值过多(too many values to unpack)”的异常。
解决方案
Python 的星号表达式可以用来解决这个问题。比如,有一组值,你想去掉第一个和最后一个,可以这样:
代码语言:javascript复制>>> x = range(10)
>>> first, *middle, last = x
>>> first
0
>>> last
9
>>> middle
[1, 2, 3, 4, 5, 6, 7, 8]
另外一种情况,假设有一些用户记录,记录由名字、邮件,后面跟着任意数量的电话号码。则可以像下面这样分解记录:
代码语言:javascript复制>>> record = ('Dave', 'dave@example.com', '773-555-1212', '847-555-1212')
>>> name, email, *phone_numbers = record
>>> name
'Dave'
>>> email
'dave@example.com'
>>> phone_numbers
['773-555-1212', '847-555-1212']
>>>
值得注意的是,不管分解出多少个电话号码(甚至是0个),变量 phone_numbers 都是一个列表。这样做的好处是使用到 phone_numbers 变量的代码就不需要做多余的类型检查去确实它是否为列表了。
星号表达式也能用在列表的开始部分。例如:
代码语言:javascript复制>>> x = range(10)
>>> *head, tail = x
>>> head
[0, 1, 2, 3, 4, 5, 6, 7, 8]
>>> tail
9
综上,我们测试了Python的星号表达式用在开头、中间以及结尾。事实上,星号表达式可以用在任意位置。例如:
代码语言:javascript复制>>> x = range(10)
>>> one, two, *any, tail = x
>>> one
0
>>> two
1
>>> any
[2, 3, 4, 5, 6, 7, 8]
>>> tail
9
讨论
星号表达式在迭代对象的长度可变是非常有用,例如下面是一个带标签的元组序列:
代码语言:javascript复制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)
星号解压语法在字符串操作的时候也会很有用,比如字符串的分割。
当和某些特定的字符串处理操作相结合,比如做拆分(splitting)操作时,星号表达式语法所支持的分解操作也非常有用。例如:
代码语言:javascript复制>>> 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
>>>
参考
- 《Python Cookbook》第三版
- http://python3-cookbook.readthedocs.org/zh_CN/latest/