Python在生物信息学中的应用:从任意长度的可迭代对象中分解元素

2024-02-21 16:28:51 浏览数 (1)

需要从某个可迭代对象中分解出 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)。例如:

代码语言:javascript复制
>>> 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/

0 人点赞