在Python中,可變參數的傳遞使用*args和**kwargs來實作,其中:
- *args表示任意個位置參數(positional argument),被表示為一個隻讀的數組(tuple);
- **kwargs表示任意個關鍵字參數(keyword argument),被表示為一個字典(dict)。
例如:
>>> def foo(*args, **kwargs):
... print("*args:\t\t", args)
... print("**kwargs:\t", kwargs)
...
>>>
>>> foo()
*args: ()
**kwargs: {}
>>> foo(1)
*args: (1,)
**kwargs: {}
>>> foo(1, 2)
*args: (1, 2)
**kwargs: {}
>>>
>>> foo(a=1, b=2)
*args: ()
**kwargs: {'a': 1, 'b': 2}
>>>
>>> foo(1, 2, a=1, b=2, c=3)
*args: (1, 2)
**kwargs: {'a': 1, 'b': 2, 'c': 3}
>>>
>>> foo(1, 2, [3, 4, 5], a=1, b=2, c={'A':1, 'B':2, 'C':3})
*args: (1, 2, [3, 4, 5])
**kwargs: {'a': 1, 'b': 2, 'c': {'A': 1, 'B': 2, 'C': 3}}
>>>
>>> foo(1, a=1, 2, b=2, 3, c=3)
File "<stdin>", line 1
SyntaxError: positional argument follows keyword argument
>>>
注意: 位置參數(*args)必須在關鍵字參數(**kwargs)的前面。
另外,*args和**kwargs都是可以無限次地向下傳遞的(這一點類似Bash裡的"$@"),例如:
- foo_args.py
1 #!/usr/bin/python
2
3 from __future__ import print_function
4 import sys
5
6 def l2_foo(*args):
7 print(">>L2:\t\t*args =", args, "\n")
8
9 def l1_foo(head, *args):
10 print(" >L1: head =", head, "\t*args =", args)
11 l2_foo(*args)
12
13 def main(argc, argv):
14 l1_foo(1)
15 l1_foo(1, 2)
16 l1_foo(1, 2, 3)
17
18 return 0
19
20 if __name__ == '__main__':
21 argv = sys.argv
22 argc = len(argv)
23 sys.exit(main(argc, argv))
注意L6, L9 和 L11:
6 def l2_foo(*args):
..
9 def l1_foo(head, *args):
..
11 l2_foo(*args)
- 運作foo_args.py
$ ./foo_args.py
>L1: head = 1 *args = ()
>>L2: *args = ()
>L1: head = 1 *args = (2,)
>>L2: *args = (2,)
>L1: head = 1 *args = (2, 3)
>>L2: *args = (2, 3)
最後,給出一個使用*args的更有工程意義的例子:
- foo.py
1 #!/usr/bin/python
2 from __future__ import print_function
3 import sys
4
5 class Foo(object):
6 def __init__(self, name, oid):
7 self.name = name
8 self.oid = oid
9
10 def get_name(self):
11 return self.name
12
13 def set_name(self, name):
14 self.name = name
15
16 def get_info(self):
17 return "name = %s, oid = %d" % (self.name, self.oid)
18
19 def set_info(self, name, oid):
20 self.name = name
21 self.oid = oid
22
23 class Bar(object):
24 def __init__(self, name, oid):
25 self.foo = Foo(name, oid)
26
27 def __op_foo(self, method, *args):
28 func = getattr(self.foo, method)
29 try:
30 prop = func(*args)
31 return prop
32 except Exception as e:
33 print(e)
34
35 def foo_set_name(self, name):
36 return self.__op_foo('set_name', name)
37
38 def foo_get_name(self):
39 return self.__op_foo('get_name')
40
41 def foo_set_info(self, name, oid):
42 return self.__op_foo('set_info', name, oid)
43
44 def foo_get_info(self):
45 return self.__op_foo('get_info')
46
47 def get_oid(self):
48 return self.foo.oid # XXX: Ugly but simple for demo
49
50 def foo_arg0():
51 b = Bar('Jack', 12345)
52 s = b.foo_get_name()
53 print("foo_arg0: name = %s, oid = %d" % (s, b.get_oid()))
54
55 def foo_arg1():
56 b = Bar('Jack', 12345)
57 o = b.foo_set_name('Lynn')
58 print("foo_arg1: return", o)
59 s = b.foo_get_name()
60 print("foo_arg1: name = %s, oid = %d" % (s, b.get_oid()))
61
62 def foo_arg2():
63 b = Bar('Jack', 12345)
64 o = b.foo_set_info('Mary', 54321)
65 print("foo_arg2: return", o)
66 s = b.foo_get_info()
67 print("foo_arg2: %s" % s)
68
69 def main(argc, argv):
70 if argc != 2:
71 sys.stderr.write("Usage: %s <func ID>\n" % argv[0])
72 return 1
73
74 func_id = int(argv[1])
75 exec('foo_arg%d()' % func_id)
76
77 return 0
78
79 if __name__ == '__main__':
80 argv = sys.argv
81 argc = len(argv)
82 sys.exit(main(argc, argv))
- 運作foo.py
$ ./foo.py 0
foo_arg0: name = Jack, oid = 12345
$ ./foo.py 1
foo_arg1: return None
foo_arg1: name = Lynn, oid = 12345
$ ./foo.py 2
foo_arg2: return None
foo_arg2: name = Mary, oid = 54321
小結:在函數的參數清單中,零個*表示普通的位置參數, 一個*表示元組(tuple), 兩個*表示字典(dict)。
1 '*' * 0: arg : regular arg
2 '*' * 1: *args: tuple (i.e. readonly list)
3 '*' * 2: **args: dict