【转载】:理解 Python 装饰器看这一篇就够了
【转载】:简单聊聊Python中的wraps修饰器
1)
# 1) 【简单装饰器】use_logging 就是一个装饰器,它一个普通的函数,它把执行真正业务逻辑的函数 func 包裹在其中,看起来像 foo 被 use_logging 装饰了一样
def use_logging(func):
def wrapper():
logging.warning("{} is running".format(func.__name__))
return func()
return wrapper
def foo():
print('i am foo')
foo = use_logging(foo)
foo()
"""
i am foo
WARNING:root:foo is running
"""
2)
# 2)【语法糖】有了 @ ,我们就可以省去foo = use_logging(foo)这一句了
def use_logging(func):
def wrapper():
logging.warning("{} is running".format(func.__name__))
return func()
return wrapper
@use_logging
def foo():
print('i am foo')
foo()
"""
i am foo
WARNING:root:foo is running
"""
3)
# 3) 【*args, **kwargs】如此一来,甭管 foo 定义了多少个参数,我都可以完整地传递到 func 中去
def use_logging(func):
def wrapper(*args, **kwargs):
logging.warning("{} is running".format(func.__name__))
return func(*args, **kwargs)
return wrapper
@use_logging
def foo(name, age=None, height = None):
print("i am {}, age {}, height {}".format(name, age, height))
foo('allen', 3, 110)
"""
WARNING:root:foo is running
i am allen, age 3, height 110
"""
4)
# 4) 带参数的装饰器
def use_logging(level):
def decorator(func):
def wrapper(*args, **kwargs):
if level == "warn":
logging.warning("%s is running" % func.__name__)
elif level == "info":
logging.info("%s is running" % func.__name__)
return func(*args, **kwargs)
return wrapper
return decorator
# @use_logging(level= "info")
@use_logging(level= "warn")
def foo(name='foo'):
print('i am %s' % name)
foo("allen")
"""
WARNING:root:foo is running
i am allen
"""
5)
# 5)【装饰器不仅仅针对函数,也可以对类】 类装饰器主要靠 __call__方法
class Foo:
def __init__(self, func):
self._func = func
def __call__(self, *args, **kwargs):
print('class decorator runing')
self._func()
print('class decorator ending')
@Foo
def bar():
print('bar')
bar()
"""
class decorator runing
bar
class decorator ending
"""
6)
# 6)【functools.wraps】wraps本身也是一个装饰器,它能把原函数的元信息拷贝到装饰器里面的 func 函数中
from functools import wraps
def logged(func):
@wraps(func)
def with_logging(*args, **kwargs):
print(func.__name__)
print(func.__doc__)
return func(*args, **kwargs)
return with_logging
@logged
def cal_func(x):
"""does some math"""
return x + x*x
cal_func(2)
"""
cal_func
does some math
"""
7)
"""这个函数的声明如下:functools.partial(func, *args, **keywords)。它的作用就是返回一个partial对象,当这个partial对象被调用的时候,就像通过func(*args, **kwargs)的形式来调用func函数一样。如果有额外的 位置参数(args) 或者 关键字参数(*kwargs) 被传给了这个partial对象,那它们也都会被传递给func函数,如果一个参数被多次传入,那么后面的值会覆盖前面的值。
"""
from functools import partial
def add(x:int, y:int):
return x+y
add2 = partial(add, y=2)
print(add2(3))
8)
# update_wrapper update_wrapper(wrapper_function, f) # << 添加了这条语句 等价于 @wraps(f)
"""
从上面的例子我们可以看到,我想要获取wrapped这个被修饰函数的文档字符串,但是却获取成了wrapper_function的文档字符串,wrapped函数的名字也变成了wrapper_function函数的名字。这是因为给wrapped添加上@wrapper修饰器相当于执行了一句wrapped = wrapper(wrapped),执行完这条语句之后,wrapped函数就变成了wrapper_function函数。遇到这种情况该怎么办呢,首先我们可以手动地在wrapper函数中更改wrapper_function的__doc__和__name__属性,但聪明的你肯定也想到了,我们可以直接用update_wrapper函数来实现这个功能。
"""
"""
没错,就是这么的简单,只有这么一句,我们可以看出,wraps函数其实就是一个修饰器版的update_wrapper函数,它的功能和update_wrapper是一模一样的。我们可以修改我们上面的自定义修饰器的例子,做出一个更方便阅读的版本。
"""
from functools import wraps
def wrapper(f):
@wraps(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__)