天天看點

[Python學習筆記-004] 可變參數*args和**kwargs

在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      

繼續閱讀