闭包函数与装饰器
一、闭包函数
闭是封闭(函数内部的函数),包是包含(该内部函数对外部作用域而非全局作用域的变量的引用)。
闭包就是指:函数内部的函数 对外部作用域 而非全局作用域 的引用。
闭包函数的作用:可以把 闭包函数内部的变量 + 闭包函数内部的函数 这两者包裹在一起, 然后通过返回值的形式返回出来。
闭包函数的特点:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域。
def f1(url):
def f2():
print(url)
return f2
res = f1() # res = f2
res() # res() == f2()
二、装饰器
1、装饰器的定义
装饰器:就是一种用来 为 被装饰函数对象 添加额外功能的 函数
特点:
- 不改变原函数的代码
- 不改变原函数的调用方式
import time
def index():
print('hello world')
time.sleep(1)
def f2():
print('f2')
time.sleep(2)
start = time.time()
index()
end = time.time()
print(f'run time is {end-start}')
start = time.time()
f2()
end = time.time()
print(f'run time is {end-start}')
index 和 f2 的功能一样,而且可以发现对他们的使用方式也是一样的,因此可以想办法将调用它们的代码简化,即可以再定义一个函数来使用它们。
第一种方法:改变调用方式
import time
def index():
print('hello world')
time.sleep(1)
def time_count(func):
start = time.time()
func()
end = time.time()
print(f'{func} run time is {end-start}')
time_count(index)
可以发现,这样做和上面一种方法得到的结果是一样的,但是可以发现使用index的方法不一样了。
第二种方法:包给函数-外包
import time
def index():
print('hello world')
time.sleep(1)
def time_count(func):
def wrapper():
start = time.time()
func()
end = time.time()
print(f'{func} run time is {end-start}')
return wrapper
# f = time_count(index)
# f() # 这里的f其实就是在调用index函数,但如果命名为f,用户就不知道你在调用index
index = time_count(index)
index() # 新变量也命名为index,对用户来说,就是在调用之前的index,只不过功能更新了
没有改变index的源代码,也没有改变index函数的调用方式,这里的time_count函数就是装饰器。
我们发现,最后调用index函数的时候实际上就是在调用wrapper函数,因此我们想到,如果index有返回值,那wrapper函数中必须要有一个变量用来接收index函数的返回值,并作为wrapper函数的返回值返回出来。
我们做了如下调整:
import time
def index():
print('hello world')
time.sleep(1)
return 123
def time_count(func):
def wrapper():
start = time.time()
res = func()
end = time.time()
print(f'{func} run time is {end-start}')
return res
return wrapper
index = time_count(index)
res = index()
print(f'res:{res}')
最后可以返回index函数的返回值 123。
如果原始的index()方法需要传参,那么我们之前的方法是无法实现的,由于有wrapper_ = index(),所以给wrapper()函数传参即可。
import time
def home(name):
print(f'welcome {name} to home page')
time.sleep(1)
return name
def time_count(func):
def wrapper(*args,**kwargs):
start = time.time()
res = func(*args,**kwargs)
end = time.time()
print(f'{func} time is {end - start}')
return res
return wrapper
home = time_count(home) # 前一个home = wrapper , 括号里的home = home函数
res = home('egon') # == wrapper('egon')
print(f'res:{res}')
2、装饰器语法糖
在被装饰函数正上方,并且是单独一行写上@装饰器名
import time
def time_count(func):
def wrapper(*args,**kwargs):
start = time.time()
res = func(*args,**kwargs)
end = time.time()
print(f'{func} run time is {end-start}')
return res
return wrapper
@time_count
def home(name):
print(f'welcome {name} to home page')
time.sleep(1)
return name
res = home('egon')
print(f'res:{res}')
效果和上面一个方法一样
3、装饰器模板
def deco(func):
def wrapper(*args,**kwargs):
res = func(*args,**kwargs)
return res
return wrapper
按模板写就行了
三、三层装饰器
套三层的装饰器,其实是指含参装饰器
username_list = []
def sanceng(role):
def login_deco(func):
def wrapper(*args,**kwargs):
if username_list:
print('请勿重复登录')
res = func(*args,**kwargs)
return res
username_lnp = input('请输入用户名:')
pwd_inp = input('请输入密码:')
with open(f'{role}_info.txt','r',encoding='utf8') as fr:
for user_info in fr:
username,pwd = user_info.strip().split(':')
if username_lnp == username and pwd_inp== pwd:
print('登录成功')
username_list.append(username)
res = func(*args,**kwargs)
return res
else:
print('登陆失败')
return wrapper
return login_deco
@sanceng('user')
def index(x,y):
print('index')
print('x,y',x,y)
return 123
因为sanceng('user') 返回 login_deco,
所以上面的函数其实就是:
@login_deco
def index(x,y):
print('index')
print('x,y',x,y)
return 123
res = index(10,20)