python 中 * 与 ** 的参数传递

在 python 中,经常可以看到定义函数或调用函数时 f(*arg)f(**args) 类型的参数,本文主要讲述这两个形式的参数的含义以及应用。

定义函数时参数加上 ***

首先这两个类型的参数都表示不确定具体参数个数,怎么理解这句话呢?通常在定义函数的时候,定义了几个参数,调用是也要传入几个参数 (默认参数除外,可传可不传),但是只要在定义函数的时候将参数写成 *** 的形式,就可以传入多个参数。如下面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> def a(*args):
... print type(args)
... for i in args:
... print i
...
>>> a(1,2,3,4)
<type 'tuple'>
1
2
3
4
>>> a([3,2,3])
<type 'tuple'>
[3, 2, 3]
>>> a()
<type 'tuple'>

从上面的例子可以看到,通过 * 声明的参数在调用是可以传入 0~n 个参数,且不管传入的参数为何类型,在函数内部都被存放在以形参名为标识符的 tuple 中,无法进行修改

同理,通过 ** 声明的参数也是可以传入多个参数,但是传入的参数类型需要为 k1=v1,k2=v2..... 的类型,且参数在函数内部将被存放在以形式名为标识符的 dictionary 中,这种方法在需要声明多个默认参数的时候特别有用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> def a(**args):
... print type(args)
... print args
... for k, v in args.items():
... print k,v
... print args['k1']
...
>>> a(k1=1, k2=2, k3=3, k4=4)
<type 'dict'>
{'k3': 3, 'k2': 2, 'k1': 1, 'k4': 4}
k3 3
k2 2
k1 1
k4 4
1

下面显示了如何通过这两个参数使代码变得简洁 (注意 *args**kwargs 可以同时在函数的定义中,但是 *args 必须在 **kwargs 前面)

1
2
3
4
5
6
7
8
9
10
11
def sum(*values, **options):
s = 0
for i in values:
s = s + i
if "neg" in options and 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

除此之外,*args**kwargs 也可以和命名参数一起混着用。命名参数首先获得参数值,然后所有的其他参数都传递给 *args**kwargs . 命名参数在列表的最前端。例如:

def table_things(titlestring, *args, **kwargs)

调用函数时参数加上 ***

除了在定义函数时可以加上 ***, 还可以在调用函数时加上 ***, 表示将输入的 集合(序列)类型参数拆开,见下面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def sum(a, b):
return a + b

# values = set()
# values.add(1)
# values.add(2)

values = (1, 2)

# values = [1,2]

# values = {1:3, 2:4}

s = sum(*values)

无论是集合、列表、元组还是字典, 在作为参数输入时加上 *,表示将里面的元素拆开,然后一个个传进去,所以上面执行的结果相当于 s = sum(1, 2), 由于字典比较特殊,传入参数时只会拆开 key 然后传入。下面结合上面定义函数时参数加上 * 来讲述这个 * 的含义,例子如下:

1
2
3
4
5
6
7
8
>>> def sum(*args):
... print args[0]
...
>>> val = (1,2)
>>> sum(val)
(1, 2)
>>> sum(*val)
1

上面的 sum 函数输出传入的第一个参数,由于 sum(val) 将整个 val 元组作为参数传入,相当于 sum((1,2)), 所以会输出 (1,2); 而 sum(*val) 则会将 val 拆开,相当于 sum(1,2),因此输出为 1

而在调用参数时加上 **, 作用也是将传入的参数拆开,只是输入的参数必须为字典,且每个 key 必须要为函数的某个形参,key 对应的 value 为该参数的值。详见下面的例子:

1
2
3
4
5
6
7
8
>>> def parrot(voltage, state='a stiff', action='voom'):
... print("-- This parrot wouldn't", action, end=' ')
... print("if you put", voltage, "volts through it.", end=' ')
... print("E's", state, "!")
...
>>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
>>> parrot(**d)
-- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !

上面的 parrot(**d) 相当于 parrot(voltage = "four million", state = "bleedin' demised", action = "VOOM") 通过上面的方法,可以先将所有参数用字典封装,再通过 ** 传递。

结合定义函数时参数加上 ** 有以下例子:

1
2
3
4
5
6
7
8
>>> def b(**args):
... for k,v in args.items():
... print k,v
...
>>> val = {'keke':1, 'hehe':2}
>>> b(**val)
keke 1
hehe 2

参考: http://stackoverflow.com/questions/2921847/what-does-the-star-operator-mean-in-python https://docs.python.org/3/tutorial/controlflow.html#unpacking-argument-lists