[关闭]
@songying 2018-07-27T22:12:46.000000Z 字数 1825 阅读 1221

python 装饰器

python高级特性


从装饰模式看python装饰器

一个标准的装饰器

  1. def clock(func):
  2. def clocked(*args, **kwargs):
  3. ...
  4. return result
  5. return clocked

什么是装饰模式?

如果读者了解设计模式的话,应该都知道有一种常见且强大的模式叫做装饰模式。它的定义是:

动态的给一个对象添加一些额外的职责。看不太懂是吧,我们来举个简单的例子:假设我们现在有很多简毛呸房要装修, 装修方式有多种

什么是装饰器?

python中的装饰器要远远比面向对象中的装饰模式要强大的多,这要得益于python中一等函数的概念。

回到主题, 装饰器本质上是一个 Python函数或类,它可以让其他函数或类在不需要做任何代码修改的前提下增加额外功能,装饰器的返回值也是一个函数/类对象。 装饰器在python中极为常见,是初学者进阶的必会知识。

一个简单的例子

  1. def decoder_test(func):
  2. def wrapper():
  3. print("我是decoder_test装饰器")
  4. return func()
  5. return wrapper
  6. def test():
  7. print('我是 test 函数')
  8. real_test = decoder_test(test)
  9. real_test()

我们来分析一下上面的代码, 首先我们定义了一个函数decoder_test来作为装饰器, 然后定义了一个普通的函数test, 最后我们通过将test传入到decoder_test的方式来获得装饰后的函数real_test_, 而这个real_testtest已经是完全不同的函数了。
接下来我们分析一下程序中的数据流:

real_test = decoder_test(test): 我们将test()传入decoder_test()中, 然后返回wrapper()注意,此时的wrapper()中的func()已经变成了test() 也就是说,real_test()函数等价于:

  1. def real_test():
  2. print("我是decoder_test装饰器")
  3. return test()

语法糖

为了更方便的让你使用装饰器, python提供了@语法糖,通过@,你可以精简你的代码。如下的两段代码是等价的。假设有一个名为decorate的装饰器。

  1. @decorate
  2. def target():
  3. print('running target()')
  4. def target():
  5. print('running target()')
  6. target = decorate(target)

那么,上面的那个小例子可以等价于:

  1. def decoder_test(func):
  2. def wrapper():
  3. print("我是decoder_test装饰器")
  4. return func()
  5. return wrapper
  6. @decoder_test
  7. def test():
  8. print('我是 test 函数')
  9. test()

python何时执行装饰器?

这是装饰器的一个关键特性:装饰器在被装饰的函数定义之后立即执行。这通常是在导入时(即python加载模块时)。
我们在上面的例子上稍微修改一下:

  1. def decoder_test(func):
  2. print("我是decoder_test装饰器")
  3. return func
  4. @decoder_test
  5. def test():
  6. print('我是 test 函数')
  7. def main():
  8. print("运行main函数中")
  9. test()
  10. if __name__ == "__main__":
  11. main()

此时的输出为:

  1. 我是decoder_test装饰器
  2. 运行main函数中
  3. 我是 test 函数

这也就是说:函数装饰器在导入模块时立即执行, 而被装饰的函数只在明确调用时运行。 在实际中, 装饰器通常在一个模块中定义,然后应用到其他模块中的函数上。

叠放装饰器

  1. @d1
  2. @d2
  3. def f():
  4. print('f')

等价于:f = d1(d2(f))

参数化装饰器

问题: 如何让装饰器接受其他参数?
答案: 创建一个装饰器工厂函数,把参数传给它,返回一个装饰器,然后再把它应用到要装饰的函数上。

  1. def clock(fmt=DEFAULT_FMT): # 参数化装饰器工厂函数
  2. def decorate(func): # 真正的装饰器
  3. def clocked(*_args):
  4. ...
  5. _result = func(*_args)
  6. ...
  7. return _result
  8. return clocked
  9. return decorate
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注