闭包的定义:
闭包是嵌套在函数中的函数。
闭包必须是内层函数对外层函数的变量(非全局变量)的引用。
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)
原文:https://www.cnblogs.com/yaoqi17/p/11084589.html