Python函数
函数是Python里组织代码的最小单元,Python函数包含以下几个部分:
- 定义函数
- 调用函数
- 参数
- 函数的返回值
- 函数的嵌套
- 作用域
- 函数执行流程
- 递归函数
- 匿名函数
- 生成器
- 高阶函数
定义函数
函数是有输入(参数)和输出(返回值)的代码单元, 把输入转化为输出
调用函数
定义函数的时候,并不会执行函数体, 当调用函数的时候,才会执行其中的语句块
参数
传参方式
参数默认值
参数可以有默认值,当一个参数有默认值时, 调用时如果不传递此参数,会使用默认值
参数默认值和关键字参数一起使用,会让代码非常简洁
可变参数
可变参数两种形式:
- 位置可变参数 : 参数名前加一个星号, 构成元组, 传参只能以位置参数的形式
- 关键字可变参数: 参数名前加两个信号, 构成字典, 传参只能以关键字参数的形式
位置可变参数
关键字可变参数
位置可变参数和关键字可变参数混合使用
可变参数和普通参数混合使用
普通参数可以和可变参数一起使用,但是传参的时候必须匹配,演示如下
位置可变参数可以在普通参数之前, 但是在位置可变参数之后的普通参数变成了keyword-only参数:
关键字可变参数不允许在普通参数之前,演示如下:
关于默认参数和可变参数的总结:
通常来说:
- 默认参数靠后
- 可变参数靠后
- 默认参数和可变参数一般不同时出现
- 当默认参数和可变参数一起出现的时候, 默认参数相当于普通参数
参数解构
参数解构有两种形式
- 一个星号 解构的对象:可迭代对象 ,解构的结果:位置参数
- 两个星号 解构的对象:字典 ,解构的结果:关键字参数
一个星号的情况
二个星号
参数解构发生在函数调用时, 可变参数发生函数定义时,所以两者并不冲突
keyword-only 参数
使用方法参见:Python: 函数参数列表中单个星号的意思,Keyword-Only Arguments
星号可以以一个参数的形式出现在函数声明中的参数列表中,但星号之后的所有参数都必须有关键字(keyword),这样在函数调用时,星号*之后的所有参数都必须以keyword=value
的形式调用,而不能以位置顺序调用。
使用示例如下:也可参考上面链接中的示例
函数的返回值
- return 语句除了返回值之外,还会结束函数, return之后的语句将不会被执行
- 一个函数可以有多个return语句, 执行到哪个return由哪个return返回结果并结束函数
- 函数中 return可以提前结束循环
- 当函数没有return语句的时候,返回None
- 当函数需要返回多个值时, 可以用封装把返回值封装成一个元组
- 可以通过解构获取到多返回值
- return None 可以简写为 return, 通常用于结束函数
函数的嵌套
函数可以嵌套使用
作用域
变量的作用域为定义此变量的作用域
表明变量的作用域就在fn函数之中
上级作用域对下级作用域只读可见
不同作用域变量不可见, 但是下级作用域可以对上级作用域的变量只读可见
不要使用全局变量global
除非你清楚的知道global会带来什么,并且明确的知道,非global不行, 否则不要使用global
闭包函数
闭包定义(Wikipedia):在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。闭包可以用来在一个函数与一组“私有”变量之间创建关联关系。在给定函数被多次调用的过程中,这些私有变量能够保持其持久性
通俗理解:当某个函数被当成对象返回时,夹带了外部变量,就形成了一个闭包。
如果我们想实现一个无限增长的计数器,可以写一个counter函数,函数内部进行自增就行。假定我们按照以下写法:就会报错
在 python 的函数内,可以直接引用外部变量,但不能改写外部变量,因此如果在闭包中直接改写父函数的变量,就会发生错误。比如上述程序直接改写父函数中的变量c
python的闭包中如果想改写父函数的变量可以用可变容器实现,这也是python2实现的唯一方式
nonlocal关键字
在python3中改写父变量还有一种方就是使用**nonlocal
关键字**
nonlocal 关键字用于标记一个变量由他的上级作用域定义, 通过nonlocal标记的变量, 可读可写
如果上级没有定义nonlocal的变量,使用nonlocal时会抛出语法错误
函数的__defaults__
属性
可变参数和不可变参数的__defaults__
属性不一样
参数可变时
当使用可变类型作为默认值参数默认值时,需要特别注意,会改变函数的__default__
属性
参数不可变时
使用不可变类型作为默认值,函数体内不改变默认值
可变参数时None的使用
通常如果使用一个可变类型作为默认参数时, 会使用None来代替
Python作用域、闭包、装饰器资料
函数执行流程
函数的执行过程就是压栈和出栈的过程。具体如下
当调用函数的时候, 解释器会把当前现场压栈,然后开始执行被调函数, 被调函数执行完成,解释器弹出当前栈顶,恢复现场
递归函数
递归函数的定义就是函数调用函数自身。
- 递归函数必须要有退出条件
- 为了保护解释器, Python对最大递归深度有限制
- 绝大多数递归都可以转化为循环使用
- 尽量避免使用递归
- sys模块中的getrecursionlimit和setrecursionlimit可以获取和设置最大递归深度
匿名函数
匿名函数有以下特点
- lambda来定义
- 参数列表不需要用小括号
- 冒号不是用来开启新语句块
- 没有return,最后一个表达式的值即返回值
- 匿名函数(lambda表达式)只能写在一行上,所以也叫单行函数
匿名函数的好处是
- 函数没有名字,不必担心函数名冲突
- 匿名函数也是一个函数对象,可以把匿名函数返回给一个变量,再利用变量调用函数
普通函数所支持的参数的变化,匿名函数都支持
匿名函数的常见用法:通常用于高阶函数的参数, 当此函数非常短小的时候,就适合使用匿名函数
比如匿名函数可以作为sorted
函数的自定义键函数(custom key function)
高阶函数
高阶函数的定义
高阶函数英文叫Higher-order function。
在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数:
- 接受一个或多个函数作为输入:通常用于大多数逻辑固定,少部分逻辑不固定的场景
- 输出一个函数:函数作为返回值: 通常是用于闭包的场景, 需要封装一些变量
常见的高阶函数有map,reduce,filter
高阶函数:插入排序
插入排序时,排序顺序分为升序和降序,我们可以使用一个函数作为插入排序函数的参数来控制是升序还是降序。
首先看一下按照升序插入排序,然后再改进成升序降序可控的插入排序
如果想让这个函数降序排序,则只需要修改代码中的注释处,改成x > y
即可
如果传入一个函数来控制if后面的bool值,则就实现了通过参数控制升降了
这个函数就默认为升序排序了,但是可以传入一个比较函数变成降序,如下
map
map()
函数原型:**map(func, *iterables) --> map object
**
map()
函数接收两个参数,一个是函数func
,一个是可迭代对象Iterable
,map
将传入的函数依次作用到可迭代对象的每个元素,并把结果放入**map对象
**这个迭代器中。所以map
函数是高阶函数。
map
类中存在__iter__
和__next__
函数
map使用示例
把list中的所有数字的平方
reduce
map
函数是map
类的函数,但是reduce函数属于functools
包的reduce
模块中
然后可以使用help
方法查看reduce
函数的使用
输出结果如下
reduce使用示例
输出结果为55
str2int(‘1234321’) => 1234321
filter
help(filter)
之后可以发现filter
是一个类,其中有一个filter
函数,原型如下
和map()
类似,filter()
也接收一个函数和一个序列。和map()
不同的是,filter()
把传入的函数依次作用于每个元素,然后根据返回值是True
还是False
决定保留还是丢弃该元素。返回值也是一个迭代器。
filter使用示例
使用filter筛选出list中的回文数
所以filter()
函数用于过滤序列,重点在于选择一个正确的筛选函数。
生成器
带yield语句的函数称之为生成器函数, 生成器函数的返回值是生成器
- 生成器函数执行的时候,不会执行函数体
- 当next生成器的时候, 当前代码执行到之后的第一个yield,会弹出值,并且暂停函数
- 当再次next生成器的时候,从上次暂停处开始往下执行
- 当没有多余的yield的时候,会抛出StopIteration异常,异常的value是函数的返回值
生成器的基本形式
生成器的执行顺序
生成器的应用
计数器第一种形式
计数器第二种形式
斐波拉契数列
生成器的高级用法
生成器的高级用法是协程
生成器参考资料