【python】进阶之函数详解(三)
3 函数
3.1类型提示
- Python 3.6后加入了新功能:类型提示,用来声明一个变量的类型
- 在FastAPI中,类型提示可以用到Swagger文档中
1 |
|
3.2 函数是一等公民
在编程语言中,一等公民可以作为函数参数,可以作为函数返回值,也可以赋值给变量。
python中一切且对象,所以函数也是对象,函数也是一等公民。
也就是说函数可以作为函数参数,可以作为函数返回值,也可以赋值给变量。
示例1:函数当变量被赋值引用
1 |
|
示例2:函数做容器元素的参数
1 |
|
示例3:函数做实参
1 |
|
示例4:函数做返回值
1 |
|
3.3闭包函数
前面说了,函数是一等公民,所以函数可以作为入参和返回值传递
- 内嵌函数包含对外层函数作用域中变量的引用(非全局作用域的变量,是外层函数内部的变量),那么该内嵌函数就是闭包函数,称为闭包(Closures)
- 正常函数内部变量在函数调用之后就会被回收,但是闭包函数的出现,破坏了这个规则,让内部变量可以在函数外部使用
1 |
|
【如何判断闭包函数】
- 方法1:是否引用外层函数作用域中的变量
- 方法2:通过函数的closure属性,查看到闭包函数所包裹的外部变量。不是闭包该值为None
1 |
|
【闭包函数的用途】
- 为函数体传参。一次传参,后续使用无须再传参 。闭包函数的这种特性有时又称为惰性计算。
- 装饰器(重要)
- 示例2:为函数体传参
1 |
|
作用:
- 1、实现函数工厂:动态地生成和返回具有不同参数的函数
- 2、保护数据: 将变量封装在闭包中,限制对变量的直接访问,实现数据的封装和隐藏
- 3、装饰器:用于在函数前后执行额外的代码,如性能分析,日志收集等,用于加强函数
3.4 普通装饰器【有另开一篇】
目的:在不修改原有函数代码的情况下,增强函数,
1 |
|
3.5 带参数装饰器
知道装饰器的原理之后,带参数的装饰器实现就很简单,就是在外层函数的外面,再嵌套一层函数,且最外层函数的入参就是带参装饰器的入参,接上面
被装饰的函数需要参数
1 |
|
使用装饰器时可以给装饰器传参数
-
1 |
|
分析:先调用wrapper(“am”) 返回outer,再看@outer,即普通的装饰器,相当于 foo = outer(foo)
3.5.1 装饰器的优缺点和应用场景
优点: 不改变函数代码的前提下,对函数进行增强
缺点:无法一眼看清楚代码,有些人看不懂,嵌套比较多
应用场景:
- 1、日志记录,无需写login方法就可打印日志
- 2、权限控制,判断入参有没有某个属性,如果没有就无权限
- 3、性能优化:
- 4、数据验证:在输入输出的数据验证和转换方面,装饰器可以实现输入验证、类型转换
- 5、代码计时:记录这段函数代码运行的花费的时间
3.6 可变长参数的高级用法
首先python是常规函数是严格按照定义的形参传的,如果实参数量大于形参就会报错
- 【可边长位置参数】*args 如过在最后一个形参前面加星号,那么在调用函数的时候,溢出的位置实参都会被接收下来,且会以元组的形式保存下来赋值给该形参。这里用 *args
1 |
|
- 【可变长关键字参数】如果在最后一个形参前面加两个星号 那么在调用函数的时候,溢出的关键字仍然都会被接收,且会以字典的形式保存下来赋值给该形参,一般用 **kwargs传递
1 |
|
- 【补充一下 *args的用法】 将可迭代对象的元素打散传入函数
1 |
|
- 【补充一下 **的用法】 可以将字典每个key-value拆开为关键字传参
1 |
|
3.7 yield【重点】
普通函数在被调用的时候,会从上往下依次执行函数代码。遇到return语句时立即退出函数且返回返回值,再次调用这个函数的试试,函数会再次从头到尾执行一遍
函数生成器被调用时,也是会从上往下执行代码,遇到yield语句后,再yield位置处挂起并且返回yield后的数据出来。此函数再次被调用的时候,会从挂起位置处(也就是yield处)再往下执行函数
【函数生成器】函数体包含yield关键字,就是函数生成器
生成器对象,指对象内置有
_iter__
__和__nex__
t方法,所以生成器本身就是一个迭代器,可以被for循环遍历
1 |
|
- 生成器函数的使用yiled语句实现的,python的yield表达式也很强大。yield不仅可以从函数体内往外取值,还可以从外部往函数体内传值,传值使用
generator.send(value)
1 |
|
3.8 匿名函数
- 匿名函数lambda 就是不命名的函数,一般用于一次性使用的场景,不属于任何类
- 定义有名字的函数用def 定义没名字的函数用lambda
- fun = lambda 入参 : 返回结果 lambda arguments: expression
1 |
|
3.9 内置函数之filter (其实是一个类)
- filter是一个类,不是一个函数,查看源码发现
- fulter的入参是两个:function_or_None和iterable可迭代对象
- 如果function是None的话,就报错
1 |
|
3-9.2 内置函数之map
map()
是 Python 的内置函数之一,它用于将一个函数应用于一个或多个可迭代对象(如列表、元组等)的每个元素,并返回一个包含结果的新的迭代器。
map()
函数的工作原理是遍历 iterable
中的每个元素,并将每个元素传递给 function
进行处理。function
将对每个元素执行指定的操作,并返回结果。最后,map()
返回一个新的迭代器,其中包含了经过 function
处理后的每个元素。
1 |
|
3.10 内置函数值enumerate
- enumerate看源码,入参是一个可迭代对象,和一个默认为0的start起始值,
- 返回:以元组形式返回enumerate对象,元组内是(下标索引,值)的形式,
- 可以通过给start传参定义起始位置的索引值
1 |
|
3.11 模块的本质
- 一个python文件就是一个模块,
- 模块是一堆功能的集合,算是一种代码“封装”的方式
- 内置模块,第三方模块,自定义模块。其实就是各种文件
1 |
|
- 模块先导入后使用。导入模块的方式:
import、from、或两者配合使用
-
1 |
|
- 导入模块的本质
1 |
|
- 模块的本质可以理解为是一个命名空间(Namespace)
- 命名空间相互隔离的,这个命名了的空间下面的东西,就只属于这个命名空间
- import 导入,就是导入了一个命名空间,就是导入了一个py文件
3.12 py文件的两种用途
- 1、当脚本被执行
- 2、当模块被导入使用
- 区别:
- 脚本文件执行的时候,产品的命名空间会在程序解释后失效回收
- 模块导入运行的试试,产生的命名空间会在引用计数为零的时候回收释放。
- 每一个py文件都有一个属性:
__name___
这个不是他的命名空间 - 当py文件被当做模块导入的时候,
__name__
==”模块名” - 所以
if __name__=“__main__”
时,就意味着该模块是主程序入口
3.13 包的本质
- 包: 就是一堆模块的集合,那单个模块是单个py文件,那包就是好多文件的集合,就是文件夹,且包含一个
.__init__
文件 - 导入包,起始就是导入包内init文件
1 |
|
- 导入包就是导入包内的
__init__.py
文件
1 |
|
- 不管是import包名.模块名 还是from 包名 import模块名,带点的时候,点的前面都是包名