类装饰器是用来装饰python类的,使用方法就是在类定义的头部加上@decorator
基本用法如下:
#-*- coding:utf-8 -*-
def decorator(original_cls):
class Wrapper:
def __init__(self, *args):
print "Wrapper __init__",args
self.original_inst = original_cls(*args)
def __getattr__(self, name):
print "Wrapper:__getattr__",name
return getattr(self.original_inst, name)
return Wrapper
@decorator
class A:
def __init__(self, x, y):
self.firstName = u'小二'
self.lastName = u'王'
print "A:__init__",x,y
x = A(6, 7)
print(x.lastName)
print(x.firstName)
代码返回结果如下:
Wrapper __init__ (6, 7)
A:__init__ 6 7
Wrapper:__getattr__ lastName
王
Wrapper:__getattr__ firstName
小二
decorator函数接受一个类做为入参,返回一个新类Wrapper。
A(6,7)其实是Wrapper(6,7),所以首先实例化Wrapper,调用Wrapper的__init__函数,在Wrapper的__init__函数中,生成了原A类的一个实例,所以紧接着调用了A的__init__函数。
x.lastName语句,x其实是Wrapper的实例,该实例没有lastName属性,调用类任何没有的实例都会调佣该类的 __getattr__,所以首先调用了Wrapper的__getattr__函数,__getattr__函数调用了getattr(self.original_inst,name),其实就是调用了Wrapper实例中保存的A类实例的lastName属性,所以正确的返回了属性值“王”。
不用装饰器语法等价代码如下:
#-*- coding:utf-8 -*-
def decorator(original_cls):
class Wrapper:
def __init__(self, *args):
print "Wrapper __init__",args
self.original_inst = original_cls(*args)
def __getattr__(self, name):
print "Wrapper:__getattr__",name
return getattr(self.original_inst, name)
return Wrapper
class A:
def __init__(self, x, y):
self.firstName = u'小二'
self.lastName = u'王'
print "A:__init__",x,y
A = decorator(A) #这里是关键代码
x = A(6, 7)
print(x.lastName)
print(x.firstName)
类装饰器一个常见的错误用法把装饰器decorator定义为一个类,把被装饰的类存放在该类的成员变量中,当获取原类的属性时,使用__getattr__函数进行截获,然后在该函数中使用getattr获取原类的属性。代码如下:
#-*- coding:utf-8 -*-
class Decorator:
def __init__(self, C):
self.orignalClass = C
print"__init__"
def __call__(self, *args):
self.wrapped = self.orignalClass(*args)
print "__call__",self.wrapped.name
return self
def __getattr__(self, attrname):
return getattr(self.wrapped, attrname)
@Decorator
class C:
def __init__(self,name):
self.name = name
print "C:",name
x = C("王小二")
y = C("万能的小明") # 把x覆盖了
print x.name
print y.name
测试结果如下:
__init__
C: 王小二
__call__ 王小二
C: 万能的小明
__call__ 万能的小明
万能的小明
万能的小明
从返回结果可以看出,第二个实例y把x覆盖了。
@Decorator def class C: ... 等价于C = Decorator(C),所以C就是Decorator的一个实例
x = C("王小二")就相当于x = Decorator.__call__("王小二"),该函数生成了一个原C类的一个实例,并把自己(Decorator实例)返回了。
y = C("万能的小明")又调用了Decorator.__call__函数,重新生成了原C类的一个实例,以前的实例被覆盖了。所以后面打印x.name和y.name都变成了“万能的小明”
装饰器还可以嵌套使用,如下面代码所示:
def a(f):
return lambda: 'a' + f()
def b(f):
return lambda: 'b' + f()
def c(f):
return lambda: 'c' + f()
@a
@b
@c
def function():
return "function"
print function()
返回结果为abcfunction,上面代码等于与function = a(b(c(function)))
装饰器还可以带参数
def decorator(*args):
def wrapper(f):
return lambda: ''.join(args) + f()
return wrapper
@decorator('a','b','c')
def function():
return "function"
print function()
这个代码相当于function = decorator('a','b','c')(function)
返回结果为abcfunction