python函数之闭包及装饰器
作者:互联网
一、闭包
闭包的定义:
闭包是嵌套在函数中的函数。
闭包必须是内层函数对外层函数的变量(非全局变量)的引用。
def make_average(): li = [] def average(price): li.append(price) total = sum(li) return total/len(li) return average avg = make_average() print(avg(100000)) # 100000.0 print(avg(110000)) # 105000.0 print(avg(120000)) # 110000.0
如上图当在函数嵌套时,第一层函数返回的是第二层函数的函数名,并且内层函数引用了第一层函数的变量的时候就形成了闭包。闭包函数的空间不会随着函数 的结束而消失,引用的变量被称为自由变量,也不会随函数的结束而消失。
如何判断闭包
如果此函数拥有自由变量,那么就可以侧面证明其是否是闭包函数了
# 对上个例子使用入下命令 print(avg.__code__.co_freevars) # ('li',)
通过以上命令可查看函数中是否存在自由变量
# 拓展:查看其他变量名命令 print(avg.__code__.co_varnames) # 查看局部变量 # ('price', 'total') print(avg.__closure__ ) # 获取具体的自由变量 # (<cell at 0x000001ACC81487F8: list object at 0x000001ACC83447C8>,) print(avg.__closure__[0].cell_contents) # 获取具体自由变量的值 # [100000, 110000, 120000]
闭包的作用
保存局部信息不被销毁,保证数据安全性
闭包的应用
- 可以保存一些非全局变量但是不易被销毁和改变的数据
- 装饰器的基础
二、装饰器
开放封闭原则
软件面世时不可能将所有的功能都设计好,会定期进行更新迭代。对于软件之前的源码一般不会修改,对函数里面的代码以及函数的调用方式不会改变。
封闭原则:不改变源码
开放原则:更新增加一些额外的功能
而装饰器完美的诠释了开放封闭原则
初识装饰器
定义:装饰器就是一个函数,他要装饰一个函数,在不改变原函数的源码及调用方式的前提下给其增加额外的功能
有一个函数,现在要给其增加一个测试执行效率的功能
def index(): print('欢迎访问博客园')
第一步:通过添加time模块进行测试
import time def index(): time.sleep(2) print('欢迎访问博客园首页') start_time = time.time() index() end_time = time.time() print(f'此函数的执行效率{end_time-start_time}')
虽然这种方式完成了对此函数增加效率测试功能,但当需要对多个对象增加此项功能时,就会出现过多的代码重复
第三步:通过能将功能封装在函数中来避免代码的重复
import time def index(): time.sleep(2) print('欢迎访问博客园首页') def test_time(x): start_time = time.time() x() end_time = time.time() print(f'此函数的执行效率{end_time- start_time}') test_time(index)
虽然解决了代码重复问题,并且在不改变源码的前提下完成了给函数添加了功能,但改变了函数的调用方式
第四步:通过闭包来达到使原函数的调用方式不变
import time def index(): time.sleep(2) print('欢迎访问博客园首页') def test_time(x): def inner(): start_time = time.time() x() end_time = time.time() print(f'此函数的执行效率{start_time-end_time}') return inner ret = test_time(index) ret()
此时虽然进行了闭包但是函数调用方式还是改变了
第五步:将函数调用这一步直接赋给原函数的函数名
import time def index(): time.sleep(2) print('欢迎访问博客园首页') def test_time(x): def inner(): start_time = time.time() x() end_time = time.time() print(f'此函数的执行效率{start_time-end_time}') return inner index = test_time(index) index()
这样就完成了在不改变原函数源码以及调用方式的前提下,给函数增加了一项功能
通过语法糖加装装饰器
import time def test_time(x): def inner(): start_time = time.time() x() end_time = time.time() print(f'此函数的执行效率{start_time-end_time}') return inner @test_time def index(): time.sleep(2) print('欢迎访问博客园首页') index()
被装饰的函数带返回值
当被装饰函数带返回值时,由于装饰器实际执行的时inner函数,而inner函数并没有返回值,因此当被装饰函数带返回值时,直接用上述装饰器的话得到的返回值就是None。
所以应对装饰器进行升级,对其增加返回值并且返回的应该是index函数的返回值
import time def test_time(x): def inner(): start_time = time.time() ret = x() end_time = time.time() print(f'此函数的执行效率{start_time-end_time}') return ret return inner @test_time def index(): time.sleep(2) print('欢迎访问博客园首页') return 666 index()
被装饰的函数带参数
当函数带参数时,同上面的原因,由于inner函数缺少形参函数执行时会报错,要使函数执行就需要给inner函数增加形参
import time def test_time(x): def inner(*args,**kwargs): start_time =time.time() ret = x(*args,**kwargs) end_time = time.time() print(f'此函数的执行效率{start_time-end_time}') return ret return inner @test_time def index(1,2,3): time.sleep(2) print('欢迎访问博客园首页') return 666
标准版装饰器
装饰器的格式:
def warpper(f): def inner(*args,**kwargs): '''被装饰函数之前的操作''' ret = f(*args,**kwargs) '''被装饰函数之后的操作''' return ret return inner
带参数的装饰器
举例说明,抖音:绑定的是微信账号密码。 qq:绑定的是qq的账号密码。 你现在要完成的就是你的装饰器要分情况去判断账号和密码,不同的函数用的账号和密码来源不同。
由于之前写的装饰器只能接受一个参数就是函数名,所以现在要写一个可以接受参数的装饰器。
def chioce(n): def wrapper(f): def inner(*args,**kwargs): if n== 'qq': user_name = input('请输入用户名:').strip() password = input('请输入密码:').strip() with open('qq',encoding = 'utf-8') as f1: for i in f1: user, pwd = i.strip().split('|') if user_name == user and pwd == password: ret = f(*args,**kwargs) return ret print('用户名密码错误') elif n == 'tiktok': user_name = input('请输入用户名:').strip() password = input('请输入密码:').strip() with open('qq',encoding = 'utf-8') as f1: for i in f1: user, pwd = i.strip().split('|') if user_name == user and pwd == password: ret = f(*args,**kwargs) return ret print('用户名密码错误') return inner return wrapper @chioce('qq') def qq(): print('成功访问qq') @chioce('tiktok') def tiktok(): print('成功访问抖音') qq() tiktok()
由于中间有一部分是重叠代码可进行简化:
def chioce(n): def wrapper(f): def inner(*args,**kwargs): user_name = input('请输入用户名:').strip() password = input('请输入密码:').strip() with open(n,encoding = 'utf-8') as f1: for i in f1: user, pwd = i.strip().split('|') if user_name == user and pwd == password: ret = f(*args,**kwargs) return ret else: print('用户名密码错误') return inner return wrapper @chioce('qq') def qq(): print('成功访问qq') @chioce('tiktok') def tiktok(): print('成功访问抖音') qq() tiktok()
将文件名用传进去的参数n代替,可通过n直接判断调用的使哪个函数的文件
多个装饰器装饰一个函数
有如下代码分析最后打印的结果:
def wrapper1(func1): # func1 == f函数 def inner1(): print('wrapper1,before func') # 2 func1() # 3 print('wrapper1,after func') # 4 return inner1 def wrapper2(fun2): # func2 == inner1 def inner2(): print('wrapper2,before func') # 1 func2() # func2 == inner1 print('wrapper2,after func') # 5 return inner2 @wrapper2 # f = wrapper2(f)中里面的f==inner1 外面的f==inner2 @wrapper1 # f = wrapper1(f)中里面的f==func1 外面的f==inner1 def f(): print('in f') f() 结果: wrapper2,before func wrapper1,before func in f wrapper1,after func wrapper2,after func
递归函数
递归函数:函数或者其他代码都可以解决递归解决的问题,但是递归在某些时候能出奇制胜的效果,人理解函数,神理解递归。其实递归就是函数本身调用他自己
举例:
def age(n): if n == 1: return 18 else: return age(n-1)+2 print(age(4)) 结果: 24 # 18+2+2+2
可通过
sys.setrecursionlimit
查看电脑递归次数上限:import sys import sys print(sys.setrecursionlimit(10000000)) def func(n): print(n) n += 1 print(666) func(n) func(1)
将如下列表中的每项元素打印出来:
l1 = [1, 3, 5, ['太白','元宝', 34, [33, 55, [11,33]]], [77, 88],66] def func(n): for i in n: if type(i) == list: fun(i) else: print(i) func(l1)
标签:return,函数,包及,python,之闭,inner,time,print,def 来源: https://www.cnblogs.com/yaoqi17/p/11084589.html