吴良超的学习笔记

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