Python 装饰器
装饰器在不修改源代码块的情况下,增加代码的功能,有助于代码的精简,抽象出公共的辅助模块,便于维护代码
入门
入门主要以 python 之禅的教程,谈装饰器前,还要先要明白一件事,Python 中的函数和 Java、C++不太一样,Python 中的函数可以像普通变量一样当做参数传递给另外一个函数
def foo():
print("foo")
def bar(func):
func()
bar(foo)
装饰器本质上是一个 Python 函数或类 ,它可以让其他函数或类在不需要做任何代码修改的前提下增加额外功能,装饰器的返回值也是一个函数/类对象。它经常用于 有切面需求的场景 ,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景,装饰器是解决这类问题的绝佳设计。有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码到装饰器中并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能
# 打印日志为例子
def use_logging(func):
logging.warn("%s is running" % func.__name__)
func()
def foo():
print('i am foo')
# 打印日志的功能可以实现 但是破坏了原来的代码结构 变成每次调用 foo 都要调用 use_logging(foo)
use_logging(foo)
def use_logging(func):
def wrapper():
logging.warn("%s is running" % func.__name__)
return func() # 把 foo 当做参数传递进来时,执行 func() 就相当于执行 foo()
return wrapper
def foo():
print('i am foo')
foo = use_logging(foo) # 因为装饰器 use_logging(foo) 返回的时函数对象 wrapper,这条语句相当于 foo = wrapper
foo()
use_logging 就是一个装饰器,它一个普通的函数,它把执行真正业务逻辑的函数 func 包裹在其中,看起来像 foo 被 use_logging 装饰了一样,use_logging 返回的也是一个函数,这个函数的名字叫 wrapper。在这个例子中,函数进入和退出时,被称为一个横切面,这种编程方式被称为 面向切面的编程
@语法糖
此时会发现还是要为程序多添加一句 foo = use_logging(foo)
,python 提供了 @
的语法糖,用于省略 foo = use_logging(foo)
这样的赋值功能
def use_logging(func):
def wrapper():
logging.warn("%s is running" % func.__name__)
return func()
return wrapper
@use_logging # 实现了 foo = use_logging(foo) 的功能
def foo():
print('i am foo')
foo()
装饰有参数的函数
# 相当于 foo = use_logging(foo) 返回的是一个 wrapper 函数, 然后调用 wrapper(name, age=None, height=None)
def use_logging(func):
def wrapper(*args, **kwargs):
# args 是一个数组,kwargs 一个字典
logging.warn("%s is running" % func.__name__)
return func(*args, **kwargs)
return wrapper
@use_logging
def foo(name, age=None, height=None):
print("I am %s, age %s, height %s" % (name, age, height))
带参数的装饰器
装饰器还有更大的灵活性,例如带参数的装饰器。装饰器的语法允许我们在调用时,提供其它参数,比如 @decorator(a)。这样,就为装饰器的编写和使用提供了更大的灵活性。比如,我们可以在装饰器中指定日志的等级,因为不同业务函数可能需要的日志级别是不一样的
def use_logging(level):
def decorator(func):
def wrapper(*args, **kwargs):
if level == "warn":
logging.warn("%s is running" % func.__name__)
elif level == "info":
logging.info("%s is running" % func.__name__)
return func(*args)
return wrapper
return decorator
@use_logging(level="warn")
def foo(name='foo'):
print("i am %s" % name)
foo()
类装饰器
相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性 可继承性(详见 Python 进阶-装饰器 ) 等优点。使用类装饰器主要依靠类的 __call__
方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。
class Foo(object):
def __init__(self, func):
self._func = func
def __call__(self):
print ('class decorator runing')
self._func()
print ('class decorator ending')
@Foo
def bar():
print ('bar')
bar()
functools.wraps 找回被装饰函数的元信息
from functools import wraps
def logged(func):
@wraps(func) # with_logging = wraps(with_logging)
def with_logging(*args, **kwargs):
print func.__name__ # 输出 'f',如果没有 @wraps(func) 返回的是 with_logging
print func.__doc__ # 输出 'does some math',如果没有 @wraps(func) 返回的是 None
return func(*args, **kwargs)
return with_logging
@logged
def f(x):
"""does some math"""
return x + x * x
# 这个相当于 f = wraps(logged(f))
多个装饰器
@a
@b
@c
def f ():
pass
执行顺序是从里到外,最先调用最里层的装饰器,最后调用最外层的装饰器,它等效于 f = a(b(c(f)))
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: Python 魔术方法
下一篇: Covenant 利用分析
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论