这里介绍Python的函数如何定义、调用
函数定义/调用
1 | print("------------------- 函数定义-----------------------") |
函数实参
位置实参
所谓位置实参,即是按照函数定义中的顺序传递的参数
1 | print("----------------位置实参--------------------") |
关键字实参
所谓关键字实参,即是通过指定参数名称来传递的参数。无需按照函数定义中的位置顺序依次传入
1 | print("----------------关键字实参--------------------") |
混用位置实参、关键字实参
当位置实参、关键字实参混用时,必须保证 位置实参在前、关键字实参在后 的顺序
1 | print("----------------混用位置实参、关键字实参--------------------") |
函数形参
普通形参
对于普通形参,调用时通过位置实参、关键字实参传值均可
1 | print("------------------- 普通形参 -----------------------------") |
*形式的可变形参
对于*形式的可变形参,其会收集未匹配的位置实参 并以元组tuple形式进行存储。形参名称习惯上使用args
1 | print("------------------- *形式的可变形参 -----------------------------") |
**形式的可变形参
对于**形式的可变形参,其会收集未匹配的关键字实参 并以字典dict形式进行存储。形参名称习惯上使用kw
1 | print("------------------- **形式的可变形参 -----------------------------") |
限定位置形参
Python 3.8版本引入限定位置形参。具体地,在形参列表中使用 /斜杠 进行分隔。其中 /斜杠 前面的形参都是限定位置形参,调用时只能使用位置实参传参
1 | print("------------------- 限定位置形参 -----------------------------") |
限定关键字形参
限定关键字形参,又称为命名关键字形参。具体地,在形参列表中使用 *星号 进行分隔,其中 *星号 后面的形参都是限定关键字形参,调用时只能使用关键字实参传参
1 | print("------------------- 限定关键字形参 -----------------------------") |
组合拳
定义函数时,可以组合使用上述多种形参。形参组合顺序为:限定位置形参、普通形参、*形式的可变形参、限定关键字形参、**形式的可变形参。特别地,如果形参列表中存在 *形式的可变形参 时,则 限定关键字形参 前面无需添加 *星号分隔符;否则需要添加。故,下述函数定义示例中的 my_city、my_sex 均为 限定关键字形参
1 | def mix_func1(a,b,c, /, first_name, last_name, *, my_city, my_sex, **kw): |
调用函数时,位置实参、关键字实参 与 各种形参的传递规则为:
- 对于位置实参而言,其会按顺序依次传递给 限定位置形参、普通形参,剩余的位置实参全部传递给可变形参*
- 对于关键字实参而言,其会根据参数名匹配传递给 限定关键字形参、剩余的普通形参,剩余的关键字实参全部传递给可变形参**
1 | print("------------------- 组合拳 -----------------------------") |
具有默认值的形参
规则
Python中形参支持设置默认值。可以从 限定位置形参 或 普通形参中的任意一个形参 开始设置默认值,其后面所有的 限定位置形参、普通形参 都必须设置默认值
1 | # 可以从 限定位置形参 或 普通形参中的任意一个形参 开始设置默认值,其后面所有的 限定位置形参、普通形参 都必须设置默认值 |
限定关键字形参则可以任意设置默认值,不需要保证其后面所有的限定关键字形参都有默认值
1 | # 限定关键字形参则可以任意设置默认值,不需要保证其后面所有的限定关键字形参都有默认值 |
陷阱:使用可变对象作为形参默认值
当使用可变对象作为形参默认值,可能会发生意外情况
1 | print("------------------- 将可变对象作为函数参数的默认值 -----------------------------") |
当显式传入一个list,不使用函数的默认值时。测试结果符合预期;但使用函数的默认值时,我们会发现names3、names4竟然指向的是同一个列表对象
这是因为Python中,默认值是在函数定义时计算的,其只会被计算一次(通常发生在模块加载时)。具体地,默认值会存储在函数对象的defaults属性当中,故其会被重复使用的。所以,如果函数参数的默认值是一个可变对象时,那么每次使用该默认值时,其实是同一个对象。同理,当类的方法形参的默认值使用可变对象,也会存在上述问题
1 | def foo(name, names=[]): |
针对上述问题,可以解决的办法是使用不可变对象作为函数形参的默认值
1 | print("------------------- 将不可变对象作为函数参数的默认值 -----------------------------") |
函数注解
函数注解会作为元数据存储在函数的annotations属性里,仅此而已。目前Python解释器不会利用它进行任何检查、验证、限制。后续可供IDE、框架、装饰器等工具使用。具体地:
- 函数声明中可在形参后添加 :符号 和 注解表达式
- 对于有默认值的形参, 可在 形参与=号 之间添加 :符号 和 注解表达式
- 对于返回值, 可在 函数声明末尾与:号 之间添加 ->符号 和 注解表达式
- 其中,注解表达式可以是任何类型
1 | import inspect |
参考文献
- Python编程·第3版:从入门到实践 Eric Matthes著
- Python基础教程·第3版 Magnus Lie Hetland著
- 流畅的Python·第1版 Luciano Ramalho著