函数
数学函数
Python函数
若干语句块、函数名称、参数列表构成,组织代码的最小单元
完成一定的功能
作用
结构化编程对代码的最基本的封装,一般按照功能组织一段代码
复用,减少冗余代码
简洁美观,可读易懂
函数分类
内建函数,max()、reversed()
库函数,math.ceil()
函数定义、调用
def语句定义函数
def 函数名(参数列表):
函数体
[return 返回值]
函数名为标识符,要求按照标识符的命名规则
语句块必须缩进,4个空格
若没有return语句,默认返回None值
定义中的参数列表成为形式参数,形参
调用
函数定义,只是声明了一个函数,需要调用才能被执行
调用方式:函数名(参数)
调用时给定的参数是实际参数,实参,是实实在在传入的值
函数是可调用对象,callable()
函数参数
函数调用时传入的参数要和定义的个数相匹配(可变参数例外)
位置参数
按照参数定义顺序传入实参
关键字参数
使用形参的名字来传入实参,若使用了形参的名字,则传参顺序和定义顺序可不同
传参
要求位置参数必须在关键字参数之前传入,位置参数是按位置对应的
参数默认值
定义函数时,在形参后面给定一个值
作用
在未传入足够的实参时,对没有给定的参数赋值为默认值
参数非常多时,不需要用户每次输入所有参数,简化函数调用
可变参数
位置参数的可变参数
一个形参可以匹配任意个参数
在形参前使用*表示该形参是可变参数,可以接收多个实参
收集多个实参为一个tuple
关键字参数的可变参数
形参前使用**表示可以接收多个关键字参数
收集的实参名称和值组成一个字典
混合使用参数时,可变参数要放到参数列表的最后,普通参数需要放在参数列表的前面,位置可变参数需要放在关键字参数之前
相同参数不能重复赋值
keyword-only参数
在一个*参数后面或者一个位置可变参数后面,若出现的是普通参数,实际上不在是普通参数了,而是keyword-only参数
*args可以收集所有的位置参数,如果其后的普通参数不使用关键字参数的方式传递参数就得不到参数
不能使用**kwargs后面跟普通参数的方式传递参数,原因:即使后面的普通参数使用关键字的方式传递参数也会被**kwargs收集,导致普通参数无法获取到参数
def fn(*,x,y):在*号之后,普通形参都变成了必须给出的keyword-only参数
参数规则
参数列表的一般顺序:普通参数、缺省参数、可变位置参数、keyword-only参数(可带缺省值)、可变关键字参数
def fn(x, y, z=3, *arg, m=4, n, **kwargs):
参数解构
在给函数提供实参的时候,可以在集合类型前使用*或**将集合类型 的结构解开,提取出所有元素作为函数的实参
非字典类型使用*解构成位置参数
字典类型使用**解构成关键字参数
提取出来的元素个数和参数要求的要一致,且要类型匹配
函数的返回值、作用域
返回值
使用return语句返回”返回值”
所有函数都有返回值,若没有return语句则隐式调用return None
Return语句并不一定是函数的语句块的最后一条语句
一个函数可以存在多个return语句,但是只有一条能够被执行,如果return语句都没有执行,隐式调用return None
若有必要,可显式调用return None,简写return
如果函数执行了return语句,函数就会返回,当前被执行的return语句后面的其他语句将不会被执行
作用:结束函数调用,返回值
返回值只能有一个,不能同时返回多个值
return [1,2,3]指明返回一个列表,是一个列表对象
return 1,2,3 看似返回多个值,实则隐式被封装成了一个元组
使用x,y,z = show()的方式可以方便的解构提取
函数嵌套
在一个函数中定义了另一个函数
函数有可见范围,即作用域
内部函数不能被外部直接调用,会抛NameError异常
作用域
一个标识符的可见范围,就是标识符的作用域,即变量的作用域
全局作用域
在整个程序运行环境都可见
局部作用域
在函数、类等内部可见
使用范围不能超出其所在的局部作用域
外层变量在内层作用域可见
内层作用域中如果定义和外层作用域中同名的变量,相当于在当前作用域中重新定义了一个新的变量,但该变量并没有覆盖外层作用域中的同名变量
错误原因
x += 1等价于x = x + 1
相当于在foo内部定义了一个局部变量x,foo内部均使用该局部变量x
但是x还未完成赋值,就被用来做+1操作
全局变量global
使用global关键字的变量,将内部变量x声明为使用外部的全局作用域中定义的x
若内部作用域中未定义x,则在全局作用域中必须有x的定义
若全局作用域中未定义x,则在内部作用域中可为x赋值,即在内部作用域为一个外部作用域的变量赋值,x的作用域为全局
使用规则
外部作用域变量在内部作用域可见,但不要在内部作用域中直接使用,因为函数的目的就是为了封装,实现和外界的隔离
若函数需要使用外部全局变量,可使用函数的形参传参
尽量不用global
闭包
自由变量:未在本地作用域中定义的变量,定义在内部函数外的外层函数中的作用域中的变量
闭包:在嵌套函数中,内层函数引用到了外层函数的自由变量
nonlocal关键字:将变量标记为不在本地作用域中定义,而在上级的某一级局部作用域中定义,而不能在全局作用域中定义
默认值的作用域
函数地址未改变,即函数对象没有改变,它的属性__defaults__中使用元组保存默认值
w的默认值为引用类型,引用类型的元素变动,元组并未变化
传递参数时未使用到默认值,默认值不会改变
属性__defaults__中使用元组保存所有位置的默认值,w的默认值为简单类型,不可变,即使return w也不会改变函数的默认值
属性__kwdefaults__中使用字典保存所有keyword-only参数的默认值
默认值修改
方法1
使用影子拷贝创建一个新的对象,永远不能改变传入的参数
方法2
通过值的判断灵活的选择创建或修改传入的对象
方式灵活,应用广泛
使用None这个不可变的值作为默认参数,是一种惯用方法
变量名解析原则
顺序:LEGB
Local:本地作用域、局部作用域的local命名空间,函数调用时创建,调用结束消亡
Enclosing:嵌套函数的外部函数的命名空间
Global:全局作用域,即一个模块的命名空间,模块被import时创建,解释器退出时消亡
Build-in:内置函数的命名空间,从解释器启动时创建到解释器退出时消亡
函数的销毁
全局函数销毁
重新定义同名函数
del语句删除函数对象
程序结束时
局部函数销毁
重新在上级作用域定义同名函数
del语句删除函数对象
上级作用域销毁时
递归函数
递归
函数直接或间接调用自身
需要有边界条件、递归前进段、递归返回段
一定要有边界条件
当边界条件不满足时,递归前进
当递归条件满足时,递归返回
递归要求
一定要有退出条件,递归调用一定要执行到这个退出条件。没有退出条件的递归调用,就是无限调用
递归调用的深度不易过深
python对递归调用的深度做了限制,已保护解释器
超过递归深度限制,抛出超出最大深度异常
sys.getrecursionlimit()查看深度限制
递归的性能
循环稍复杂,但只要不是死循环,就可以多次迭代直至算出结果
递归有深度限制,如果递归复杂,函数反复压栈,栈内存很快就溢出了
间接递归
通过别的函数调用了函数自身
构成循环递归调用时非常危险的,要用代码的规范来避免这种递归调用的发生
总结
递归是一种很自然的表达,符合逻辑思维
递归相对运行效率低,每一次调用函数都要开辟栈帧
递归有深度限制,若递归藏瓷太深,函数反复压栈,栈内存很快就溢出了
如果是有限次的递归,可以使用递归调用,或使用循环代替
绝大多数递归都可以通过循环实现
虽然递归代码简洁,但是能不用则不用为好
匿名函数
使用lambda表达式构建函数
格式
lambda 参数列表 :表达式
特点
使用lambda关键字来定义匿名函数
参数列表不需要小括号
使用冒号分割参数列表和表达式
不需要使用return,表达式的值即为匿名函数的返回值
Lambda表达式只能写在一行上,被称为单行函数
用途
在高阶函数传参时,使用lambda表达式,能简化代码
原创文章,作者:ZBD20,如若转载,请注明出处:http://www.178linux.com/96272