装饰器介绍
装饰器的本质:一个闭包函数
装饰器的功能:在不修改原函数及其调用方式的情况下对原函数功能进行扩展
一个简单的装饰器实例
def time(func):#定义一个装饰器,接收一个函数作为参数
def inner(*args,**kwargs):#在装饰器内定义一个内部函数
"""执行函数之前要做的"""
re = func(*args,**kwargs)#被装饰的函数并且要执行,并接受结果
"""执行函数之后要做的"""
return re#返回原函数的运行结果
return inner#返回内部函数名
def func():
pass
func = time(func) # 此时func ==inner
python为我们提供了简单写法
def time(func):
def inner(*args,**kwargs):
"""执行函数之前要做的"""
re = func(*args,**kwargs)
"""执行函数之后要做的"""
return re#返回原函数的运行结果
return inner
@time
def func():
pass
# @time就等于func = time(func),下面要紧接函数
#当再次调用函数func是实际调用的是inner
#接收到的参数会被inner(*args,**kwargs)接受,并存储到元祖或字典中
#而args或者kwargs在传递到原函数时又会被分解
#从而实现了不改变函数调用方式,添加了新功能
当我们明白装饰器的本质之后,就可以去搞搞更多的装饰器,比如带参数的装饰器,以及用类写一个装饰器
带参数的函数装饰器:@xxx()就相当于xxx()的返回结果还可以接受一个函数被调用,然后再返回一个函数
def deco(arg):
def _deco(func):
def __deco():
print("before %s called [%s]." % (func.__name__, arg))
func()
print(" after %s called [%s]." % (func.__name__, arg))
return __deco
return _deco
@deco("mymodule")
def myfunc():
print(" myfunc() called.")
用类实现一个装饰器:func = Clsaa(func),func()就是执行了Class中的__call__
class ttl_func:
def __init__(self,ttl_property):
self.ttl_property=ttl_property
def __call__(self, *args, **kwargs):
# 函数执行前做的
ret = self.ttl_property( *args, **kwargs)
# 函数执行后做的
return ret
@ttl_property
def cc():
print(time.time())
装饰器产生的问题
def outer(func):
def inner(*args,**kwargs):
"""我是装饰器里的函数"""
func(*args,**kwargs)
return inner
@outer
def function():
"""我是被装饰的函数"""
print("哈哈哈")
print(function.__name__) # 函数名
print(function.__doc__) # 函数注释
# 打印了装饰其中的内容,function其实就是outer(function) 就是inner函数
# inner
# 我是装饰器里的函数
# 修复方法
from functools import wraps
def outer(func):
@wraps(func)
def inner(*args,**kwargs):
"""我是装饰器里的函数"""
func(*args,**kwargs)
return inner
@outer
def function():
"""我是被装饰的函数"""
print("哈哈哈")
print(function.__name__) # 函数名
print(function.__doc__) # 函数注释
#function
#我是被装饰的函数
来了解一下wraps是如何做到的,首先我们要介绍一下partial和update_wrapper函数
functools.partial函数
该函数接收一个函数func和一些位置参数和关键字参数,内部会将这些参数绑定给func并返回一个新的函数,如果一个关键字参数被重复传入,后面的值会覆盖前面的值
from functools import partial
def add(x, y):
return x+y
# 关键字传入
add2 = partial(add, y=2)
add3 = partial(add2, y=3)
q = add2(3) # 这里将会输出5
p = add3(3) # 这里将会输出6
# 位置传入
add4 = partial(add, 2)
add5 = partial(add4, 3)
b = add2(3) # 这里将会输出5
d = add3() # 这里将会输出5
这个函数是使用C而不是Python实现的,但是官方文档中给出了Python实现的代码,如下所示,大家可以进行参考
def partial(func, *args, **keywords): # 设置的参数
def newfunc(*fargs, **fkeywords): # 调用的参数传入
newkeywords = keywords.copy()
newkeywords.update(fkeywords) # 关键字参数的更新
return func(*args, *fargs, **newkeywords)
newfunc.func = func
newfunc.args = args
newfunc.keywords = keywords
return newfunc
functools.update_wrapper
源码
WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',
'__annotations__')
WRAPPER_UPDATES = ('__dict__',)
def update_wrapper(wrapper,
wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES):
for attr in assigned:
try:
value = getattr(wrapped, attr) # 从被修饰的函数中获得指定属性
except AttributeError:
pass
else:
setattr(wrapper, attr, value) # 设置给修饰他的函数
for attr in updated:
getattr(wrapper, attr).update(getattr(wrapped, attr, {})) # 更新修饰字典的键值
wrapper.__wrapped__ = wrapped
return wrapper #返回修饰函数
使用update_wrapper来修复一个修饰器函数
from functools import update_wrapper
def wrapper(f):
def wrapper_function(*args, **kwargs):
"""这个是修饰函数"""
return f(*args, **kwargs)
update_wrapper(wrapper_function, f) # << 此处修复
return wrapper_function
@wrapper
def wrapped():
"""这个是被修饰的函数"""
print('wrapped')
print(wrapped.__doc__) # 输出`这个是被修饰的函数`
print(wrapped.__name__) # 输出`wrapped`
functools.wraps
源码:
def wraps(wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES):
return partial(update_wrapper, wrapped=wrapped,
assigned=assigned, updated=updated)
@wraps
def func():
pass
# func = wraps(func) = partial(update_wrapper, wrapped=func,assigned=assigned, updated=updated) = 一个绑定好属性的函数