函数
撰写时间:2025-09-08
修订时间:2025-09-19
定义与调用
使用docstring后,可打印特定函数的__doc__属性值以输出docstring。
可通过注解 (annotation) 来标注函数参数类型及其返回值类型:
嵌套函数
Python支持嵌套函数。
参数与调用
为最大化地方便客户端调用函数,Python对函数参数的类型与顺序作出了众多规定。在本文中,我们将一一剖析相关术语及其内在原理机制。
术语约定
下面是一个函数定义及及其函数调用:
通常,在英文环境中,在函数定义时所出现的函数参数,称为parameters;而在函数调用时所指定的参数,称为arguments。
本文将parameters统一称谓为形参,将arguments统一称谓为实参。
上面代码,在函数定义中,参数a及b均为形参;而在函数调用时,变量num1及num2均为实参。
参考资源
位置形参
位置形参 (positional arguments) 是指在声明函数时按形参在参数列表中出现的先后顺序来确定实参与形参映射的机制。
在定义函数时,将各个形参的名称以逗号,
隔开即可。这是最常见的声明方式。
规则1:在调用时,各个实参将依序赋值于各个位置形参。
规则2:每个位置形参都要求提供相应的实参,不能省略。
规则3:在调用时,实参可以通过赋值语句的方式,显式地列出各个位置形参的名称。这种特性,使得Python的函数调用细节非常清晰。应用规则3时,同样受规则2的约束。
规则4:实参使用赋值语句方式时,实参次序可以与形参次序不一致。但依旧受规则2的约束。
关键字形参
基本规则
在定义函数时,以赋值语句的方式来声明的形参,称为关键字形参 (keyword arguments )。
关键字形参的主要目的是为每个关键字形参均提供一个默认的初始值,以允许客户端在调用时,可以无须显式地提供相应的实参。
规则1:调用时,可像位置形参一样,依序提供各个实参的数值。
规则2:应用规则1时,可从任意位置的形参开始,省略后续所有实参。
规则3:每个关键字形参可以无须提供相应的实参。
规则4:以赋值语句形式提供实参时,无须考虑关键字形参的声明顺序。
位置形参与关键字形参的混同
规则1:在定义函数时,如果函数参数既有位置形参,又有关键字形参,所有的位置形参须出现在所有的关键字形参的前面。
规则2:在调用函数时,必须为每个位置形参显式地提供相应的实参。如果以赋值语句方式为位置形参提供实参,则实参的次序不受约束。
可变长形参
可变长形参 (variadic arguments) 是可以接收任意数量的实参的形参。
基本用法
下面函数,不管客户端传入多少个数值,均能自动求和。
在函数定义中,在形参名称前面加上一个星号*
,则该形参即为可变长形参。
调用时,所有的实参都打包为一个tuple后,再赋值于形参args。因此,形参args的数据类型为tuple。
对于可变长形参,如果实参为一个诸如list的集合,也会自动打包为tuple,而其元素类型为list。
此时,形参args实际上是一个二维数组。
可变长形参与位置形参的混同
当函数参数中同时有位置形参及可变长形参时,由于可变长形参用于接收数量不固定的所有后续的实参,因此可变长形参须排在位置形参之后。
可变长形参与关键字形参的混同
可变长形参实际上也是一种位置形参,当它与关键字形参混同使用时,可暴露出其原形。
参数operator是关键字形参,参数operands是可变长形参,当我们将可变长形参排在关键字形参之后时,将出现编译错误:位置实参位于关键字实参之后
。
上面代码,如果没有调用函数,则不会报错;只有当调用函数时,才会导致报错。
因此,在定义函数时,参数的正确顺序应为:先是可变长形参, 然后才到各个关键字形参。
在调用时,即使省略个别或所有关键字实参,均不影响正常编译。
不同于位置形参与关键字形参的混同中的规则2,我们不能为可变长形参以赋值语句的方式来提供实参。
此时,Python编译器将实参operands识别为关键字实参,从而报错。
鉴于此,当调用函数时,我们也应老老实实地将可变长实参排在关键字实参之前,且不使用赋值语句的方式。
不知Python的后续版本能否统一应对这种情况?
序列的解包
可以在序列之前添加一个*
号,则序列的值将被解包为各个独立的个体,可用以投喂可变长形参。
dict形参
需求
在一个普通的函数中,我们可以声明一个形参,用以接收类型为dict的实参。
这种方式,由调用者负责来构建dict。
也可以使用另一种方式,调用者只需提供各个键值对作为实参,由函数将这些有键值对格式的所有实参自动打包为一个类型为dict的形参,以供函数体使用。
方法是在声明函数参数时,使用**param_name的语法来声明一个形参。本文将这种类型的形参称之为dict形参。
调用函数时,共有2个键值对,形参**kargs将它们打包为一个dict。
这种效果等同于:
但上述代码的局限性在于,我们必须事先定义好name及age这两个形参。
而使用自动打包为dict的形参的好处是,只需定义一个函数,却可以处理任意数量、任意键值对类型的数据。
基本规则
规则1:在函数调用时,对应于dict形参的实参可以省略。
当省略对应实参时,在函数体内将打包为一个空的dict对象。
与其他形参的混同
dict形参排在最后
在与其他形参进行混同时,在函数定义中,dict形参均须排在最后面。
dict形参与关键字形参的识别
由于关键字形参在函数定义中已明确键名,因此,在调用函数时,当实参中出现了不在上述键名之列的的键名,都可被识别为dict实参。故此,在函数调用时,dict实参的顺序可随意排列。
dict解包
在调用函数时,**person称为dict解包,将变量person的各个键值对都分离为独立的键值对;然后,连同gender='male'在内,一起打包为一个新的dict,再传送给demo_func的kargs参数。
快速识别参数类别
定义
如上所述,位置实参既可以位置实参的方式,也可以关键字实参的方式来投喂位置形参。
为规范函数调用顺序,同时也为了更好地识别函数各种参数的类别,Python定义了以下语法:
使用分隔符/
与*
来隔开各种类别的参数。
- 位于
/
之前的,只能以位置实参的方式来投喂; - 位于
/
与*
之间的,既可以位置实参,也可以关键字实参来投喂; - 位于
*
之后的,只能以关键字实参来投喂。
分隔符/
与*
是可选的,可以不使用。因此上面所有的代码均为这种方式。当不提供/
与*
时,实参可以同时使用位置实参或关键字实参的方式向形参传递数值。
应用
限定只能以位置实参的方式来传递参数数值。
在函数定义中,形参name与age的后面有/
字符,则要求相应的实参须以位置实参的方式来投喂。而上面代码实参name却以关键字实参的方式来投喂,因此出错。修正如下:
形参gender由于位于/
之后,因此它可以关键字实参的方式来投喂。
限定只能以关键字实参的方式来传递参数数值。
由于出现了*
号,则位于它之后的3个形参,要求它们对应的实参必须以关键字实参的方式来投喂。实参顺序无关紧要。
函数设计技巧
定义一个fib函数:
根据变量result的不断变化的长度来决定迭代次数。
result的初始元素数量为2个,使用参数n进行切割,以确定返回的result的长度(同时应对当n值为1时的特殊情况)。
一些常用库函数
input
当在 Web 端使用时,input函数将导致弹出一个提示框窗口。
input函数所返回的数据类型为字符串。
