参数类型
Python函数的参数类型一共有五种,分别是:
- POSITIONAL_OR_KEYWORD(位置参数或关键字参数)
- VAR_POSITIONAL(可变参数)
- KEYWORD_ONLY(关键字参数)
- VAR_KEYWORD(可变关键字参数)
- POSITIONAL_ONLY(位置参数)
下面用举几个例子解释一下这5个参数类型的含义:
POSITIONAL_OR_KEYWORD如其名所见,既可以用位置传参,也可以用关键字传参,并且他没有任何*的声明
>>> def foo(name): ... print(name) ... >>> foo("hello") hello >>> foo(name="hello") hello
VAR_POSITIONAL是可变参数,通过*来声明,它会把接收到的值存入一个元组
>>> def foo(*args): ... print(args) ... >>> foo(1, 2, 3, 4, 5) (1, 2, 3, 4, 5)
KEYWORD_ONLY只能通过关键字传参,这种参数会在VAR_POSITIONAL参数类型的后面,而且不带**前缀,如同语义,只能通过指定关键字来传参,不可以用位置传参
>>> def foo(n1, *, n2): ... print(n1, n2) ... >>> foo("hello", n2="world") hello world
VAR_KEYWORD是可变关键字参数,通过前缀**来声明,这种参数类型可以接收0个或多个参数,并存入一个字典
>>> def foo(**kwargs): ... for key, value in kwargs.items(): ... print("%s=%s" % (key, value)) ... >>> foo(a=1, b=2, c=3) a=1 b=2 c=3
POSITIONAL_ONLY是第五个参数类型,但是它已经不重要了,因为高版本的Python无法创建一个POSITIONAL_ONLY类型的参数,但是有些使用C语言实现且不接收关键字参数的函数(如divmod)支持
从下面的例子,我们可以看到,新定义的foo函数,每个参数都对应到上面的一个类型
>>> def foo(name, *args, middle=None, **kwargs): ... print("name:", name) ... print("args:", args) ... print("middle:", middle) ... print("kwargs:", kwargs) ... >>> foo("hello", 1, 2, 3, middle="world", a=1, b=2, c=3) name: hello args: (1, 2, 3) middle: world kwargs: {'a': 1, 'b': 2, 'c': 3} >>> my_foo = {"name": "hello", "middle": "world", "a": "1", "b": "2", "c": "3"} >>> foo(**my_foo) name: hello args: () middle: world kwargs: {'a': '1', 'b': '2', 'c': '3'} >>> from inspect import signature >>> sig = signature(foo) >>> for name, param in sig.parameters.items(): ... print(param.kind, ":", name, '=', param.default) ... POSITIONAL_OR_KEYWORD : name = <class 'inspect._empty'> VAR_POSITIONAL : args = <class 'inspect._empty'> KEYWORD_ONLY : middle = None VAR_KEYWORD : kwargs = <class 'inspect._empty'>
参数绑定
将函数的参数绑定到一个字典上
>>> def foo(name, *args, middle=None, **kwargs): ... print("name:", name) ... print("args:", args) ... print("middle:", middle) ... print("kwargs:", kwargs) ... >>> my_foo = {"name": "hello", "middle": "world", "a": "1", "b": "2", "c": "3"} >>> from inspect import signature >>> sig = signature(foo) >>> bound_args = sig.bind(**my_foo) >>> for name, value in bound_args.arguments.items(): ... print(name, '=', value) ... name = hello middle = world kwargs = {'a': '1', 'b': '2', 'c': '3'} >>> del my_foo["name"] >>> bound_args = sig.bind(**my_foo) Traceback (most recent call last): ... TypeError: missing a required argument: 'name'
在inspect模块的帮助下,展示了Python数据模型把实参绑定给函数调用的形参的机制,这与解释器使用的机制相同,当我们删除字典中的name,执行时会报错缺少name参数。
再者,我们使用operator模块进行参数绑定
>>> from operator import methodcaller >>> s = "The time has come" >>> upcase = methodcaller("upper") >>> upcase(s) 'THE TIME HAS COME' >>> hiphenate = methodcaller("replace", " ", "-") >>> hiphenate(s) 'The-time-has-come'
如上,methodcaller创建的函数会在对象上调用参数指定的方法,我们可以自己建立一个对象并生成自己的方法,再用methodcaller调用
class Person: def __init__(self, name): self.name = name def say(self, text): print(self.name, 'say:', text) p = Person("John") person_say = methodcaller("say", "hello") person_say(p)
最后会打印:
John say: hello
使用function.partial冻结参数
function.partial这个高阶函数用于部分应用函数,部分应用是指,基于一个函数创建一个新的可调用对象,把原函数的某些参数固定
>>> from operator import mul >>> from functools import partial >>> triple = partial(mul, 3) >>> triple(7) 21 >>> list(map(triple, range(1, 10))) [3, 6, 9, 12, 15, 18, 21, 24, 27]
如上,我们使用mul创建了triple函数,把第一个参数固定为3,然后返回一个可调用的对象,再传入不同的参数