一、函数
1、意义:函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段
2、格式:def 函数名(参数),中间处理完数据后 return 结束函数并返回数据,return可不写,默认return None。
3、参数:
* 形参:定义 函数时的 参数变量
* 实参:调用 函数时,使用的参数变量
* 传递:在函数调用的过程中,把实参的引用 传递给 形参 ,使用实参的值来执行函数体的过程。
* 引用:在 Python 中,函数的 实参/返回值 都是是靠 引用 来传递来的 即:形参引用实参执行函数计算,所得结果被调用函数的地方引用
* 使用分类:
(1)位置实参: 按照参数位置,依次传递参数
(2)关键字实参: 如果不想严格按照顺序传递参数,也可以按关键字传递。
(3)属性参数:指定参数数据类型,Python中没有声明类型的语句,所以如果没有指定参数类型,当你刚看到这个函数的时候,你根本不知道我这个函数的作用是两个整数相加还是两个字符串拼接,(在pycharm里,Ctrl把鼠标放函数名上会显示参数类型,或者使用function_name.__annotations__查看)
(4)缺省参数:定义函数时,可以给 某个参数 赋值一个默认值,具有默认值的参数就叫做 缺省参数
* 缺省参数的定义位置:没有多值参数的时候在末尾。有多值参数的时候在多值参数前,最好别和多值参数一起用,下讲(很重要、很重要、很重要)
* 调用函数时,如果没有传入 缺省参数 的值,则在函数内部使用 参数默认值,
* 将常见的值设置为参数的缺省值,从而 简化函数的调用,比如:对一个数组排序,一般都是升序,这个时候就可以设缺省值,调用的时候不传参数也会默认升序,当需要降序的时候,传对应的参数让他降序处理
* 调用带有多个缺省参数的函数:在 调用函数时,如果有 多个缺省参数,需要指定参数名,这样解释器才能够知道参数的对应关系!
(4)多值参数:当函数需要处理的参数个数不确定时,可使用 多值参数。
* 位置:参数列表末尾!(很重要、很重要、很重要)
* python 中有两种多值参数:
接收 元组:参数名前加 一个 *
接收 字典:参数名前加 两个 *
* 一般在给多值参数命名时,习惯使用以下两个名字
*args — 存放 元组 参数
**kwargs — 存放 字典 参数,(kw 即 keyword,kwargs 即 键值对参数)
* 先 *args,后 **kwargs,这个顺序是死的,不能改
* 拆包,就是实参传递的规则。
(1)再以位置,从0开始一一传递给除*、**之外的形参,这个时候缺省参数也会传给一个值,因为系统判断不出来你到底是否想给缺省参数传值,为了清晰逻辑,当有多值参数的时候,缺省参数的缺省值无效
(2)剩下的实参再以属性分类,以第一个字典实参为基准,之前的整成数组传递给 一个*形参,之后的整成字典传递给两个*的形参
4、匿名函数:
* 格式:lambda x ,y : x * y (fun = lambda 参数 : 返回值)
* fun:代表这个函数,因为等号=代表赋值
* lambda,定义匿名函数的关键字,相当于函数的def.
* x ,y:形参,用逗号(,)分隔。可以传入多个参数,规则参照上面的参数说明,注意的是多值参数与缺省参数的顺序要求好像没那么严格,可以随意,但是一般用不到,因为一行里你写不了多少东西,参数多了你也用不上
* : x * y:匿名函数的返回值,可以是一个值x,也可以是x*y这种处理数据的表达式,相当于一般函数里return的值,必须要有一个返回值用来返回,不支持复杂的诸如逻辑判断语句,只能写最简单的语句,最多支持到写3元运算
* 使用:
(1)直接用:print((lambda x: x +6)(5)) # 11
(2) 赋值给变量后调用:fun= lambda x: x +6 print( fun(5) ) #11
(3)嵌套使用:内嵌lambda表达式可以获得上层lambda函数的变量
(4)高阶函数(参数为函数的函数):
* filter:过滤 filter ( 匿名函数,数据源)
* max/mix:最大/最小
* sort:排序
* map:循环让每个元素执行函数,将每个函数执行的结果保存到新的map中
* reduce:合并、叠加
5、变量作用域
1、变量的作用域决定了在哪一部分程序你可以访问哪个特定的变量名称
2、分类:
* 全局变量:定义在函数外的拥有全局作用域,全局变量可以在整个程序范围内访问。
* 局部变量:定义在函数内部的变量拥有一个局部作用域,局部变量只能在其被声明的函数内部访问
3、更改作用域:
* 场景:1、在函数内改变函数外的变量值时
* 全局 -->> 局部:global 关键字
* nonlocal :适用于函数嵌套,nonlocal关键字修饰变量后标识该变量是上一级函数中的局部变量,如果上一级函数中不存在该局部变量,nonlocal位置会发生错误(最上层的函数使用nonlocal修饰变量必定会报错)。
二、模块
1、定义:是一个 Python 文件,以 .py 结尾,包含了 Python 对象定义和Python语句。
2、引入:
* import + 模块路径:引入模块,使用到的时候需要 模块名.函数(..)
* from + 模块路径 + import * :引入整个模块的所有内容,使用的时候可以直接 函数名(...)
* from + 模块路径 + import + 要引用的函数:引入这个模块里的指定部分,使用的时候可以直接 函数名(...)
* from…import *语句与import区别在于:
import 导入模块,每次使用模块中的函数都要是定是哪个模块。
from…import * 导入模块,每次使用模块中的函数,直接使用函数就可以了;注因为已经知道该函数是那个模块中的了。
* 被引用的几个模块里有一个同名的函数,当这个函数呗调用时,最后调的那个模块的,就执行哪个,其余的会被覆盖
3、搜索路径:
(1)、当前目录
(2)、如果不在当前目录,Python 则搜索在 shell 变量 PYTHONPATH 下的每个目录。
(3)、如果都找不到,Python会察看默认路径。UNIX下,默认路径一般为/usr/local/lib/python/。
4、if__name__=='__main__': 当 .py 文件被直接运行时,if __name__ == '__main__' 之下的代码块将被运行; 当 .py 文件以模块形式被导入时,if __name__ == '__main__'之下的代码块不被运行 ,作用,有时候模块里的函数,为了方便测试直接就调用了,如果忘了注释的话,当作为模块引用了之后,他会执行你的测试代码,为了容错,所以有了这个规则
三、迭代器
1、迭代器 :所有能使用for循环的基本都可以视为迭代器,严格的说任意对象,只要定义了__next__方法,它就是一个迭代器。因此,python中的容器如列表、元组、字典、集合、字符串都可以被称作迭代器。
2、迭代:迭代就是从迭代器中取元素的过程。类似于for循环中取值,这种遍历过程就被称作迭代。
3、使用:
(1) 先调用容器(以字符串为例)的iter()函数
(2)再使用 next() 内置函数来调用 __next__() 方法
(3)当元素用尽时,__next__() 将引发 StopIteration 异常
四、生成器
在 Python 中,使用了 yield 的函数被称为生成器(generator)
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。
调用一个生成器函数,返回的是一个迭代器对象。
注:
1、yield;可以当成return,它会阻断函数,并返回 yield后的值
2、生成器函数,返回的是一个迭代器对象。这个很重要,以yield为分隔点,可以理解为它把这个函数拆成了两个函数,并且弄成了一个函数组,以010101 的顺序被调用,直到函数内的条件不成立 报StopIteration异常 结束,
3、使用生成器时一定要 try...except StopIteration: 处理异常
五、闭包
1、定义:函数套函数,比如函数1里有一个变量a,还有了一个函数2,并且函数2调用了变量a,当每次调用函数1的时候,其实和其他的函数使用看不出什么区别,重点是,当把函数1赋值给了一个变量b的时候,热闹来了,下面上个图来捋一下:
(1)当func1函数赋值给b的时候,创建了一个实例,我们知道,变量赋值后除非删除变量或者重新赋值,不然这个实例是不会被清理的,那么也就是说a这个变量也会一直存在
(2)22行的第一次打印,对于这个实例来说就干了两件事,1、是改变了变量a的值为1,2、是对外返回了a的值,实例没销毁,变量a也没销毁
(3)23行的第二次打印,重复了22行的事,所以这个时候a=2,24行同理 a=3
(4)25行重点来了,函数1被重新赋值给了c,这个时候对于一个全新的实例来说,内部的变量a=0,所有的疑惑都在于变量b和c是两个不相干的东西,互不影响且无交集,这个想清楚就没问题了,所以打印c会从1开始
总结:每次赋值都生成一个新的实例,内部会初始化
2、返回值注意:
* func1 返回的一般是func2,不然没有使用闭包的必要
* 当func1的参数时函数的时候 func2 返回的必须是这个函数的实例,不然这个函数根本不会执行
六、装饰器
1、定义:装饰器本质上就是一个python函数,它可以让其它函数在不需要做任何代码改动的前提下增加额外的功能,装饰器的返回值也是一个函数对象。可以简单理解成一个三层或三层以上的嵌套函数的组合的应用, 一个函数可以使用多个装饰器
2、作用:为已经存在的函数或者对象添加额外的功能,抽离出大量与函数功能本身无关的雷同的代码并且可以重复使用。
3、场景:插入日志、性能测试、事务处理、缓存、权限校验等场景
4、规范:
* 必须有一个函数的形参
* 在被装饰函数上一行 ,以 @+装饰器名 的形式调用
* 装饰器的内部函数必须返回一个被装饰函数的实例
* 装饰器内部函数参数要不设置成 *args, **kwargs,要不与被装饰函数的形参相同
4、分类:
以函数类型划分:
(1) 单函数装饰器:不管装饰后的函数调用几次,只在首次的时候装饰器起作用
(2)嵌套函数装饰器:根据闭包规则,每次调用被装饰函数都会触发装饰器的内部函数
以参数划分:
装饰器的内部函数的参数个数:当被装饰函数有参数的时候,装饰器内部函数必须有相同的形参,否则报错。解释一下:调用一个使用了装饰器的函数的时候,他的执行顺序是把被装饰的这个函数传递到装饰器里,装饰器里干两类事,一是它自己内部定义的一些方法,二是执行传进来的这个被装饰的函数并返回它的实例,那么,当被装饰函数有参数的时候,怎么能让装饰器内部函数拿到这些参数,从而顺利完成被装饰函数的实例化,答案是:在装饰器内部函数参数部分设置和被装饰函数一样的形参, 这样,既可以顺利实例化被装饰函数,也可以在内部函数有需要的时候拿到外面传进来的数据
(1)无参装饰器:装饰器内部函数无参的时候,被装饰函数也不能有参数,否则报错
(2)有参装饰器:
* 你有啥,我有啥;被装饰函数的参数和装饰器内部函数的参数一样
* 不管你有啥,我都有:装饰器内部函数以数组和字典的方式接收传递被装饰函数的参数,重点是下图35行创建实例时的参数名里的那几个*号,窃以为是和内部函数参数身份的转变有关系,面对被装饰函数传过来的数据,它是形参,创建实例引用它的时候,它是实参
装饰器有无参数:
* 装饰器不带参数:上面所有的例子都是,没啥可说的
* 装饰器带参数:是个三层以上的嵌套函数,最外层函数接收参数,根据参数的不同,做不同的操作
以实现方式划分:
(1)、以函数方式实现,上面的就是这种
(2)、以类的方式实现 ,在类的 __call__ 方法下 写,使用方式同上
5、使用:
* 注解使用:类似于java的注解,在函数上方 @+装饰名
* 类似函数调用,把被装饰函数当做参数,扔进装饰器里
注意:这种情况会出问题,怎么回事等我查查