函数调用中**(双星/星号)和*(星号/星号)是什么意思?

问题描述 投票:0回答:4

zip(*x)
f(**k)
这样的代码中,
*
**
分别意味着什么? Python 如何实现该行为,以及对性能有何影响?


另请参阅:将元组扩展为参数。请使用该选项来结束 OP 需要在某个参数上使用

*
并且不知道它存在的问题。同样,对于使用 ** 的情况,请使用
Converting Python dict to kwargs?

请参阅 **(双星/星号)和 *(星号/星号)对参数有何作用?有关参数的补充问题。

python syntax parameter-passing iterable-unpacking argument-unpacking
4个回答
1173
投票

单个星号

*
将序列或集合解包为位置参数。假设我们有

def add(a, b):
    return a + b

values = (1, 2)

使用

*
解包运算符,我们可以编写
s = add(*values)
,这相当于编写
s = add(1, 2)

双星

**
对字典做同样的事情,为命名参数提供值:

values = { 'a': 1, 'b': 2 }
s = add(**values) # equivalent to add(a=1, b=2)

两个运算符可用于同一个函数调用。例如,给定:

def sum(a, b, c, d):
    return a + b + c + d

values1 = (1, 2)
values2 = { 'c': 10, 'd': 15 }

那么

s = add(*values1, **values2)
就相当于
s = sum(1, 2, c=10, d=15)

另请参阅 Python 文档中教程的相关部分


类似地,

*
**
可用于参数。使用
*
允许函数接受任意数量的位置参数,这些参数将被收集到单个参数中:

def add(*values):
    s = 0
    for v in values:
        s = s + v
    return s

现在,当像

s = add(1, 2, 3, 4, 5)
这样调用函数时,
values
将是元组
(1, 2, 3, 4, 5)
(当然,它会产生结果
15
)。

类似地,标有

**
的参数将收到
dict
:

def get_a(**values):
    return values['a']

s = get_a(a=1, b=2)      # returns 1

这允许指定大量可选参数而无需声明它们。

再次强调,两者可以结合使用:

def add(*values, **options):
    s = 0
    for i in values:
        s = s + i
    if "neg" in options:
        if options["neg"]:
            s = -s
    return s
        
s = add(1, 2, 3, 4, 5)            # returns 15
s = add(1, 2, 3, 4, 5, neg=True)  # returns -15
s = add(1, 2, 3, 4, 5, neg=False) # returns 15

29
投票

在函数调用中,单星将列表转换为单独的参数(例如,

zip(*x)
与给定
zip(x1, x2, x3)
x=[x1,x2,x3]
相同),双星将字典转换为单独的关键字参数(例如,
f(**k)
是与给定
f(x=my_x, y=my_y)
k = {'x':my_x, 'y':my_y}
相同。

在函数定义中,情况正好相反:单星将任意数量的参数转换为列表,双星将任意数量的关键字参数转换为字典。例如。

def foo(*x)
表示“foo 接受任意数量的参数,并且可以通过
x
访问它们(即,如果用户调用
foo(1,2,3)
x
将是
(1, 2, 3)
)”,而
def bar(**k)
表示“bar 接受任意数量的关键字参数,并且可以通过
k
访问它们(即,如果用户调用
bar(x=42, y=23)
k
将是
{'x': 42, 'y': 23}
)”。


24
投票

我发现这对于存储函数调用的参数特别有用。

例如,假设我对函数“add”有一些单元测试:

def add(a, b):
    return a + b

tests = { (1,4):5, (0, 0):0, (-1, 3):3 }

for test, result in tests.items():
    print('test: adding', test, '==', result, '---', add(*test) == result)

除了手动执行类似

add(test[0], test[1])
之类的操作之外,没有其他方法可以调用add,这很难看。另外,如果变量数量可变,则代码可能会因为您需要的所有 if 语句而变得非常难看。

另一个有用的地方是定义工厂对象(为您创建对象的对象)。 假设您有一些工厂类,它生成 Car 对象并返回它们。 您可以让

myFactory.make_car('red', 'bmw', '335ix')
创建
Car('red', 'bmw', '335ix')
,然后返回它。

def make_car(*args):
    return Car(*args)

当您想要调用超类的构造函数时,这也很有用。


20
投票

称为扩展调用语法。来自文档

如果函数调用中出现*表达式语法,则表达式的计算结果必须为序列。该序列中的元素被视为附加位置参数;如果有位置参数 x1,..., xN,并且表达式计算结果为序列 y1, ..., yM,则这相当于使用 M+N 位置参数 x1, ..., xN, y1, 的调用。 ..,yM.

和:

如果语法**表达式出现在函数调用中,则表达式必须计算为映射,其内容被视为附加关键字参数。如果关键字同时出现在表达式中并作为显式关键字参数,则会引发 TypeError 异常。

© www.soinside.com 2019 - 2024. All rights reserved.