前面写python的AOP解决方案时提到了decorator,这篇文章就详细的来整理下python的装饰器——decorator。
python中的函数即objects
一步一步来,先了解下python中的函数。
def shout(word='hello,world'): |
python中函数还可以嵌套,即将函数定义在另一个函数里面。例如
def talk(): |
从上面的例子我们已经可以得到,函数可以被赋值给另外的变量,也能在另一个函数里面定义函数。可以推断函数也能返回给另外的函数或者作为参数传递给其他函数。看下面的代码:
def getTalk(type='shout'): |
初识decorator
明白以上的这些,将有助于理解装饰器decorator,其实装饰器decorator就是在不改变函数的本身情况下在函数执行的前后包装另外的代码来改变函数的具体表现行为。有点儿AOP的味道。继续从代码来解释。
# 装饰器就是一个将其他函数(也就是被装饰的函数)作为参数的一个function |
如果不想调用a_stand_alone_function_decorated()这个方法,还是钟情于以前的那个名字,方法也简单。重写下a_stand_alone_function方法即可。即:
a_stand_alone_function = my_shiny_new_decorator(a_stand_alone_function) |
decorator揭秘
上面的代码就是装饰器的体现。现在用python中专门的decorator语法就是酱紫滴:
|
简单来说,@my_shiny_new_decorator就是another_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function)的缩写。
python中的装饰器其实是GoF中的装饰模式的一个变种。像迭代器iterators也是一种设计模式。
装饰器还可以同时使用多个。
def bread(func): |
被装饰的函数传参
仅仅在包装的函数上将参数传递过来即可。
def a_decorator_passing_arguments(function_to_decorate): |
装饰类中的方法
python中的方法和函数差不多,只不过类中的方法的第一个参数是当前对象的引用self。
def method_friendly_decorator(method_to_decorate): |
在不知道参数个数的情况下有一种更加通用的传参方法,就是用*args, **kwargs。*args代表没有给定默认值的参数列表(arg1,arg2,……),**kwwars代表有给定默认值(arg1=val1,arg2=val2,……)
def a_decorator_passing_arbitrary_arguments(function_to_decorate): |
将参数传给装饰函数
因为装饰函数本身要将一个函数作为参数传递进来,那怎么把参数传给装饰函数呢?其实不能直接将函数传递给装饰函数。但也有解决方案,先看下面的例子
# 装饰器函数也是function |
def decorator_maker(): |
用标记间隔下,整个输出如下:
我是构造装饰器的工人,当你让我构造装饰器时,我被执行一次 |
省略中间步骤,如下
#省略中间步骤,如下 |
可以更短
|
上面@后面是一个函数调用,调用函数当然可以传递参数。就是在上面的@decorator_maker()括号里面加上参数而已。
# -*- coding: utf-8 -*- |
通过这种方案就可以实现往装饰器里面传递参数了,甚至可以用上面的*args, **kwargs来传递任意参数。值得注意的是装饰器只被调用了一次,当python在import当前脚本的时候调用。不能在后面再动态的设置传递的参数,当import xx的时候,被decorated的函数已经被装饰好了,不能再变了。
decorator注意事项
- python2.4及其以上版本有此功能
- 装饰器使代码执行的效率变低了
- 一旦一个函数已经被装饰好了,就定下来了,不能变了
- 装饰器decorator在函数之间包装起来了,比较难debug
python自己也提供了几个装饰器,property,staticmethod等。Django用装饰器来管理缓存和视图权限,能够把python的decorator玩透,将会帮你解决很多事情。
参考资料(其实就是翻译了下):stackoverflow