一、闭包
定义:内层函数对外层函数非全局变量的引用,就会形成闭包,闭包只存在于嵌套函数中
- 被引用的非全局变量也称为自由变量,这个自由变量会与内层函数产生一个绑定关系
- 自由变量不会再内存中消失
- 闭包的作用,保证数据的安全 (返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域)
# 默认情况下inner函数是没有办法被外层执行的
def outer():
name = "小白,自学编程"
def inner():
print("inner",name)
return inner #返回inner的内存地址
func = outer() #相当于把inner赋值给func
func() #相当于执行了inner
print(outer.__code__.co_freevars) # 查看有没有自由变量,如果有说明是闭包
二、装饰器
定义:在不改变源代码和调用方式的情况下增加新功能,装饰器的本质就是闭包
准守开放封闭原则
封闭:已经实现的模块代码不应该被修改
开放:对现有功能的扩展开放
2.1、初识别装饰器
写一个函数,测试另外同事写的函数执行效率
版本一
import time
def index():
time.sleep(2)
print('欢迎访问博客园主页')
return 666
def timer(func):
def inner():
start_time = time.time()
func()
stop_time = time.time()
print(f'此函数的执行效率为{stop_time-start_time}')
return inner #返回inner
index = timer(index)
index() #相当于执行inner
通过以上的代码已经实现了测试执行效率的功能,但是有两个问题,
- 第一,当执行index() 的时候其实是执行了inner,而inner函数里边的func其实才是真正执行的index(),所以原来index里边的return 666 无法返回,如果想要返回应该把func()的执行结果赋值给一个变量,然后inner函数return这个变量
- 第二,如果执行index() 需要带有变量,那么这个时候会报错,原因是执行index()相当于执行了 inner(),而执行inner()又相当于执行func(),所以如果想要index()带有变量,inner()和func()都应该加上相应的变量,可用使用万能变量,接收任何参数
版本二:
import time
def index(name):
time.sleep(2)
print(f'{name}欢迎访问博客园主页')
return 666
def timer(func):
def inner(*args,**kwargs):
start_time = time.time()
s = func(*args,**kwargs) # 将func的返回结果赋值给一个变量
stop_time = time.time()
print(f'此函数的执行效率为{stop_time-start_time}')
return s # 返回func的返回结果,相当于返回index的返回结果
return inner
index = timer(index)
index('老王')
通过版本二的改进,上边说的两个问题已经解决了,但是还有一个问题,和一个改进的地方
- 第一,每次函数在添加装饰器的时候需要在调用函数之前加上一句index = timer(index) ,显得比较麻烦,python引入了一个语法糖的东西,改进这一缺点
- 第二,在使用装饰器之后,原函数的属性会变成包装器的属性,如果还想保留原函数的属性就需要用
functools.wraps
functools.wraps
就是装饰包装器的装饰器
版本三
from functools import wraps #调用装饰包装器模块
import time
def timer(func):
@wraps(func) # 装饰包装器
def inner(*args,**kwargs): #包装器
start_time = time.time()
s = func(*args,**kwargs)
stop_time = time.time()
print(f'此函数的执行效率为{stop_time-start_time}')
return s
return inner
@timer # 语法糖 相当于index = timer(index)
def index(name):
time.sleep(2)
print(f'{name}欢迎访问博客园主页')
return 666
index('老王')
2.2 、装饰器模板
def wrapper(f):
def inner(*args,**kwargs):
'''添加额外功能,执行被装饰函数之前的操作'''
ret = f(*args,**kwargs)
'''添加额外功能,执行被装饰函数之后的操作'''
return ret
return inner
三、装饰器的应用
网页登录验证
某网站需要给所有页面添加用户名密码验证功能,只有通过验证的用户才可以访问网页资源,并且在一个网页通过验证后,其他网页可直接访问,如果用户密码输入错误超过三次直接退出程序
from functools import wraps
account={
"is_authenticated":False,#用户登录了就把这个改成Ture
"username": "laowang", #假装这里是数据库的用户
"password": "abc123"
}
def login(funk):
@wraps(funk)
def inner(*args):
if account["is_authenticated"] is False:
count = 0
while count < 3:
username = input("user:")
password = input("password:")
if username == account["username"] and password == account["password"]:
print("welcome login.....")
account["is_authenticated"]=True
funk(*args)
break
else:
print("wrong username or password")
count += 1
if count == 3:
exit()
else:
print("用户以登录,验证通过....")
funk(*args)
return inner #返回inner的内存地址
@login
def home():
print("欢迎来到首页")
@login
def america():
print("欢迎来到欧美专区")
@login #添加装饰器,等于henan = login(henan)
def henan(vip_live):
if vip_live > 3: #vip等及大于3的解锁本专区高级私密
print("欢迎来到河南专区,解锁本专区高级私密")
else:
print("欢迎来到河南专区")
# henan = login(henan)
home()
henan(4)
america()