@songying
2018-07-27T14:12:46.000000Z
字数 1825
阅读 1445
python高级特性
从装饰模式看python装饰器
def clock(func):def clocked(*args, **kwargs):...return resultreturn clocked
如果读者了解设计模式的话,应该都知道有一种常见且强大的模式叫做装饰模式。它的定义是:
动态的给一个对象添加一些额外的职责。看不太懂是吧,我们来举个简单的例子:假设我们现在有很多简毛呸房要装修, 装修方式有多种
python中的装饰器要远远比面向对象中的装饰模式要强大的多,这要得益于python中一等函数的概念。
回到主题, 装饰器本质上是一个 Python函数或类,它可以让其他函数或类在不需要做任何代码修改的前提下增加额外功能,装饰器的返回值也是一个函数/类对象。 装饰器在python中极为常见,是初学者进阶的必会知识。
def decoder_test(func):def wrapper():print("我是decoder_test装饰器")return func()return wrapperdef test():print('我是 test 函数')real_test = decoder_test(test)real_test()
我们来分析一下上面的代码, 首先我们定义了一个函数decoder_test来作为装饰器, 然后定义了一个普通的函数test, 最后我们通过将test传入到decoder_test的方式来获得装饰后的函数real_test_, 而这个real_test与test已经是完全不同的函数了。
接下来我们分析一下程序中的数据流:
real_test = decoder_test(test): 我们将test()传入decoder_test()中, 然后返回wrapper()。 注意,此时的wrapper()中的func()已经变成了test()。 也就是说,real_test()函数等价于:
def real_test():print("我是decoder_test装饰器")return test()
为了更方便的让你使用装饰器, python提供了@语法糖,通过@,你可以精简你的代码。如下的两段代码是等价的。假设有一个名为decorate的装饰器。
@decoratedef target():print('running target()')def target():print('running target()')target = decorate(target)
那么,上面的那个小例子可以等价于:
def decoder_test(func):def wrapper():print("我是decoder_test装饰器")return func()return wrapper@decoder_testdef test():print('我是 test 函数')test()
这是装饰器的一个关键特性:装饰器在被装饰的函数定义之后立即执行。这通常是在导入时(即python加载模块时)。
我们在上面的例子上稍微修改一下:
def decoder_test(func):print("我是decoder_test装饰器")return func@decoder_testdef test():print('我是 test 函数')def main():print("运行main函数中")test()if __name__ == "__main__":main()
此时的输出为:
我是decoder_test装饰器运行main函数中我是 test 函数
这也就是说:函数装饰器在导入模块时立即执行, 而被装饰的函数只在明确调用时运行。 在实际中, 装饰器通常在一个模块中定义,然后应用到其他模块中的函数上。
@d1@d2def f():print('f')
等价于:f = d1(d2(f))
问题: 如何让装饰器接受其他参数?
答案: 创建一个装饰器工厂函数,把参数传给它,返回一个装饰器,然后再把它应用到要装饰的函数上。
def clock(fmt=DEFAULT_FMT): # 参数化装饰器工厂函数def decorate(func): # 真正的装饰器def clocked(*_args):..._result = func(*_args)...return _resultreturn clockedreturn decorate
