越来越发现 python 真是一门灵活的语言。
可变参数列表 *args
和 **kwargs
是我们经常在一些函数定义中见到的,那么我们实际如何利用它们呢?我们自己的函数中怎么使用呢?
事实上,如果函数参数中含有它们,只需要分别将它们视作列表和字典即可,用法也类似。
*args
来看一个例子:
1
2
3
|
def foo(*args):
for arg in args:
print(f"arg={arg})
|
实际中,如果我们想要接收不知数量的多个参数,但对参数的名称没有要求的话(强调这一点是为了与 **kwargs
做区别),比如说我们想写一个求任意个参数中最大值的函数,就可以这样写:
1
2
3
4
|
from functools import reduce
def max(*values):
return reduce(lambda x,y: x if x > y else y, values)
|
这里需要注意,*args
只是一个约定俗成的参数名称,实际中只要用一个星号 *
接参数,这个参数就是一个可变参数。
在上面的函数中,我们使用了 reduce
,从前到后逐个比较,取得其中的最大值,可见 values
在这里和一个普通的列表没有区别。
执行示例:
1
2
3
4
|
>>> max(1)
1
>>> max(-1, 2, 0, -3, 4, 2)
4
|
再看下面的函数:
1
2
|
def prtArgs(*args):
print(args)
|
执行示例:
1
2
|
>>> print(3, '个', False)
(3, '傻', False)
|
*args
就说这么多,有趣的是 **kwargs
的用法。
**kwargs
从名字就可以看出一二,它是一个‘keyword args’,也就是带有关键字的,首先看它的用法:
1
2
3
|
def foo(**kwargs):
for k, v in kwargs.items():
print(f"{k}: {v}")
|
执行示例:
1
2
3
4
5
6
7
8
|
>>> foo(a=1, b='2', c=False)
a: 1
b: 2
c: False
>>> foo(1,3,2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() takes 0 positional arguments but 3 were given
|
可见,如果要填充 **kwargs
的参数,必须使用 k=v
的形式,也就是要指配一个参数名,而 kwargs.items()
就会返回一个字典,其键为参数名,值为参数值,再看一个例子:
1
2
3
4
|
def foo(a, **kwargs):
print(f"a={a}")
for k, v in kwargs.items():
print(f"{k}:{v}")
|
加了一个固定参数 a
,如果我们为 **kwargs
再指定一个 a
会怎样呢?
1
2
3
4
|
>>> foo(3, a='2')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() got multiple values for argument 'a'
|
可见,**kwargs
中的参数不能与其他参数同名,而这也说明它里面的参数具有同等效力(废话)。
话不多说,我们考虑这样一种情况:我们定义了一个拥有很多变量的类,大概长这样:
1
2
3
4
5
6
7
|
class Jumbled:
__a = 1
__b = 2
__c = 3
__d = 4
j = Jumbled()
|
而我们在修改 j
的时候,有时候要修改 a
,有时候要修改 d
,每次一个,不会同时修改,而它们又是私有的,不能外部修改,所以我们会考虑用一个这样的函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
def alter(self, a=None, b=None, c=None, d=None):
if j:
if a is not None:
self.__a = a
if b is not None:
self.__b = b
if c is not None:
self.__c = c
if d is not None:
self.__d = d
def __str__(self):
return f"a={self.__a} b={self.__b} c={self.__c} d={self.__d} "
|
但是只有四个参数就搞得这么复杂,10 个参数的话就会有很多无用代码,怎么简化呢?我们可以考虑使用 **kwargs
,就可以这样写:
1
2
3
4
5
6
7
8
9
10
11
12
|
class Jumbled:
__a = 1
__b = 2
__c = 3
__d = 4
def alter(self, **kwargs):
for k, v in kwargs.items():
exec('self._Jumbled__' + k + ' = v')
def __str__(self):
return f"a={self.__a} b={self.__b} c={self.__c} d={self.__d} "
|
执行一下:
1
2
3
4
|
>>> j = Jumbled()
>>> j.alter(b=0)
>>> print(j)
a=1 b=0 c=3 d=4
|
利用 **kwargs
,只将要修改的变量填进去就可以进行修改,我们也可以直接使用 __setattr__
方法,例如:
1
2
3
4
5
6
7
8
9
10
11
12
|
class Jumbled:
a = 1
b = 2
c = 3
d = 4
def alter(self, **kwargs):
for k, v in kwargs.items():
self.__setattr__(k, v)
def __str__(self):
return f"a={self.a} b={self.b} c={self.c} d={self.d} "
|
执行一下:
1
2
3
4
|
>>> j = Jumbled()
>>> j.alter(a=5, d=11)
>>> print(j)
a=5 b=2 c=3 d=11
|
可见,*args
和 **kwargs
大大增加了我们编码的灵活度,简化了很多编程工作,还有更多的用法就等你自己来发掘啦!