1. Python中参数的分类
- 位置参数:
def test(a, b)
, 其中,a,b就是位置参数。调用函数时,要保证实参和形参的数量和个数相同。 - 命名参数(关键字参数):
*
后的参数即为关键字参数,传参的时候,必须以name=value
的格式传:def test(*, a, b)
; 调用:test(a=1, b=2)
- 缺省参数:有默认值的参数。
def test(a, b=2)
- 不定长参数:
def test(*args, **kwargs)
这里重点强调一下不定长参数:
*args
: 接收数据并用元组封装**kwargs
: 仅接收name=value
格式的数据,以字典的方式封装
def test(a, *args, **kwargs):
print(a)
print(args)
print(kwargs)
test(1, 2, 3, 4, n=10, m=90)
输出
1
(2, 3, 4)
{'n': 10, 'm': 90}
再来讨论一下*
的作用:解包
看个例子
def test(a, *args, **kwargs):
print(a)
print(*args)
print(*kwargs)
test(1, 2, 3, 4, n=10, m=90)
输出
1
2 3 4
n m
对元组对象的作用就是解包,对字典的作用是提出每一个键。
那么**
又会是什么样的作用呢?
这里做了几个实验,由于**
是无法输出他的结果的,但是可以通过实验过程大概猜出他的作用:
将字典变成name=value
的形式
我们知道,不定长参数只接收name=value
形式的参数,如下
def test(a, *args, **kwargs):
print(a)
print(*args)
print(*kwargs)
test(1, 2, 3, 4, {'n':1, 'm':2})
输出
1
2 3 4 {'n': 1, 'm': 2}
尽管有一个字典类型的对象,但是由于不是name=value
格式的,因此**kwargs
不接受。
def test(a, *args, **kwargs):
print(a)
print(*args)
print(*kwargs)
test(1, 2, 3, 4, **{'n':1, 'm':2})
输出
1
2 3 4
n m
可以看到,**
的作用其实就是将{'n':1, 'm':2}
转换成了n=1, m=2
的形式。
再用一个例子证明一下
def test(n, m):
print(n)
print(m)
test(**{'n':1, 'm':2})
结果
1
2
即test(**{'n':1, 'm':2})
就相当于是test(n=1, m=2)
。因此,形参n和m能够准确无误的收到字典中的值。
2. 偏函数
介绍完python函数中的各种参数后,就可以了解一下偏函数了。因为对于不同类型的参数,偏函数的使用会有稍许的不同。
偏函数: 可为原函数中的参数拟定一个初始值,并将带着初始值的原函数返回
用法
from functools import partial
obj = partial(func, *args, **kwargs)
-> func : 原函数对象
举个使用例子
from functools import partial
def test(a):
return a
t = partial(test, 1)
t() # 等价于test(1)
即每次调用t()
的时候,就是调用了test(1)
。很适合于每次调用函数都要传进去大量的一样的参数,可定义一个偏函数解决重复代码。
接着要搞清楚不同类型的参数对于偏函数的影响
2.1 位置参数
当原函数中的形参为位置参数的时候,偏函数所定义的参数就会被锁死,无法更改。
比如
from functools import partial
def test(a, b):
print(a, b)
t = partial(test, 1, 2)
t() # 1 2
t(5, 7) # 报错
此时,调用偏函数t时,a,b的值被固定死为1,2。调用t()
,无法再向t中传入参数更改a和b的值。
2.2 关键字参数
当定义偏函数时,以关键字的方式初始化参数的值,则调用偏函数时,可以以关键字传参的方式更改参数值
如下
from functools import partial
def test(a, b):
print(a, b)
t = partial(test, a=1, b=2)
t(a=5, b=7) # 5 7
2.3 可变长参数
当形参为可变长参数的时候,定义偏函数时初始化的参数值,会被插入可迭代对象中,如果调用偏函数时又传递了新的参数,则会添加到队尾
如下
from functools import partial
def test(*args, **kwargs):
print(args) # (1, 2, 3, 1, 2, 3)
print(kwargs) # {'a': 1, 'b': 7, 'm': 5}
t = partial(test, 1, 2, 3, a=1, b=2) # b=2
t(1, 2, 3, m=5, b=7) # b=7,覆盖上一行的b=2
这里要注意当后续调用时以name=value
传入的参数的name
和定义偏函数时,初始化的name
冲突的时候,会覆盖掉。
3. 总结
那么偏函数到底有什么用呢?
说实话,具体的应用场景我也不知道。我认为可能当你面临需要大量调用某个函数,而且这个函数的参数很多并且每次都不变或仅有少部分参数的值变动时,你可以直接把他再定义成一个偏函数,可能会比较方便。
当然,其他的博主的文章中有这么一个例子:
大致就是为求和函数的结果再加10,他用了偏函数,代码如下
def sum(*args):
s = 0
for _ in args:
s += _
print(s)
sum(1, 2, 3, 4, 5) # 15
上面就是一个求和函数,当你需要使每次运算结果都再加10的时候,可以这么做
from functools import partial
def sum(*args):
s = 0
for _ in args:
s += _
print(s)
s = partial(sum, 10)
s(1, 2, 3, 4, 5) # 25
这里args初始会带一个值10,每次做求和运算的时候都会自动加上一个10