Python 星形和双星运算符在函数调用中的含义是什么?

Python 星形和双星运算符在函数调用中的含义是什么?,python,syntax,parameter-passing,iterable-unpacking,argument-unpacking,Python,Syntax,Parameter Passing,Iterable Unpacking,Argument Unpacking,在Python中,*运算符是什么意思,例如在zip(*x)或f(**k)之类的代码中 s = sum(1, 2, c=10, d=15) 在解释器内部如何处理它 它会影响性能吗?是快还是慢 什么时候有用,什么时候不有用 它应该在函数声明或调用中使用吗 在函数调用中,单星将列表转换为单独的参数(例如zip(*x)与zip(x1,x2,x3)ifx=[x1,x2,x3]),双星将字典转换为单独的关键字参数(例如f(**k)与f(x=my_x,y=my_y)ifk={ s = sum(1, 2, c

在Python中,
*
运算符是什么意思,例如在
zip(*x)
f(**k)
之类的代码中

s = sum(1, 2, c=10, d=15)
  • 在解释器内部如何处理它
  • 它会影响性能吗?是快还是慢
  • 什么时候有用,什么时候不有用
  • 它应该在函数声明或调用中使用吗

  • 在函数调用中,单星将列表转换为单独的参数(例如
    zip(*x)
    zip(x1,x2,x3)
    if
    x=[x1,x2,x3]
    ),双星将字典转换为单独的关键字参数(例如
    f(**k)
    f(x=my_x,y=my_y)
    if
    k={

    s = sum(1, 2, c=10, d=15)
    

    在函数定义中,情况正好相反:单星将任意数量的参数转换为列表,双起点将任意数量的关键字参数转换为字典。例如,
    def foo(*x)
    表示“foo接受任意数量的参数,并且可以通过列表x访问它们(即,如果用户调用
    foo(1,2,3)
    x
    将是
    [1,2,3]
    ),
    def bar(**k)
    意味着“bar包含任意数量的关键字参数,它们可以通过字典k访问(即,如果用户调用
    bar(x=42,y=23)
    k
    将是
    {code>{x':42,'y':23}/code>”.

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

    s = sum(1, 2, c=10, d=15)
    
    如果函数调用中出现语法*表达式,则表达式必须计算为一个序列。此序列中的元素被视为附加位置参数;如果存在位置参数x1,…,xN,且表达式计算为一个序列y1,…,yM,则这相当于使用M+N位置参数x1的调用,…,xN,y1,…,yM

    s = sum(1, 2, c=10, d=15)
    
    以及:

    s = sum(1, 2, c=10, d=15)
    
    如果语法**表达式出现在函数调用中,则表达式必须计算为映射,其内容将被视为其他关键字参数。如果关键字同时出现在表达式和显式关键字参数中,则会引发TypeError异常

    s = sum(1, 2, c=10, d=15)
    

    单星
    *
    将序列/集合解压为位置参数,因此可以执行以下操作:

    def sum(a, b):
        return a + b
    
    values = (1, 2)
    
    s = sum(*values)
    
    s = sum(1, 2, c=10, d=15)
    
    这将解压元组,使其实际执行为:

    s = sum(1, 2)
    
    s = sum(1, 2, c=10, d=15)
    
    双星
    **
    仅使用字典和命名参数执行相同操作:

    values = { 'a': 1, 'b': 2 }
    s = sum(**values)
    
    s = sum(1, 2, c=10, d=15)
    
    您还可以组合:

    def sum(a, b, c, d):
        return a + b + c + d
    
    values1 = (1, 2)
    values2 = { 'c': 10, 'd': 15 }
    s = sum(*values1, **values2)
    
    s = sum(1, 2, c=10, d=15)
    
    将按以下方式执行:

    s = sum(1, 2, c=10, d=15)
    
    另请参阅Python文档的一节

    s = sum(1, 2, c=10, d=15)
    

    此外,您可以定义函数以获取
    *x
    **y
    参数,这允许函数接受声明中未明确命名的任意数量的位置参数和/或命名参数

    s = sum(1, 2, c=10, d=15)
    
    例如:

    s = sum(1, 2, c=10, d=15)
    
    def sum(*values):
        s = 0
        for v in values:
            s = s + v
        return s
    
    s = sum(1, 2, 3, 4, 5)
    
    或使用
    **

    s = sum(1, 2, c=10, d=15)
    
    def get_a(**values):
        return values['a']
    
    s = get_a(a=1, b=2)      # returns 1
    
    这允许您指定大量可选参数,而无需声明它们

    s = sum(1, 2, c=10, d=15)
    
    同样,您可以组合:

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

    一个小点是:这些不是运算符。运算符在表达式中用于从现有值创建新值(例如,1+2变为3)。这里的*和**是函数声明和调用语法的一部分。

    我发现这对于“存储”函数调用特别有用

    s = sum(1, 2, c=10, d=15)
    
    例如,假设我对函数“add”进行了一些单元测试:

    s = sum(1, 2, c=10, d=15)
    
    def add(a,b):返回a+b
    测试={(1,4):5,(0,0):0,(-1,3):3}
    对于测试,结果为tests.items():
    打印“测试:添加”,测试“=”,结果“--”,添加(*test)=结果
    
    除了手动执行类似于
    add(test[0],test[1])
    的操作之外,没有其他方法可以调用add。这很难看。此外,如果变量数量可变,那么代码可能会变得非常难看,包含您需要的所有if语句

    s = sum(1, 2, c=10, d=15)
    
    另一个有用的地方是定义Factory对象(为您创建对象的对象)。 假设您有一些类工厂,它生成Car对象并返回它们。 您可以将其设置为myFactory.make_car('red','bmw','335ix')
    创建
    car('red','bmw','335ix')
    ,然后返回它

    s = sum(1, 2, c=10, d=15)
    
    def make_car(*args):
    返回车(*args)
    

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

    只需在教科书的答案中添加一个脚注-在语法支持到来之前,内置的
    apply()就实现了相同的功能
    function为什么需要这个,函数不能在不展开的情况下迭代提供的列表吗?当然可以,但是您必须调用它:
    s=sum((1,2,3,4,5))
    s=sum([1,2,3,4,5])
    *values
    选项使调用看起来像是接受了许多参数,但它们被打包到函数代码的集合中。真正的好处是:如果需要可变数量的参数,您可以编写不可能的函数。例如,C的printf函数,它有1+n argu在Python中,初学者可以编写def printf(string_template,*args)并继续前进。如果(意外地:p)用一个*而不是两个*解包一个字典,会发生什么情况?它似乎做了一些事情,好像出现了一个元组,但它是什么并不那么明显。(编辑:好的,我认为答案是它只是解包键,值被丢弃)最后一个例子暗示*和**不仅在解包,而且在打包!请参阅这篇优秀的页面附录:我认为这应该用“*函数调用语法”来表达。它们不是运算符,但会让人混淆,因为有一个
    *
    ***
    运算符与此语法无关。@Ian Bicking:你说得对,参数列表中的*和**是纯语法(标记)。注:对于
    s = sum(1, 2, c=10, d=15)