读书笔记 摘写自《python学习手册》第18章 参数
本文使用python版本3.6
python函数的参数
参数在python中总是通过复制传入的。传入的对象被赋值给了在def头部的变量名。而在这一模型的上层,python提供了额外的工具,来改变调用中参数对象和头部的参数名的配对关系。
1.位置次序:
从左至右进行匹配
>def f(a,b,c):
> print(a,b,c)
>f(1,2,3)
1 2 3
2.关键字参数:
更具体地指定传递内容与传递名称的关系。关键字参数允许我们通过名称匹配,而不是基于位置。
关键字参数在调用中起到了数据标签的作用。
>f(c=3,b=2,a=1)
1 2 3
3.默认值参数:
默认值参数允许我们让特定的参数变为可选的,如果没有传入值的话,在函数运行前参数就会被赋予默认值。
>>> def f(a,b=2,c=3): print(a,b,c)
当调用这个函数时,我们必须为a提供值,无论是用基于位置的参数还是关键字参数实现。然而,为b和c提供值是可选的。如果我们不给b和c传递值,他们会默认地分别赋值为2和3。
>>>f(1)
>1 2 3
>>>>f(a=1)
>1 2 3
当给函数传递两个值的时候,只有c得到默认值,并且当有三个值传递时,不会使用默认值:
>>>f(1,4)
>1 4 3
>>>f(1,4,5)
>1 4 5
最后,这是关键字参数和默认参数一起使用后的情况。因为它们都破坏了通常的从左至右的基于位置映射,关键字参数从本质上允许我们跳过带默认值的参数:
>>>f(1,c=6)
>1 2 6
这里,a通过位置拿到了1,c通过关键字拿到了6,而b,在两者之间,通过默认值拿到了2。
混合使用关键字参数和默认值参数
4.可变长参数 args
“*
”和“**
”作为最后两种匹配的扩展,旨在让函数支持接受任意多的参数。它们都可以出现在函数定义或是函数调用中,并且它们在两种位置下有着相关的目的。
函数定义中:收集参数
第一种用法,是在函数定义中把不能匹配的基于位置的参数收集到一个元组中:
def f(*args): print(args)
当这个函数被调用时,python将所有基于位置的参数收集到一个新的元组中,并将这个元组赋值给变量args。因为它是一个普通的元组对象,所以能被索引或在一个for循环中遍历等。
def f(**args): print(args)
“**
”的特性与上面的类似,但是它只对关键字参数有效,它将这些关键字参数收集到一个新的字典中,这个字典之后将能够用一般的字典工具处理,也就是说,“**
”形式允许你将关键字参数转换成字典,从而让你能使用诸如keys调用、字典迭代器遍历等。(当传入关键字参数时,这基本上与dict调用类似,但区别在于“**
”会返回一个新的字典)
函数调用中:解包参数
调用函数时使用“*
”语法,在这种上下文中,它与函数定义中的意思相反。也就是说,它会解包参数的集合,而不是创建参数的集合。例如,我们能给一个函数传递含有四个参数的元组,并且让python自动将这个元组解包成相应的参数。
>>> def func(a,b,c,d):print(a,b,c,d)
...
>>> args = (1,2)
>>> args += (3,4)
>>> func(*args)
1 2 3 4
“**
”会以键/值对的形式把一个字典解包为独立的关键字参数;
>>> args = {'a':1,'b':2,'c':3}
>>> args['d'] = 4
>>> func(**args)
1 2 3 4
>>>
也可以和一般函数、基于位置的参数以及关键字参数混合使用。
>>> func(*(1,2),**{'d':4,'c':3})
1 2 3 4
>>> func(1,*(2,3),**{'d':4})
1 2 3 4
>>> func(1,c=3,*(2,),**{'d':4})
1 2 3 4
>>> func(1,*(2,3),d=4)
1 2 3 4
>>> func(1,*(2,),c=3,**{'d':4})
1 2 3 4
泛化地使用函数
很多程序需要以一种泛化的形式来调用任意的函数,即在运行前并不知道函数的名称和参数。实际上,特殊的“可变长参数”调用的真正强大之处在于,在编写一段脚本之前不需要知道一个函数调用需要多少参数。例如,你可以使用if逻辑来从一系列函数和参数列表中选择,并且泛化地调用其中的任何一个:
if sometest:
action,args = func1,(1,)
else:
action,args = func2,(1,2,3)
...etc...
action(*args)
这种就能同时利用“*
”的形式以及函数本身是对象的事实(也就是说,函数可以用任意的变量名来引用和调用)。更一般地说,每当你无法预计参数列表时,这种可变长参数调用语法都是很有用的。
python3.x的keyword-only参数
python3.x将函数头部的顺序规则一般化,以允许我们指定keyword-only参数——即必须只按照关键字传入并且永远不会被基于位置参数来填充的参数。如想要一个函数既处理任意多个参数,又接受可选的配置项的话,这种技术就能派上用场。
从语法上讲,keyword-only参数编写为出现在参数列表中*args之后的有名的参数。所有这些参数都必须在调用中使用关键字语法来传递。例如,在如下的代码中,a可以按照名称或者位置传入,b收集所有其他的基于位置参数,而c必须只按照关键字传递。
Python 3.6.10 |Anaconda, Inc.| (default, Jan 7 2020, 21:14:29)
[GCC 7.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def kwonly(a,*b,c):
... print(a,b,c)
...
>>> kwonly(1,2,c=3)
1 (2,) 3
>>> kwonly(a=1,c=3)
1 () 3
>>> kwonly(1,2,3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: kwonly() missing 1 required keyword-only argument: 'c'
>>>
也可以在参数列表中单独使用“*
”字符,表示函数不会接受一个可变长度的参数列表,但是仍然期待跟在“*
”后面的所有参数都作为关键字参数传入。例如下面函数中,a同样可以按照位置或者名称传入,但是b和c必须按照关键字传入,而且不允许其他额外的基于位置传入:
>>> def kwonly(a,*,b,c):
... print(a,b,c)
...
>>> kwonly(1,c=3,b=2)
1 2 3
>>> kwonly(c=3,b=2,a=1)
1 2 3
>>> kwonly(1,2,3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: kwonly() takes 1 positional argument but 3 were given
>>> kwonly(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: kwonly() missing 2 required keyword-only arguments: 'b' and 'c'
>>>
也可以对keyword-only参数使用默认值。例下:a可以按照名称或者位置传入,b和c是可选的,如果使用必须按照关键字传入:
>>> def kwonly(a,*,b='spam',c='ham'):
... print(a,b,c)
...
>>> kwonly(1)
1 spam ham
>>> kwonly(1,c=3)
1 spam 3
>>> kwonly(a=1)
1 spam ham
>>> kwonly(c=3,b=2,a=1)
1 2 3
>>> kwonly(1,2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: kwonly() takes 1 positional argument but 2 were given
实际上,带默认值的keyword-only是可选的,但是,那些没有默认值的keyword-only参数真正地变成了函数必需的keyword-only参数:
>>> def kwonly(a,*,b,c='spam'):
... print(a,b,c)
...
>>> kwonly(1,b='eggs')
1 eggs spam
>>> kwonly(1,c='eggs')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: kwonly() missing 1 required keyword-only argument: 'b'
>>> kwonly(1,2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: kwonly() takes 1 positional argument but 2 were given
>>> def kwonly(a,*,b=1,c,d=2):
... print(a,b,c,d)
...
>>> kwonly(3,c=4)
3 1 4 2
>>> kwonly(3,c=4,b=5)
3 5 4 2
>>> kwonly(3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: kwonly() missing 1 required keyword-only argument: 'c'
>>> kwonly(1,2,3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: kwonly() takes 1 positional argument but 3 were given
>>>
顺序规则
最后,注意keyword-only参数必须指定在一个单个星号后面,而不是两个星号——有名参数不能出现在**
args任意关键字形式后面,并且一个“**
”不能独自出现在参数列表中。这两种做法都将产生语法错误:
>>> def kwonly(a,**pargs,b,c):
File "<stdin>", line 1
def kwonly(a,**pargs,b,c):
^
SyntaxError: invalid syntax
>>> def kwonly(a,**,b,c):
File "<stdin>", line 1
def kwonly(a,**,b,c):
^
SyntaxError: invalid syntax
这意味着在一个函数头部,keyword-only参数必须写在**args任意关键字形式之前,且当二者都有时,必须编写在*
args任意位置形式之后。每当一个参数名出现在*
args之前,它可能基于位置的默认值参数,而不是keyword-only参数:
>>> def f(a,*b,**d,c=6): print(a,b,c,d)
File "<stdin>", line 1
def f(a,*b,**d,c=6): print(a,b,c,d)
^
SyntaxError: invalid syntax
>>> def f(a,*b,c=6,**d): print(a,b,c,d)
...
>>> f(1,2,3,x=4,y=5)
1 (2, 3) 6 {'x': 4, 'y': 5}
>>> f(1,2,3,x=4,y=5,c=7)
1 (2, 3) 7 {'x': 4, 'y': 5}
>>> f(1,2,3,c=7,x=4,y=5)
1 (2, 3) 7 {'x': 4, 'y': 5}
>>> def f(a,c=6,*b,**d): print(a,b,c,d)
...
>>> f(1,2,3,x=4)
1 (3,) 2 {'x': 4}
实际上,在函数调用中类似的顺序规则也是成立的:当传入keyword-only参数时,它们可以出现在一个**args形式任何位置。keyword-only参数可以编写在*
args之前或者之后,也可以包含在**
args之内:
>>> def f(a,c=6,*b,**d): print(a,b,c,d)
...
>>> f(1,2,3,x=4)
1 (3,) 2 {'x': 4}
>>> def f(a,*b,c=6,**d): print(a,b,c,d)
...
>>> f(1,*(2,3),**dict(x=4,y=5))
1 (2, 3) 6 {'x': 4, 'y': 5}
>>> f(1,*(2,3),**dict(x=4,y=5),c=7)
1 (2, 3) 7 {'x': 4, 'y': 5}
>>> f(1,*(2,3),**dict(x=4,y=5,c=7))
1 (2, 3) 7 {'x': 4, 'y': 5}
>>>