文章目录
- 1. 位置参数
- 2. 可变参数
- 3. 关键字参数
- 4. 命名关键字参数
- 5. 多种参数形式组合
- 6. 代表任意可能参数的参数列表
注: 本文不会把“默认参数”(实际上应该称之为参数默认值才准确)作为一种独立的参数形式讲解,它实际上只是给参数赋予默认值的一种做法,可应用于位置参数和命名关键字参数等多种场合
1. 位置参数
和所有编程语言类似,Python中的函数参数,最基本的一种就是“位置参数”,也就是按其在函数签名中的生命的顺序来定义和识别是那一个参数。
def my_fun(arg1, arg2):
print(f"agr1: [ {arg1} ]")
print(f"agr2: [ {arg2} ]")
my_fun("abc", "xyz")
执行输出:
agr1: [ abc ]
agr2: [ xyz ]
2. 可变参数
不同于参数数量固定的位置参数,在实际编程时,我们经常会遇到参数数量不固定的情形,尽管我们可以使用集合类型来封装数量不确定的参数,但是这样毕竟会多出一步封装集合的操作,比较繁琐,例如这个例子:
def sum(numbers):
sum = 0
for i in numbers:
sum += i
return sum
print(sum([1,2,3]))
针对这种参数数量不确定的情况,绝大多编程语言都会提供可变(不定长)参数特性,其实也就是简化了一小步操作:不需要调用方先封装一个集合,而是直接往参数列表中填参数即可:
def sum(*numbers):
sum = 0
for i in numbers:
sum += i
return sum
print(sum(1,2,3))
python中的可变参数是在参数名前加一个*
,在方法体内该参数被定义为list或tuple类型,然后在调用时,直接输入多个数量不固定的参数,不必先封装成一个集合类型。
本质上,可变参数就是一个小语法糖。
3. 关键字参数
关键字参数可以视作可变参数的一个加强版:可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict(也就是一个map)。
关键字参数也是用于应对参数不固定的场景,但是它比可变参数更强的地方在于:它给出的参数除了值(value)本身,还额外附加了一个参数名作为key。请看出下示例:
def my_fun(arg1, arg2, **args):
print(f"arg1={arg1}")
print(f"arg2={arg2}")
print(f"args={args}")
my_fun('a','b',arg3='c',arg4='d')
输出如下:
arg1=a
arg2=b
args={'arg3': 'c', 'arg4': 'd'}
关键字参数在参数名前加**
(两个),在方法体内该参数被定义为dict类型,然后在调用时,直接输入多个数量不固定的参数=值的KV列表,不必先封装成一个dist类型。*
比较一下的话:可变参数省去了调用方封装list或tuple的操作,关键字参数省去了调用方封装dist的操作。
4. 命名关键字参数
关键字参数虽然灵活,但是调用方可以传入任意命名(key)的参数,有时候,如果函数设计者想要约束这种情况,只希望在给定的参数名集合内传入KV参数列表,此时就需要使用命名关键字参数了。依然是上述的例子,使用命名关键字参数是这样的:
def my_fun(arg1, arg2, *, arg3, arg4):
print(f"arg1={arg1}")
print(f"arg2={arg2}")
print(f"arg3={arg3}")
print(f"arg4={arg4}")
my_fun('a','b',arg3='c',arg4='d')
命名关键字参数的形式是在一系列关键字参数前使用一个*
作为一个分隔符,*后面的参数被视为命名关键字参数。
但是命名关键字参数有非常不灵活的地方,它要求调用方必须传入完成的命名关键字参数列表,而不是一部分,以上面的例子为例:如果使用my_fun('a','b',arg3='c')
,编译就会报错!提示:my_fun() missing 1 required keyword-only argument: 'arg4'
,虽然我们使用使用参数默认值语法来规避这个问题,但显然命名关键字参数已经和关键字参数有本质区别了,后者参数数量是不固定的,而前者已经固化了。
5. 多种参数形式组合
我们可以综合使用多种参数命令方式,但是要注意参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。例如,我们可以这样定义参数列表:
def my_fun(arg1, arg2, arg3='c', *args, **kw):
print(f"arg1={arg1}")
print(f"arg2={arg2}")
print(f"arg3={arg3}")
print(f"args={args}")
print(f"kw={kw}")
my_fun('a','b','c','d', arg5='e')
输出如下:
arg1=a
arg2=b
arg3=c
args=('d',)
kw={'arg5': 'e'}
如果我们调整一下arg3='c', *args, **kw
之间的先后顺序,函数就会报错,也就是前面说的,组合使用不同形式的参数时,顺序必须保持固定!
6. 代表任意可能参数的参数列表
在了解了多种参数形式组合之后,联想此前装饰器一节中,我们介绍的wrap函数,这个函数必须得能包裹任何函数,所以它的参数列表是要接受任何可能的参数组合,并委派给目标函数,这里就面临一个问题:在Python中代表任意可能参数的参数列表应该是什么样的?基于第5节的介绍,按照各种参数形式的出场顺序,我们可以想见:前任意多个位置参数+默认参数+可变参数组合落地的参数列表可以统一使用*args
反向描述,后续可能存在的关键字参数,由于其独特的K=V书写样式,不能纳入*args
,只能单独归类到**kw
,这样归纳起来,实际上就能得到任意可能参数的参数列表:(*args, **kw)
。在上一节介绍装饰器函数时就使用到了它:
def log(func):
@functools.wraps(func)
def wrap(*args, **kw):
print('before call %s():' % func.__name__)
result = func(*args, **kw)
print('after call %s():' % func.__name__)
return result
return wrap