*运算符在Python中是什么意思,比如在zip(*x)或f(**k)这样的代码中?

如何在解释器内部处理它? 它会影响性能吗?它是快还是慢? 什么时候有用,什么时候没用? 它应该用在函数声明中还是调用中?


在函数调用中,单星号将一个列表转换为单独的参数(例如,如果x=[x1,x2,x3], zip(*x)与zip(x1,x2,x3)相同),而双星号将字典转换为单独的关键字参数(例如,f(**k)与f(x=my_x, y=my_y)相同,如果k = {'x':my_x, 'y':my_y}。

In a function definition it's the other way around: the single star turns an arbitrary number of arguments into a list, and the double start turns an arbitrary number of keyword arguments into a dictionary. E.g. def foo(*x) means "foo takes an arbitrary number of arguments and they will be accessible through the list x (i.e. if the user calls foo(1,2,3), x will be [1,2,3])" and def bar(**k) means "bar takes an arbitrary number of keyword arguments and they will be accessible through the dictionary k (i.e. if the user calls bar(x=42, y=23), k will be {'x': 42, 'y': 23})".


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

如果语法*表达式出现在函数调用中,表达式必须求值为序列。这个序列中的元素被当作附加的位置参数来处理;如果有位置参数x1,…, xN,表达式求值为序列y1,…, yM,这相当于调用M+N个位置参数x1,…, xN, y1,…,,

and:

如果语法**表达式出现在函数调用中,表达式必须求值为映射,映射的内容被视为额外的关键字参数。如果关键字既出现在表达式中,又作为显式关键字参数出现,则会引发TypeError异常。


单个星号*将序列/集合解包到位置参数中,所以你可以这样做:

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

values = (1, 2)

s = sum(*values)

这将解包元组,使其实际执行如下:

s = sum(1, 2)

双星号**也有同样的功能,只是使用了一个字典,因此命名了参数:

values = { 'a': 1, 'b': 2 }
s = sum(**values)

你还可以组合:

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)

另请参阅Python文档的第4.7.4节-解包参数列表。


此外,你可以定义函数接受*x和**y参数,这允许函数接受任意数量的位置和/或在声明中没有特别命名的命名参数。

例子:

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

s = sum(1, 2, 3, 4, 5)

或者用**:

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

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

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

同样,你可以结合:

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

我发现当你想要“存储”一个函数调用时,这特别有用。

例如,假设我有一些单元测试函数'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,除了手动执行add(test[0], test[1])之类的操作,这很难看。此外,如果变量的数量是可变的,那么使用所有您需要的if语句,代码可能会变得非常丑陋。

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

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

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