前言
python中函数是一等对象,地位非常重要,类定义中的类方法、静态方法、实例方法本质就是函数。本部分主要记录自定义函数,自定义函数有两种类型,一种是lambda表达式(lambda 参数列表: 表达式
;lambda表达式本质是匿名函数,匿名函数是指没有为函数对象绑定函数名称,其参数就是参数列表,返回值就是表达式的值)。
另一种是def语句,重点是def语句,需要理解def语句中的参数、调用、返回值、以及函数的属性。
def语句
def function_name(*args, **kwargs):
""" document string """
function_body
return [expression]
执行|编译
函数的定义体是一组可执行语句(executable statement),当编译函数体后,会生成函数对象,函数对象中封装了函数体的定义逻辑,并将函数名称绑定到函数对象上。
当函数被调用时,函数对象会自动引用调用处的全局命名空间(global namespace),正式由于对调用处全局命名空间的引用,函数对象才能识别函数对象外的变量
。对调用处全局空间的引用应该是依赖__global__属性。注意函数定义并不执行函数体,只有当函数被调用时才会执行函数体。
另外在函数定义中,可以在def语句之前添加“装饰器表达式”,对于装饰器语法糖可参考Python装饰器语法部分。
参数
关于函数的参数列表,首先需要区分几组概念:
1)“形式参数”是指函数定义时的参数名统称,可以将形式参数理解为占位符号;“实际参数”是指函数调用时,传入函数的实际值。
2)函数定义时,支持为形式参数指定默认值。当函数调用时,如果不提供实际参数值,则对应的形式参数就采取默认值。默认值会在函数编译后写入函数对象的__defaults__属性中。注意函数定义中,形式参数名=默认值,对应的形式参数不一定是关键词参数。注意区分该种定义形式与函数调用时关键词参数的区别。
3)“位置参数”、“关键词参数”是在函数调用层面的概念,当以key=value
形式传递参数,对应的形式参数又称为“关键词参数”。在关键词参数中,有一种特殊情况,就是“仅关键词参数”,其意义是传递实参给形参时,必须以key=value
的形式传递。
知道形参与实参的区别、以及位置参数与关键词参数的区别后,在函数定义中还需要注意“是否为形参设置单星号”,用于收集任意个实参值,“是否为形参设置双星号”,收集任意个关键字参数。带星号、双星号的形式参数,星号、双星号并非形式参数名的一部分
,在函数定义体内,直接使用星号、双星号后面的标识符就可以。另外星号标识的形式参数,在函数被调用时,会初始化为一个空元组,双星号标识的形式参数在函数被调用时,初始化为一个字典。
形式参数、函数返回值可以定义标注信息(annotation),标注信息作为函数的元数据,不参与任何计算,定义的标注信息存储在函数的__annotation__属性中。为形式参数添加标注信息的方式为arg: expression = expression
,即使用冒号加一个表达式;为函数返回值添加标注信息的方式为,在参数列表与冒号之间添加-> expression
。另外需要注意,参数与返回值的标注信息,在函数编译后就写入__annotation__属性,与函数文档字符串写进__doc__、默认值写入__defaults__类似,并非是在函数被调用时才写入。
调用
调用函数时,函数对象做的第一件事就是为所有形式参数赋值,只有当参数列表完全解析正确,才会执行函数体中的逻辑并返回值。
关于参数列表的解析,即将实际参数值赋值给位置参数,需要了解位置参数、仅关键词参数、关键词参数的概念及区别。1)位置参数是指实际参数值传入函数的位置,没有标识符标识其属于哪个形式参数,该位置参数将传递给对应位置的形式参数;2)仅关键词参数是后来加入的特征,当参数列表中包含带单星号、双星号的形式参数时,夹在单星号形式参数与双星号形式参数之间的参数,必须以关键字的形式传递,否则其值就会被单星号形式参数、或者双星号形式参数收集;3)关键词参数,是指以key=value的形式传递参数值,其中的key就是函数定义参数列表中的参数名,value值将赋值给名为key的形式参数。
形式参数的解析顺序如下:1)为位置参数赋值,2)为关键词参数赋值,包括仅关键字参数与关键字参数;3)如果还有形式参数没有赋值,则取函数定义中的默认值,如果默认值都被取完,还有形式参数未被赋值,则抛出TypeError。根据参数解析顺序,在函数定义中,关键词参数一定要定义在位置参数后面,否则会产生语法错误
。
函数调用中的参数解析还支持“解包技术”,*expression或者**expression
,其中*expression中的表达式必须为可迭代对象, **expression中的表达式必须为mapping对象。
当调用函数时传入*expression、**expression,形式参数解析顺序不变,但要注意:1)*expression会提取出可迭代对象中的所有元素,按照传入顺序作为位置参数,*expression会在**expression、关键词参数之前被解析,即使它被后传入。2)**expression会解包关键词参数,如解析出来的关键词已经存在了,则会抛出TypeError。
返回值
除了函数体执行时抛出异常,否则函数总是会返回值,若不指定return子句,则返回None,最好指定return子句,即便是返回None。函数体执行return子句后就会停止,即便后面还有代码未执行,另外一个函数体内可以包含多条return子句。
属性
属性 | 含义 | 读写类型 |
---|---|---|
__code__ | code对象,表示编译的函数体 | writable |
__globals__ | 对字典的引用,该字典包含函数定义模块中的全局变量 | readable |
__dict__ | 一个命名空间,包含函数对象的任意属性 writable | |
__closure__ | 包含函数的绑定的自由变量 | writable |