默认情况下,函数的参数传递形式可以是位置参数或是显式的关键字参数。 为了确保可读性和运行效率,限制允许的参数传递形式是有意义的, 这样开发者只需查看函数定义即可确定参数项是仅按位置、按位置也按关键字,还是仅按关键字传递。
函数的定义看起来可以像是这样:
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
----------- ---------- ----------
| | |
| Positional or keyword |
| - Keyword only
-- Positional only
在这里 /
和 *
是可选的。 如果使用这些符号则表明可以通过何种形参将参数值传递给函数:仅限位置、位置或关键字,以及仅限关键字。 关键字形参也被称为命名形参。
- 如果函数定义中未使用
/
和*
,则参数可以按位置或按关键字传递给函数。 - 如果是 仅限位置 的形参,则其位置是重要的,并且该形参不能作为关键字传入。 仅限位置形参要放在
/
(正斜杠) 之前。 - 要将形参标记为 仅限关键字,即指明该形参必须以关键字参数的形式传入,应在参数列表的第一个 仅限关键字 形参之前放置一个
*
。
函数示例
请考虑以下示例函数定义并特别注意 /
和 *
标记:
>>> def standard_arg(arg):
... print(arg)
...
>>> def pos_only_arg(arg, /):
... print(arg)
...
>>> def kwd_only_arg(*, arg):
... print(arg)
...
>>> def combined_example(pos_only, /, standard, *, kwd_only):
... print(pos_only, standard, kwd_only)
第一个函数定义 standard_arg
是最常见的形式,对调用方式没有任何限制,参数可以按位置也可以按关键字传入:
>>> standard_arg(2)
2
>>> standard_arg(arg=2)
2
第二个函数 pos_only_arg
在函数定义中带有 /
,限制仅使用位置形参。:
>>> pos_only_arg(1)
1
>>> pos_only_arg(arg=1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: pos_only_arg() got an unexpected keyword argument 'arg'
第三个函数 kwd_only_args
在函数定义中通过 *
指明仅允许关键字参数:
>>> kwd_only_arg(3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: kwd_only_arg() takes 0 positional arguments but 1 was given
>>> kwd_only_arg(arg=3)
3
而最后一个则在同一函数定义中使用了全部三种调用方式:
>>> combined_example(1, 2, 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: combined_example() takes 2 positional arguments but 3 were given
>>> combined_example(1, 2, kwd_only=3)
1 2 3
>>> combined_example(1, standard=2, kwd_only=3)
1 2 3
>>> combined_example(pos_only=1, standard=2, kwd_only=3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: combined_example() got an unexpected keyword argument 'pos_only'
最后,请考虑这个函数定义,它的位置参数 name
和 **kwds
之间由于存在关键字名称 name
而可能产生潜在冲突:
def foo(name, **kwds):
return 'name' in kwds
任何调用都不可能让它返回 True,因为关键字 'name'
将总是绑定到第一个形参。 例如:
>>> foo(1, **{'name': 2})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() got multiple values for argument 'name'
>>>
但使用 /
(仅限位置参数) 就可能做到,因为它允许 name
作为位置参数,也允许 'name'
作为关键字参数的关键字名称:
def foo(name, /, **kwds):
return 'name' in kwds
>>> foo(1, **{'name': 2})
True
换句话说,仅限位置形参的名称可以在 **kwds
中使用而不产生歧义。
任意的参数列表
最后,最不常用的选项是可以使用任意数量的参数调用函数。这些参数会被包含在一个元组里(参见 元组和序列 )。在可变数量的参数之前,可能会出现零个或多个普通参数。:
def write_multiple_items(file, separator, *args):
file.write(separator.join(args))
一般来说,这些 可变参数
将在形式参数列表的末尾,因为它们收集传递给函数的所有剩余输入参数。出现在 *args
参数之后的任何形式参数都是 ‘仅限关键字参数’,也就是说它们只能作为关键字参数而不能是位置参数。:
>>> def concat(*args, sep="/"):
... return sep.join(args)
...
>>> concat("earth", "mars", "venus")
'earth/mars/venus'
>>> concat("earth", "mars", "venus", sep=".")
'earth.mars.venus'
解包参数列表
当参数已经在列表或元组中但要为需要单独位置参数的函数调用解包时,会发生相反的情况。例如,内置的 range() 函数需要单独的 start 和 stop 参数。如果它们不能单独使用,可以使用 * 操作符 来编写函数调用以便从列表或元组中解包参数:
>>> list(range(3, 6)) # normal call with separate arguments
[3, 4, 5]
>>> args = [3, 6]
>>> list(range(*args)) # call with arguments unpacked from a list
[3, 4, 5]
args = [3,30]
list(range(*args,2))
Out[56]: [3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29]
args1 = (3, 30)
list(range(*args1,2))
Out[58]: [3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29]
同样的方式,字典可使用 **
操作符 来提供关键字参数:
>>> def parrot(voltage, state='a stiff', action='voom'):
... print("-- This parrot wouldn't", action, end=' ')
... print("if you put", voltage, "volts through it.", end=' ')
... print("E's", state, "!")
...
>>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
>>> parrot(**d)
-- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !
Lambda 表达式
可以用 lambda
关键字来创建一个小的匿名函数。这个函数返回两个参数的和: lambda a, b: a+b
。Lambda函数可以在需要函数对象的任何地方使用。它们在语法上限于单个表达式。从语义上来说,它们只是正常函数定义的语法糖。与嵌套函数定义一样,lambda函数可以引用所包含域的变量:
>>> def make_incrementor(n):
... return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42
>>> f(1)
43
上面的例子使用一个lambda表达式来返回一个函数。另一个用法是传递一个小函数作为参数:
>>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
>>> pairs.sort(key=lambda pair: pair[1])
>>> pairs
[(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]
文档字符串
以下是有关文档字符串的内容和格式的一些约定。
第一行应该是对象目的的简要概述。为简洁起见,它不应显式声明对象的名称或类型,因为这些可通过其他方式获得(除非名称恰好是描述函数操作的动词)。这一行应以大写字母开头,以句点结尾。
如果文档字符串中有更多行,则第二行应为空白,从而在视觉上将摘要与其余描述分开。后面几行应该是一个或多个段落,描述对象的调用约定,它的副作用等。
Python 解析器不会从 Python 中删除多行字符串文字的缩进,因此处理文档的工具必须在需要时删除缩进。 这是使用以下约定完成的。 文档字符串第一行 之后 的第一个非空行确定整个文档字符串的缩进量。(我们不能使用第一行,因为它通常与字符串的开头引号相邻,因此它的缩进在字符串文字中不明显。)然后从字符串的所有行的开头剥离与该缩进 “等效” 的空格。 缩进更少的行不应该出现,但是如果它们出现,则应该剥离它们的所有前导空格。 应在转化制表符为空格后测试空格的等效性(通常转化为8个空格)。
下面是一个多行文档字符串的例子:
>>> def my_function():
... """Do nothing, but document it.
...
... No, really, it doesn't do anything.
... """
... pass
...
>>> print(my_function.__doc__)
Do nothing, but document it.
No, really, it doesn't do anything.
函数标注
函数标注 以字典的形式存放在函数的 __annotations__
属性中,并且不会影响函数的任何其他部分。 形参标注的定义方式是在形参名称后加上冒号,后面跟一个表达式,该表达式会被求值为标注的值。 返回值标注的定义方式是加上一个组合符号 ->
,后面跟一个表达式,该标注位于形参列表和表示 def
语句结束的冒号之间。 下面的示例有一个位置参数,一个关键字参数以及返回值带有相应标注:
def f(ham: str, eggs: str = 'eggsdsa') -> str:
print("Annotations:", f.__annotations__)
print("Arguments:", ham, eggs)
return ham + ' and ' + eggs #注意and前后带有空格
f('spam')
Annotations: {'ham': <class 'str'>, 'eggs': <class 'str'>, 'return': <class 'str'>}
Arguments: spam eggsdsa