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

796 投票
5 回答
277642 浏览
提问于 2025-04-15 23:14

在像 zip(*x)f(**k) 这样的代码中,*** 分别是什么意思呢?Python 是怎么实现这种功能的?这样做对性能有什么影响呢?


另请参见:将元组展开为参数。请使用这个链接来关闭那些需要在参数上使用 * 但不知道它存在的问题。同样,对于使用 ** 的情况,请使用 将 Python 字典转换为关键字参数?

有关参数的补充问题,请参见 双星号(**)和星号(*)在参数中有什么作用?

5 个回答

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]),没有其他方法,这样看起来很麻烦。而且,如果参数的数量不固定,代码就会变得很复杂,因为你需要写很多 if 语句。

这个方法还有一个好处,就是在定义工厂对象时特别有用(工厂对象就是用来创建其他对象的对象)。假设你有一个叫做 Factory 的类,它可以创建汽车对象并返回它们。你可以让 myFactory.make_car('red', 'bmw', '335ix') 这个调用创建一个 Car('red', 'bmw', '335ix') 对象,然后返回它。

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

这在你想要调用父类的构造函数时也很有用。

30

在调用函数的时候,单个星号(*)可以把一个列表拆分成多个独立的参数,比如说 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})。

1193

一个单独的星号 * 可以把一个序列或集合拆开,变成位置参数。假设我们有:

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

撰写回答