Python 函数参数解包,*
和**
操作符有一个非常棒但有点神秘的功能,那就是用来从序列和字典中“解包”函数参数。
下面来定义一个简单的函数作为例子:
def print_vector(x, y, z):
print('<%s, %s, %s>' % (x, y, z))
从中可以看到,该函数接受三个参数(x
、y
和z
)并美观地打印出来。使用这个函数能在程序中漂亮地打印三维向量:
>>> print_vector(0, 1, 0)
<0, 1, 0>
如果用其他数据结构来表示三维向量,那么使用print_vector
函数进行打印就会出问题。例如用元组或列表表示三维向量的话,在打印时就必须明确指定每个组件的索引:
>>> tuple_vec = (1, 0, 1)
>>> list_vec = [1, 0, 1]
>>> print_vector(tuple_vec[0],
tuple_vec[1],
tuple_vec[2])
<1, 0, 1>
使用普通函数调用加上多个参数既笨拙也没有必要。如果能够将向量对象“炸开”成三个组件,一次性将所有内容传递给print_vector
函数,那岂不是更好?
(当然,也可以简单地重新定义print_vector
,让其只接受一个表示向量对象的参数。但这里只是要举一个简单的例子,所以先忽略这个方法。)
幸好Python中用*
操作符进行函数参数解包能更好地处理这种情况:
>>> print_vector(*tuple_vec)
<1, 0, 1>
>>> print_vector(*list_vec)
<1, 0, 1>
在函数调用时,在可迭代对象前面放一个*
能解包这个参数,将其中的元素作为单独的位置参数传递给被调用的函数。
这种技术适用于任何可迭代对象,包括生成器表达式。在生成器上使用*
操作符会消耗生成器中的所有元素,并将它们传递给函数:
>>> genexpr = (x * x for x in range(3))
>>> print_vector(*genexpr)
*
操作符用于将元组、列表和生成器等序列解包为位置参数。除此之外,还有用于从字典中解包关键字参数的**
操作符。假设用下面这个字典对象表示前面的向量:
>>> dict_vec = {'y': 0, 'z': 1, 'x': 1}
那么可以将该字典传递给print_vector
,然后使用**
操作符解包:
>>> print_vector(**dict_vec)
<1, 0, 1>
由于字典是无序的,因此解包时会匹配字典键和函数参数:x
参数接受字典中与'x'
键相关联的值。
自Python 3.6开始,字典是有序的,但仅仅是指插入顺序,不是某种“自动排序”。
如果使用单个星号(*
)操作符来解包字典,则所有的键将以随机顺序传递给函数:
>>> print_vector(*dict_vec)
<y, x, z>
Python的函数参数解包功能带来了很多灵活性。也就是说,不一定要为程序所需的数据类型实现一个类,使用简单的内置数据结构(如元组或列表)就足够了,这样有助于降低代码的复杂度。
Python 函数参数解包 关键要点
-
*
和**
操作符可用于从序列和字典中“解包”函数参数。 -
高效使用参数解包有助于为模块和函数编写更灵活的接口。