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

What do ** (double star/asterisk) and * (star/asterisk) mean in a function call?

提问人:psihodelia 提问时间:5/27/2010 最后编辑:Karl Knechtelpsihodelia 更新时间:10/6/2023 访问量:270764

问:

在像 or 这样的代码中,和 分别是什么意思?Python 如何实现这种行为,对性能有什么影响?zip(*x)f(**k)***


Смотритетакже: 将元组扩展为参数。请使用该问题来关闭 OP 需要在参数上使用 * 并且不知道它存在的问题。同样,对于使用 ** 的情况,请使用 Converting Python dict to kwargs?

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

python 语法 参数-传递 iterable-unpacking 参数-解包

评论

0赞 wds 5/27/2010
附录:stackoverflow.com/questions/1141504/......
4赞 Ian Bicking 5/28/2010
我认为这应该表述为“* 函数调用语法”。它们不是运算符,尽管会造成混淆,因为有一个与此语法无关的 and 运算符。***
1赞 P. Ortiz 11/1/2015
@Ian Bicking:你完全正确,参数列表中的 * 和 ** 是纯语法(标记)。
3赞 ShadowRanger 3/23/2019
注意:对于 PEP 448:其他解包泛化特定内容(例如 或者),您需要在元组、列表和集合定义中读取星号,在字典定义中读取双星号,这特定于函数调用和函数定义之外的使用。对于早期的 PEP 3132,请参阅 Python 中的多次解包分配(当您不知道序列长度时)。[*a, b, *c]{**d1, **d2}

答:

29赞 sepp2k 5/27/2010 #1

在函数调用中,单个星号将列表转换为单独的参数(例如 与给定的相同),双星将字典转换为单独的关键字参数(例如 与给定的 相同。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}

在函数定义中,情况正好相反:单星号将任意数量的参数转换为列表,双星号将任意数量的关键字参数转换为字典。例如 意思是“foo 接受任意数量的参数,它们将通过 (即如果用户调用 , 将是 )” ,意思是“bar 接受任意数量的关键字参数,它们将通过 (即如果用户调用 , 将是 )”访问。def foo(*x)xfoo(1,2,3)x(1, 2, 3)def bar(**k)kbar(x=42, y=23)k{'x': 42, 'y': 23}

20赞 Mark Byers 5/27/2010 #2

它被称为扩展调用语法。从文档中:

如果语法 *expression 出现在函数调用中,则表达式的计算结果必须为序列。此序列中的元素被视为附加位置参数;如果存在位置参数 x1,...,xN,并且表达式的计算结果为序列 y1、...、yM,则等效于具有 M+N 位置参数 x1、...、xN、y1、...、yM 的调用。

和:

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

评论

3赞 Jeremy Brown 5/27/2010
只是在教科书答案中添加一个脚注 - 在语法支持到来之前,使用内置函数实现了相同的功能apply()
1178赞 Lasse V. Karlsen 5/27/2010 #3

单个星号将序列或集合解压缩为位置参数。假设我们有*

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 文档中教程的相关部分


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

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

评论

10赞 Martin Beckett 5/27/2010
你为什么需要这个,函数不能在不扩展的情况下迭代提供的列表吗?
38赞 Lasse V. Karlsen 5/27/2010
当然可以,但随后您必须调用它: 或者 ,该选项使调用看起来像是需要多个参数,但它们被打包到函数代码的集合中。s = sum((1, 2, 3, 4, 5))s = sum([1, 2, 3, 4, 5])*values
15赞 IceArdor 11/20/2013
这是真正的好处:如果你需要有可变数量的参数,你可以编写原本不可能实现的函数。例如,C 的 printf 函数具有 1+n 个参数,对于任何初学者来说,作为练习编写起来都是很棘手的。在 Python 中,初学者可以编写 def printf(string_template, *args) 并继续前进。
2赞 Ben Farmer 9/15/2017
如果你(也许是不小心:p)解开一本只有一个*而不是两个*的字典,会发生什么?它似乎在做些什么,似乎出现了一个元组,但它是什么并不那么明显。(编辑:好的,我认为答案是它只是解压缩密钥,值被丢弃)
2赞 H.C.Chen 2/17/2019
最后一个例子意味着 * 和 ** 不仅在做开包,而且还在做包装!看到这个优秀的页面 codingame.com/playgrounds/500/...
24赞 Donald Miner 5/28/2010 #4

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

例如,假设我对函数“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,除了手动执行类似 的事情,这很丑陋。此外,如果变量数量可变,则代码可能会变得非常难看,其中包含您需要的所有 if 语句。add(test[0], test[1])

另一个有用的地方是定义 Factory 对象(为您创建对象的对象)。 假设您有某个类 Factory,它生成 Car 对象并返回它们。 你可以这样创建,然后返回它。myFactory.make_car('red', 'bmw', '335ix')Car('red', 'bmw', '335ix')

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

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

评论

6赞 eksortso 5/28/2010
我喜欢你的例子。但是,我认为 -1 + 3 == 2。
8赞 Donald Miner 5/29/2010
我故意在里面放了一些会失败的东西:)