在第六章已经有所介绍,这里看到一篇比较好的文章转过来。
基本概念
装饰器是23z种设计模式之一,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理, Web权限校验, Cache等。
很有名的例子来理解,咖啡,加糖的咖啡,加牛奶的咖啡。 本质上,还是咖啡,只是在原有的东西上,做了“装饰”,使之附加一些功能或特性。
例如记录日志,需要对某些函数进行记录
笨的办法,每个函数加入代码,如果代码变了,就悲催了
装饰器的办法,定义一个专门日志记录的装饰器,对需要的函数进行装饰。
优点
抽离出大量函数中与函数功能本身无关的雷同代码并继续重用
即,可以将函数“修饰”为完全不同的行为,可以有效的将业务逻辑正交分解,如用于将权限和身份验证从业务中独立出来
概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能
Python中的装饰器
在Python中,装饰器实现是十分方便的
原因是:函数可以被扔来扔去。
函数作为一个对象:
A.可以被赋值给其他变量,可以作为返回值 B.可以被定义在另外一个函数内
def:
装饰器是一个函数,一个用来包装函数的函数,装饰器在函数申明完成的时候被调用,调用之后返回一个修改之后的函数对象,将其重新赋值原来的标识符,并永久丧失对原始函数对象的访问(申明的函数被换成一个被装饰器装饰过后的函数)
当我们对某个方法应用了装饰方法后, 其实就改变了被装饰函数名称所引用的函数代码块入口点,使其重新指向了由装饰方法所返回的函数入口点。
由此我们可以用decorator改变某个原有函数的功能,添加各种操作,或者完全改变原有实现
分类:
装饰器分为无参数decorator,有参数decorator
* 无参数decorator 生成一个新的装饰器函数 * 有参decorator 有参装饰,装饰函数先处理参数,再生成一个新的装饰器函数,然后对函数进行装饰
装饰器有参/无参,函数有参/无参,组合共4种
具体定义:
decorator方法
A.把要装饰的方法作为输入参数,
B.在函数体内可以进行任意的操作(可以想象其中蕴含的威力强大,会有很多应用场景),
C.只要确保最后返回一个可执行的函数即可(可以是原来的输入参数函数, 或者是一个新函数)
无参数装饰器 – 包装无参数函数
不需要针对参数进行处理和优化
def decorator(func): print "hello" return func @decorator def foo(): pass foo()
foo() 等价于:
foo = decorator(foo) foo()
无参数装饰器 – 包装带参数函数
def decorator_func_args(func): def handle_args(*args, **kwargs): #处理传入函数的参数 print "begin" func(*args, **kwargs) #函数调用 print "end" return handle_args @decorator_func_args def foo2(a, b=2): print a, b foo2(1)
foo2(1) 等价于
foo2 = decorator_func_args(foo2) foo2(1)
带参数装饰器 – 包装无参数函数
def decorator_with_params(arg_of_decorator):#这里是装饰器的参数 print arg_of_decorator #最终被返回的函数 def newDecorator(func): print func return func return newDecorator @decorator_with_params("deco_args") def foo3(): pass foo3()
与前面的不同在于:比上一层多了一层封装,先传递参数,再传递函数名
第一个函数decomaker是装饰函数,它的参数是用来加强“加强装饰”的。由于此函数并非被装饰的函数对象,所以在内部必须至少创建一个接受被装饰函数的函数,然后返回这个对象(实际上此时foo3= decorator_with_params(arg_of_decorator)(foo3))
带参数装饰器– 包装带参数函数
def decorator_whith_params_and_func_args(arg_of_decorator): def handle_func(func): def handle_args(*args, **kwargs): print "begin" func(*args, **kwargs) print "end" print arg_of_decorator, func, args,kwargs return handle_args return handle_func @decorator_whith_params_and_func_args("123") def foo4(a, b=2): print "Content" foo4(1, b=3)
内置装饰器
内置的装饰器有三个:staticmethod,classmethod, property
class A(): @staticmethod def test_static(): print "static" def test_normal(self): print "normal" @classmethod def test_class(cls): print "class", cls a = A() A.test_static() a.test_static() a.test_normal() a.test_class()
结果:
static static normal class __main__.A
A.test_static
staticmethod 类中定义的实例方法变成静态方法
基本上和一个全局函数差不多(不需要传入self,只有一般的参数),只不过可以通过类或类的实例对象来调用,不会隐式地传入任何参数。
类似于静态语言中的静态方法
B.test_normal
普通对象方法: 普通对象方法至少需要一个self参数,代表类对象实例
C.test_class
类中定义的实例方法变成类方法
classmethod需要传入类对象,可以通过实例和类对象进行调用。
是和一个class相关的方法,可以通过类或类实例调用,并将该class对象(不是class的实例对象)隐式地当作第一个参数传入。
就这种方法可能会 比较奇怪一点,不过只要你搞清楚了python里class也是个真实地存在于内存中的对象,而不是静态语言中只存在于编译期间的类型,就好办了。正常的方法就是和一个类的实例对象相关的方法,通过类实例对象进行调用,并将该实例对象隐式地作为第一个参数传入,这个也和其它语言比较像。
D.区别
staticmethod,classmethod相当于全局方法,一般用在抽象类或父类中。一般与具体的类无关。
类方法需要额外的类变量cls,当有子类继承时,调用类方法传入的类变量cls是子类,而不是父类。
类方法和静态方法都可以通过类对象和类的实例对象访问
定义方式,传入的参数,调用方式都不相同。
E.property
对类属性的操作,类似于java中定义getter/setter
class B(): def __init__(self): self.__prop = 1 @property def prop(self): print "call get" return self.__prop @prop.setter def prop(self, value): print "call set" self.__prop = value @prop.deleter def prop(self): print "call del" del self.__prop
其他
A.装饰器的顺序很重要,需要注意
@A @B @C def f ():
等价于
f = A(B(C(f)))
B.decorator的作用对象可以是模块级的方法或者类方法
C.functools模块提供了两个装饰器。 这个模块是Python 2.5后新增的。
functools.wraps(func) total_ordering(cls) 这个具体自己去看吧,后续用到了再补充
一个简单例子
通过一个变量,控制调用函数时是否统计时间
#!/usr/bin/env python # -*- coding:utf-8 -*- #@author: [email protected] #@version: a test of decorator #@date: 20121027 #@desc: just a test import logging from time import time logger = logging.getLogger() logger.setLevel(logging.DEBUG) is_debug = True def count_time(is_debug): def handle_func(func): def handle_args(*args, **kwargs): if is_debug: begin = time() func(*args, **kwargs) logging.debug( "[" + func.__name__ + "] -> " + str(time() - begin) ) else: func(*args, **kwargs) return handle_args return handle_func def pr(): for i in range(1,1000000): i = i * 2 print "hello world" def test(): pr() @count_time(is_debug) def test2(): pr() @count_time(False) def test3(): pr() if __name__ == "__main__": test() test2() test3()
结果:
hello world hello world DEBUG:root:[test2] -> 0.0748538970947 hello world