解压和 * 运算符

37 投票
5 回答
35799 浏览
提问于 2025-04-16 17:09

Python的文档中给出了这个代码,作为zip的反向操作:

>>> x2, y2 = zip(*zipped)

特别地,

zip()和*运算符一起使用,可以用来解压一个列表。

有人能给我解释一下在这种情况下*运算符是怎么工作的吗?我理解的情况是,*是一个二元运算符,可以用来做乘法或者浅拷贝……但在这里似乎都不是这样。

5 个回答

25

zip(*zipped) 的意思是“把 zipped 中的每个元素作为参数传给 zip”。zip 有点像转置一个矩阵,也就是说,如果你再做一次,就会回到最初的状态。

>>> a = [(1, 2, 3), (4, 5, 6)]
>>> b = zip(*a)
>>> b
[(1, 4), (2, 5), (3, 6)]
>>> zip(*b)
[(1, 2, 3), (4, 5, 6)]
62

虽然hammar的回答解释了在使用zip()函数时如何进行反转,但我们可以更广泛地理解参数解包。假设我们有一个简单的函数,它需要一些参数:

>>> def do_something(arg1, arg2, arg3):
...     print 'arg1: %s' % arg1
...     print 'arg2: %s' % arg2
...     print 'arg3: %s' % arg3
... 
>>> do_something(1, 2, 3)
arg1: 1
arg2: 2
arg3: 3

我们可以不直接写出这些参数,而是先创建一个列表(或者元组),把参数放在里面,然后告诉Python去解包这个列表,把里面的内容当作参数传给函数:

>>> arguments = [42, 'insert value here', 3.14]
>>> do_something(*arguments)
arg1: 42
arg2: insert value here
arg3: 3.14

如果你传的参数不够(或者太多),这依然会正常工作:

>>> arguments = [42, 'insert value here']
>>> do_something(*arguments)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)

/home/blair/<ipython console> in <module>()

TypeError: do_something() takes exactly 3 arguments (2 given)

在定义函数时,你也可以使用相同的方法来接受任意数量的位置参数。这些参数会以元组的形式传给你的函数:

>>> def show_args(*args):
...     for index, value in enumerate(args):
...         print 'Argument %d: %s' % (index, value)
...
>>> show_args(1, 2, 3)
Argument 0: 1
Argument 1: 2
Argument 2: 3

当然,你也可以把这两种方法结合起来使用:

>>> show_args(*arguments)
Argument 0: 42
Argument 1: insert value here

对于关键字参数,你可以用两个星号(**)和一个字典来做类似的事情:

>>> def show_kwargs(**kwargs):
...     for arg, value in kwargs.items():
...         print '%s = %s' % (arg, value)
...
>>> show_kwargs(age=24, name='Blair')
age = 24
name = Blair

当然,你也可以通过字典传递关键字参数:

>>> values = {'name': 'John', 'age': 17}
>>> show_kwargs(**values)
age = 17
name = John

将这两种方法混合使用是完全可以的,你可以在函数中有必需的参数和可选的额外参数:

>>> def mixed(required_arg, *args, **kwargs):
...     print 'Required: %s' % required_arg
...     if args:
...         print 'Extra positional arguments: %s' % str(args)
...     if kwargs:
...         print 'Extra keyword arguments: %s' % kwargs
...
>>> mixed(1)
Required: 1
>>> mixed(1, 2, 3)
Required: 1
Extra positional arguments: (2, 3)
>>> mixed(1, 2, 3, test=True)
Required: 1
Extra positional arguments: (2, 3)
Extra keyword arguments: {'test': True}
>>> args = (2, 3, 4)
>>> kwargs = {'test': True, 'func': min}
>>> mixed(*args, **kwargs)
Required: 2
Extra positional arguments: (3, 4)
Extra keyword arguments: {'test': True, 'func': <built-in function min>}

如果你要接受可选的关键字参数,并且想要设置默认值,记得你是在处理一个字典,因此可以使用它的get()方法,并设置一个默认值,以防这个键不存在:

>>> def take_keywords(**kwargs):
...     print 'Test mode: %s' % kwargs.get('test', False)
...     print 'Combining function: %s' % kwargs.get('func', all)
... 
>>> take_keywords()
Test mode: False
Combining function: <built-in function all>
>>> take_keywords(func=any)
Test mode: False
Combining function: <built-in function any>
23

当像这样使用时,*(星号,有时候也被称为“展开运算符”)是用来从一个列表中提取参数的信号。想要更详细的定义和例子,可以查看这个链接:http://docs.python.org/tutorial/controlflow.html#unpacking-argument-lists

撰写回答