函数进阶
内容摘要:本文主要是介绍闭包,装饰器,装饰器的应用与进阶,迭代器与生成器,列表推导式以及生成器表达式内容。
1.闭包
''' 整体说明: 01 动态参数:万能参数,*args **kwargs (1)在函数定义时:* ** 代表聚合 (2)在函数调用时:* ** 代表打散 02 函数名的运用 (1)函数名可以作为变量。 (2)函数名可以作为函数的参数。 (3)函数名可以作为容器类类型的元素。(容器类型的数据包括字典、集合、列表(有序)、元素) (4)函数名可以作为函数的返回值。 03 闭包 (1)闭包是嵌套在函数中的。 (2)闭包是内层函数对外层函数的变量(非全局变量)的引用(改变)。 (3)闭包需要将其作为一个对象返回,而且必须逐层返回直至最外层的函数的返回值。 (4)闭包的作用:python遇到闭包,产生一个空间,这个空间不会随着函数的结束而消失。(普通的非闭包的函数,变量的空间会随着函数的结束而消失) ''' # 01 动态参数:万能参数,*args **kwargs,在函数的定义时:* ** 代表聚合。 def func(*args, **kwargs): print(args) # (1, 2, 3, 'alex') print(kwargs) # {'name': '太白', 'age': 25} func(1, 2, 3, 'alex', name='太白', age=25) # 02 动态参数:万能参数,*args **kwargs,在函数的调用时:* ** 代表打散 # *的使用 def func(*args, **kwargs): print(args) # (1, 2, 3, 'alex', '太白') print(kwargs) # {} func(*[1, 2, 3, ], *['alex', '太白']) # **的使用 def func(*args, **kwargs): print(args) # () print(kwargs) # {'name': 'alex', 'age': 73} func(**{'name': 'alex', 'age': 73}) # func(**{1: 'alex', 'age': 73}) # 会报错,因为关键字要求必须是字符串 # 03 函数名的应用 # (1) 函数名可以作为变量 def func1(): print(1111) ret = func1 ret() 输出结果:1111 # (2) 函数名可以作为函数的参数 def func2(): print(666) def func3(x): x() func3(func2) # 输出结果:6666 # (3) 函数名可以作为容器类的元素 def func1(): print(666) def func2(): print(777) def func3(): print(888) l1 = [func1, func2, func3] for i in l1: i() # 输出结果: 666 777 888 # (4) 函数名可以作为函数的返回值 def func1(): print(666) def func2(x): print(888) return x ret = func2(func1) # 遇到赋值预算先计算赋值运算作出的内容,本赋值是先执行func2函数, 并且将func1作为返回值返回给我ret ret() # 由于func1的函数名赋值给ret,所以ret具有func1作为函数的所有特性,即函数名()-是函数的调用者,所以执行func1函数 # 输出结果:888 666 # 04 闭包 # (1) 闭包的举例: # 以下是闭包 def wrapper(): name = '太白' def inner(): print(name) return inner # 以下是闭包:外层函数的变量定义相当于name = n1,是个局部变量所以满足条件,因此是闭包。 def wrapper(name): def inner(): print(name) return inner n1 = 'wusir' wrapper(n1) # 以下不是闭包:因为内层函数调用的变量是全局变量,而不是外层函数的局部变量,未满足条件所以不是。 name = 'alex' def wrapper(): def inner(): print(name) return inner # (2)闭包的作用 # 非闭包函数 随着函数的结束临时空间关闭,即变量的赋值会清空 def func1(s1): n = 1 n += s1 print(n) func1(5) func1(5) func1(5) func1(5) # 输出结果: 6 6 6 6 # 闭包的机制:python遇到闭包,产生一个空间,这个空间不会随着函数的结束而消失。 def wrapper(s1): n = 1 def inner(): nonlocal n # 在局部作用域中,对父级作用域(或者更外层作用域非全局作用域)的变量进行引用和修改,并且引用的哪层,从那层及以下此变量全部发生改变。 n += s1 print(n) return inner ret = wrapper(5) ret() ret() ret() ret() # 输出结果:6 11 16 21
2.装饰器
''' 整体说明: 01 装饰器说明 (1) 装饰器的本质是闭包。 (2) 装饰器是在不改变原函数的内部函数体以及调用方式(也包括调用次数)的情况下,给函数增加了额外的功能:比如登录、注册、打印日志、函数效率等等。 02 标准装饰器:手写的标准装饰器总共分5步。 ''' # 01 标准装饰器 def wrapper(f): def inner(*args, **kwargs): print('执行之前', 555) # 在执行前可以编写新增的功能代码,比如登录验证、注册、打印日志等。 ret = f(*args, **kwargs) # 该处是装饰器核心,用于调用函数使用 print('执行之后', 666) # 在执行前可以编写新增的功能代码,比如登录验证、注册、打印日志等。 return ret return inner # 02 装饰器的使用方式 # 0201 初级的引用方式(未优化版本) # 引用时间模块 import time def func1(): time.sleep(0.6) print('来了,老弟') def func2(): time.sleep(0.6) print('来le') def func3(): time.sleep(0.6) print('来') # 定义一个测量函数执行执行效率的装饰器 def func1(): time.sleep(0.6) print('来了,老弟') def timmer(f): # f = func1 函数名的内存地址 def inner(): start_time = time.time() f() end_time = time.time() print('此函数的执行时间是%s' % (end_time-start_time)) return inner func1 = timmer(func1) # 首先执行赋值运算,将返回值inner函数名的内存地址赋值给新变量func1,新变量func1具有inner作为函数名的所有特性 func1() # inner() ''' 以上函数具体执行过程说明: 1.先计算赋值运算左侧的:timmer(func1) 2.执行timmer函数,并且将func1传递给f 3.inner函数不执行,将inner函数名返回给timmer(func1) 4.在内存中重新创建一个变量叫做func1,此时func1 = inner 5.执行inner()执行inner函数,执行f() ''' # 0202 使用python提供的语法糖'@'(优化方法) # 引用时间模块 import time # 定义装饰器 def timmer(f): # f = func1 函数的内存地址 def inner(): start_time = time.time() f() end_time = time.time() print('此函数的执行时间是%s' % (end_time - start_time)) return inner ''' @timmer的作用 1.@timmer相当于func1 = timmer(func1) 2.@timmer需要写在函数定义前,这可以保证函数在被调用时即执行装饰器的功能,无论多少次调用只要写一次即可。 3.@timmer可以把定义的函数看做为一行来进行使用,实际上当执行到语法糖的时候,寻找最近的函数,完成1中的等式。 ''' @timmer # 切记将语法糖写在函数定义之前 def func1(): time.sleep(0.6) print('来了,老弟') func1() # inner() @timmer def func2(): time.sleep(0.6) print('来le') func2() # 03 被装饰的函数带有参数 import time def timmer(f): # f = func1 函数的内存地址 def inner(*args, **kwargs): start_time = time.time() f(*args, **kwargs) # 真正执行函数func1的一句 end_time = time.time() print('此函数的执行时间是%s' % (end_time - start_time)) return inner @timmer # func1 = timmer(func1) def func1(a): time.sleep(0.6) print('来了,%s' % (a)) func1('alex') # 输出结果: 来了,alex 此函数的执行时间是0.6000347137451172 # 04 被装饰函数有返回值 import time def timmer(f): # f = func1 函数的内存地址 def inner(*args, **kwargs): start_time = time.time() ret = f(*args, **kwargs) # 真正调用func1函数 end_time = time.time() print('此函数的执行时间是%s' % (end_time - start_time)) return ret return inner @timmer # func1 = timmer(func1) def func1(a): time.sleep(0.6) print('来了,%s' % (a)) return 666 print(func1('alex')) # print(inner('alex')) # 输出结果:来了,alex 此函数的执行时间是0.600034236907959 666
3.装饰器的应用
# 登录认证的应用(示例,具体完整内容详见作业4博客园的登录验证) login_status = { 'username': None, 'status': False, } def login(f): def inner(*args, **kwargs): if login_status['status']: ret = f(*args, **kwargs) return ret else: print('请先登录') username = input('请输入用户名').strip() password = input('请输入密码').strip() if username == '二狗' and password == '123': login_status['username'] = username login_status['status'] = True ret = f(*args, **kwargs) return ret return inner @login def article(): print('欢迎访问文章页面') @login def dariy(): print('欢迎访问日记页面') @login def comment(): print('欢迎访问评论页面') dic = { 1: login, 2: article, 3: dariy, 4: comment, } while 1: print(''' 欢迎来到博客园首页: 1,登录 2,文章页面 3,日记页面 4,评论页面 ''') num = input('请输入数字:').strip() dic[int(num)]()
4.装饰器的进阶
''' 整体说明: 01 带参数的装饰器 (1)应用场景1:对500个函数加上装饰器,之后去掉,之后再加上。 (2)带参数的装饰器相当于增加一个开关,可以让装饰器生效与失效。 02 多个装饰器装饰一个函数 ''' # 01 带参数的装饰器的的标准形式: def login(a, b): print(a, b) def wrapper(f): def inner(*args, **kwargs): ret = f(*args, **kwargs) return ret return inner return wrapper @login(1,2) def func1(): print(111) func1() # 02 带参装饰器的举例 login_status = { 'username': None, 'status': False, } def login(a): def wrapper(f): def inner(*args, **kwargs): if a: # 判断装饰器是否生效,如果flag = True则执行装饰器,否则则不执行装饰器 if login_status['status']: ret = f(*args, **kwargs) return ret else: print('请先登录') username = input('请输入用户名').strip() password = input('请输入密码').strip() if username == '二狗' and password == '123': login_status['username'] = username login_status['status'] = True ret = f(*args, **kwargs) return ret else: ret = f(*args, **kwargs) return ret return inner return wrapper flag = False # 设置开关,定义装饰器生效和失效 @login(flag) def func1(): print(111) @login(flag) def func2(): print(12) @login(flag) def func3(): print(131) func1() login_status = { 'username': None, 'status': False, } def login(a): def wrapper(f): def inner(*args, **kwargs): if login_status['status']: ret = f(*args, **kwargs) return ret else: print('请先登录') '''根据a 京东,天猫 去验证不同密码''' username = input('请输入用户名').strip() password = input('请输入密码').strip() if username == '二狗' and password == '123': login_status['username'] = username login_status['status'] = True ret = f(*args, **kwargs) return ret return inner return wrapper @login('京东') def jd(): print('欢迎访问文章页面') @login('京东') def jdmarkte(): print('欢迎访问日记页面') @login('天猫') def TM(): print('欢迎访问评论页面') @login('天猫') def TMmarke(): print('欢迎访问评论页面') # 02 多个装饰器装饰一个函数 def wrapper1(func): # func = 函数名f def inner1(): print('wrapper1 ,before func') # 2 func() # 函数f print('wrapper1 ,after func') # 4 return inner1 def wrapper2(func): # func = inner1 def inner2(): print('wrapper2 ,before func') # 1 func() # inner1() print('wrapper2 ,after func') # 5 return inner2 @wrapper2 # f = wrapper2(f) 里面的f 是inner1 外面的f 是inner2 @wrapper1 # f = wrapper1(f) 里面的f是函数名f 外面的f是 inner1 def f(): print('in f') # 3 f() # inner2() # 输出结果: ''' wrapper2 ,before func wrapper1 ,before func in f wrapper1 ,after func wrapper2 ,after func ''' ''' 执行顺序说明:(可以简答理解为先自上而下之后再自下而上) 1.首先,遵循自上而下的原则,依次开辟wrapper1和wrapper2的空间。 2.执行到装饰器后,依据就近原则,先执行装饰器wrapper1,即f = wrapper1(f),之后执行装饰器wrapper2,即f = wrapper1(f) 此时,f = wrapper1(f) 里面的f是函数名f 外面的f是 inner1,f = wrapper2(f) 里面的f 是inner1 外面的f 是inner2。 3.执行inner2函数(inner1是函数inner2的变量),首先先打印“wrapper2 ,before func”,之后将变量inner1赋值给f。 4.执行inner1函数(f为函数inner1的变量),首先打印‘wrapper1 ,before func’,之后执行f(),即打印‘in f’,执行完成后打印 'wrapper1 ,after func',函数inner1执行完成,即wrapper2中的func()函数执行完成。 5.最后执行wrapper2种的打印语句,将‘wrapper2 ,after func’打印输出。 '''
5.迭代器与生成器
''' 整体说明: 01 可迭代对象:内部含有‘__iter__’方法的数据就是可迭代对象,包括list str tuple set dict range() 文件句柄 02 迭代器:内部含有‘__iter__’方法的并且含有‘__next__’方法的就是迭代器 03 可迭代对象 ----> 迭代器(转化) 04 迭代器的作用: (1)非常非常的节省内存 (2)满足惰性机制 (3)一条路走到黑(可以理解为类似于指针,下次读取时从上去读取结束时的位置开始读) 05 利用while循环模拟for循环机制。 (1)将可迭代对象转化成迭代器 (2)利用next 进行取值 (3) 利用异常处理终止循环。 06 生成器:自己用python代码写的迭代器 本质就是迭代器 (1)函数式写法: a)只要函数中出现yield,那么 他就不是函数了,他是生成器函数。 b)yield和return的区别 i)return 结束函数 给函数返回值 ii)yield 不会结束生成器函数,next 对应一个yield进行取值 (2)生成器表达式 ''' # 01 可迭代对象 # 判断方法 s1 = '二狗的周末生活' print(dir(s1)) # 用dir可以打印所有的方法 print('__iter__' in dir(s1)) # 判断是否包含_iter_方法,如果包含结果是True,如果不包含结果为False # 02 迭代器 # 判断方法 f1 = open('regsiter', encoding='utf-8') print('__iter__' in dir(f1)) # 判断是否包含_iter_方法,如果包含结果是True,如果不包含结果为False print('__next__' in dir(f1)) # 判断是否包含_next_方法,如果包含结果是True,如果不包含结果为False f1.close() # 03 可迭代对象->迭代器 l1 = [1, 2, 3, 'alex'] iter1 = iter(l1) # l1.__iter__(),将可迭代对象转化为迭代器 print(iter1) # 打印出的一个元素的地址<list_iterator object at 0x00000000021DE278> print(iter1.__next__()) print(iter1.__next__()) print(iter1.__next__()) print(iter1.__next__()) # for循环的方式依次输出 for i in iter1: print(i) # 04 利用while循环模拟for循环 的机制。 l1 = [1, 2, 3, 'alex'] iter1 = l1.__iter__() while 1: try: print(iter1.__next__()) except StopIteration: # except是用来异常处理的作用 break # 05 生成器 # 0501 函数式写法 构建生成器 def func1(): # print(111) # print(222) yield 3 yield 4 yield 5 yield 6 yield 7 g = func1() # 生成器对象 print(g) # 打印出生成器的地址 print(g.__next__()) # print(next(g)),一个next 对应一个 yield print(g.__next__()) print(g.__next__()) print(g.__next__()) # 0502 生成器举例 def cloth(): for i in range(1,201): print('老男孩老年体恤%s号' % (i)) cloth() def cloth1(): for i in range(1,201): yield '老男孩老年体恤%s号' % (i) g = cloth1() for i in range(5): print(g.__next__()) for i in range(195): print(g.__next__())
6.列表推导式与生成器表达式
''' 整体说明: 01 列表推导式 (1)凡是用列表推导式能构建的数据,python代码都可以构建,列表推导式不能构建的数据,python代码亦可以可以构建。 (2)两种模式: a)循环模式: [变量(加工后的变量) for 变量 in iterable] b)筛选模式:[变量(加工后的变量) for 变量 in iterable if 条件] 02 生成器表达式: (1)两种模式: a)循环模式: (变量(加工后的变量) for 变量 in iterable) b)筛选模式:(变量(加工后的变量) for 变量 in iterable if 条件) (2)生成器表达式与列表推导式用() 括起来,其他规则与列表推导式一致。 03 列表推导式,生成器表达式的优缺点。 (1)优点:构造简单,一行完成 (2)缺点: a)不能排错 b)不能构建复杂的数据结构 ''' # 01 循环模式: [变量(加工后的变量) for 变量 in iterable] # 举例:用列表推导式:构建一个列表:['python1期', 'python2期'....,'python24期'] print(['python%s期' % i for i in range(1, 25)]) # 02 筛选模式 [变量(加工后的变量) for 变量 in iterable if 条件] # 举例: # 10以内所有数的平方。 print([i * i for i in range(1, 11)]) # 30以内能被3整除的数的平方。 print([i ** 2 for i in range(1, 31) if i % 3 == 0]) # l1 = ['alex', 'taibai', 'wusir', 're', 'ab'],过滤掉长度小于3的字符串列表,并将剩下的转换成大写字母。 print([i.upper() for i in l1 if len(i) > 3])