python 学习笔记【装饰器 】

装饰器 (Decorator) 在 Python 编程中极为常见,可轻松实现 Metadata、Proxy、 AOP 等模式。简单点说,装饰器通过返回包装对象实现间接调用,以此来插入额外逻辑。语法看上去和 Java Annotation、C# Attribute 类似,但不仅仅是添加元数据。

>>> @check_args
... def test(*args):
... print args

还原成容易理解的方式:

>>> test = check_args(test)

类似的做法,我们在使用 staticmethod、classmethod 时就已见过。

>>> def check_args(func):
...     def wrap(*args):
...         args = filter(bool, args)
...         func(*args)
...
...     return wrap   # 返回 wrap 函数对象

>>> @check_args    # 解释器执行 test = check_args(test)
... def test(*args):
...     print args

>>> test     # 现在 test 名字与 wrap 关联。
<function wrap at 0x108affde8>

>>> test(1, 0, 2, "", [], 3)  # 通过 wrap(test(args)) 完成调用。
(1, 2, 3)

整个过程非常简单:

  • 将目标函数对象 test 作为参数传递给装饰器 check_args。
  • 装饰器返回包装函数 wrap 实现对 test 的间接调用。
  • 原函数名字 test 被重新关联到 wrap,所有对该名字的调用实际都是调用 wrap。

你完全可以把 "@" 当做语法糖,也可以直接使用函数式写法。只不过那样不便于代码维护,毕竟 AOP 极力避免代码侵入。装饰器不一定非得是个函数返回包装对象,也可以是个类,通过 call 完成目标调用。

>>> class CheckArgs(object):
...     def __init__(self, func):
...         self._func = func
...
...     def __call__(self, *args):
...         args = filter(bool, args)
...         self._func(*args)

>>> @CheckArgs      # 生成 CheckArgs 实例。
... def test(*args):
...     print args

>>> test       # 名字指向该实例。
<__main__.CheckArgs object at 0x107a237d0>

>>> test(1, 0, 2, "", [], 3)    # 每次都是通过该实例的 __call__ 调用。
(1, 2, 3)

用类装饰器对象实例替代原函数,以后的每次调用的都是该实例的 call 方法。这种写法有点啰嗦,还得注意避免在装饰器对象上保留状态。

猜你喜欢

转载自blog.csdn.net/weixin_37805635/article/details/85924558