Python-闭包与装饰器
闭包函数
闭包函数本身是一个函数,是把闭包函数内部的函数 + 函数内部的变量包裹起来,然后通过返回值 返回出去。
闭包函数必须得符合函数嵌套
# 闭包函数:本身是一个函数,让其调用更方便
# 定义
# 把一个函数名 + 变量 封装成一个函数 返回(return)出去
def out_fun(url):
# url = "qinyj"
def iner_fun():
print(url)
return iner_fun
a = out_fun("url")
a()
现在有一个函数,老板有一个需求,想在这个函数上加一个计时功能,就是计算这个函数执行时间,需求吩咐下去,某某公司的3个运维开发开始思考了。。。
def index():
print(from index)
第一个人 小青开始做。。。他的方式是修改源代码:
import time
def index():
start = time.time()
print("from index")
time.sleep(1)
end = time.time()
print(end - start)
index()
from index
1.0080575942993164
第二个人 小杰开始做。。。他的方式是在调用的时候计算时间
import time
def index():
print("from index")
time.sleep(1)
start = time.time()
index()
end = time.time()
print(end - start)
from index
1.0000572204589844
第三个人 小强开始做。。。他用了一个非常牛逼的看不懂的骚操作。。
import time
def index():
print("from index")
def time_count(func):
def inner():
start = time.time()
func()
time.sleep(1)
end = time.time()
print(end - start)
return inner
index = time_count(index)
index()
from index
1.0000572204589844
好了,现在三个人已经全部做完,老板该验收了,把他们三个人叫到会议室。。。
指明了小强做的非常不错!另外两个向他学习,小强做的方式就是装饰器
什么叫装饰器?
装饰器:本身是一个函数,给已有的函数加功能
原则:
不改变源代码
不改变其调用方式
刚才只有小强的代码符合上面规定
- 装饰器本身是函数,只不过它用来装饰 被装饰的函数
- 装饰器装饰函数 不改变被装饰函数源代码
- 装饰器装饰函数 不改变被装饰函数的调用方式
实现装饰器
# v1 实现最简单的装饰器
import time
def index():
"""被装饰的函数"""
print('index')
time.sleep(1)
# time_count装饰器:对被装饰函数计时
def time_count(func): # func才是真正的index
"""装饰器"""
def wrapper():
start = time.time()
func()
end = time.time()
print(end - start)
return wrapper
index = time_count(index) # index == wrapper
index() # wrapper()
# v2:带返回值
import time
def index():
"""被装饰的函数"""
print('x',x)
print('index')
time.sleep(1)
return 'index'
# time_count装饰器:对被装饰函数计时
def time_count(func): # func才是真正的index
"""装饰器"""
def wrapper():
start = time.time()
res = func() # index()
end = time.time()
print(end - start)
return res
return wrapper
index = time_count(index) # index == wrapper
res = index() # wrapper()
print(res)
# v3:加参数
import time
def index(x,y,z=10):
"""被装饰的函数"""
print('x',x)
print('index')
time.sleep(1)
return 'index'
# time_count装饰器:对被装饰函数计时
def time_count(func): # func才是真正的index
"""装饰器"""
def wrapper(*args,**kwargs): # (10, 20) # *args和**kwargs接收了所有的参数
start = time.time()
res = func(*args,**kwargs) # index() # *(10,20) # *args和**kwargs打散参数传给真正的index
end = time.time()
print(end - start)
return res
return wrapper
index = time_count(index) # index == wrapper
res = index(10,20,320) # wrapper()
print(res)
以上均为二层装饰器:
-
用来装饰函数的,它本质是函数
-
不改变函数源代码
-
不改变函数调用方式
如果实在理解不了,就记住装饰器模板:
from functools import wraps
def deco(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 要加什么功能就加上去
res = func(*args, **kwargs)
return res
return wrapper
这里提到了一个wraps,其实Python装饰器(decorator)在实现的时候,被装饰后的函数其实已经是另外一个函数了(函数名等函数属性会发生改变),为了不影响,Python的functools包中提供了一个叫wraps的decorator来消除这样的副作用。写一个decorator的时候,最好在实现之前加上functools的wrap,它能保留原有函数的名称和docstring。
废话不多说,上俩例子就能搞明白!
实例一:
不加wraps
# -*- coding=utf-8 -*-
from functools import wraps
def my_decorator(func):
def wrapper(*args, **kwargs):
'''decorator'''
print('Calling decorated function...')
return func(*args, **kwargs)
return wrapper
@my_decorator
def example():
"""Docstring"""
print('Called example function')
print(example.__name__, example.__doc__)
# 执行结果:
# ('wrapper', 'decorator')
实例二:
加wraps
# -*- coding=utf-8 -*-
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
'''decorator'''
print('Calling decorated function...')
return func(*args, **kwargs)
return wrapper
@my_decorator
def example():
"""Docstring"""
print('Called example function')
print(example.__name__, example.__doc__)
# 执行结果:
# ('example', 'Docstring')
warps 作用: 消除或修复(被装饰后的函数名等属性的改变)副作用
装饰器语法糖
在被装饰的函数上 加上 @装饰器函数名,目的是为了让代码更加简洁。
@time_count
def index(x,y,z=10):
"""被装饰的函数"""
print('x',x)
print('index')
time.sleep(1)
return 'index'
三层装饰器
三层装饰器就是给装饰器加参数,在原有的两层装饰器上再嵌套一层函数,利用闭包的思想,把函数变量和函数名返回出去。
username_list = []
def sanceng(role):
def login_deco(func):
def wrapper(*args, **kwargs):
if username_list:
print('已经登录,请勿重复登录')
res = func(*args, **kwargs)
return res
username_inp = 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_inp == username and pwd_inp == pwd:
print('登录成功')
username_list.append(username)
res = func(*args, **kwargs)
return res
else:
print('登录失败')
return wrapper
return login_deco
@sanceng('admin')
def index(x, y):
print('index')
print('x,y', x, y)
return 123
res = index(10, 20)
print(res)