Python装饰器的使用2---类装饰器

内容绝大部分出自《Python高级编程》,Luke Sneeringer,清华大学出版社,Python版本2.7。
代码部分经修改可以完整运行,方便理解和直接测试。

装饰器接受一个可调用的对象(函数,类)作为参数,返回一个可调用的对象(函数,类),所即既可以装饰类返回类,也可以装饰函数返回类。
类装饰器可以与被装饰类的属性交互,修改类的方法(包括私有方法)等。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/usr/bin/env python
#_*_ coding:utf-8 _*_
import functools
import time
def sortable_by_creation_time(cls):
#保留原始__init__方法,并追加_created属性
origin_init = cls.__init__
@functools.wraps(origin_init)
def new_init(self,*args,**kwargs):
origin_init(self,*args,**kwargs)
#仅设置该值没有用处,因为sort等函数无法识别。所以还要添加相应的方法
self._created = time.time()
cls.__init__ = new_init
#添加小于和大于方法,如果cls本身已经拥有了__lt__和__gt__方法,则这两个方法
#会被覆盖
cls.__lt__ = lambda self,other:self._created < other._created
cls.__gt__ = lambda self,other:self._created > other._created
return cls
#有了__lt__和__gt__方法后,可以比较数值大小,也就可以被相关函数操作了。
@sortable_by_creation_time
class Sortable(object):
def __init__(self,identifier):
self.identifier = identifier
def __repr__(self):
return self.identifier
first = Sortable('The first one.')
second = Sortable('The second one.')
third = Sortable('The third one.')
#开始是乱序
sortables = [third,second,first]
print 'Before => ',sortables
print
#排序后是升序
print 'After => ',sorted(sortables)

上述装饰器的方法可以用mixin来实现,也很简单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/usr/bin/env python
#_*_ coding:utf-8 _*_
import time
#定义mixin类
class SortedByCreationTime(object):
def __init__(self):
self._created = time.time()
def __lt__(self,other):
return self._created < other._created
def __gt__(self,other):
return self._created > other._created
#使用多重继承为类添加方法
class Sortable(SortedByCreationTime):
def __init__(self,identifier):
#如果不添加这部分内容,私有属性将无法继承
#SortedByCreationTime.__init__(self)
super(Sortable,self).__init__()
self.identifier = identifier
def __repr__(self):
return self.identifier
#测试
first = Sortable('The first one.')
second = Sortable('The second one.')
third = Sortable('The third one.')
sortables = [third,second,first]
print 'Before => ',sortables
print dir(first)
print second > third
print 'After => ',sorted(sortables)

装饰器可以装饰一个函数,但是返回一个类,因为两者都是可调用的,并且可以将类理解为函数的更高层次。
类可以比函数定义更多内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/usr/bin/env python
#_*_ coding:utf-8 _*_
class Task(object):
def run(self,*args,**kwargs):
raise NotImplementedError('Subclasses must implement `run`.')
def identify(self):
return 'I am task .'
def task(func):
class TaskSubclass(Task):
def run(self,*args,**kwargs):
return func(*args,**kwargs)
return TaskSubclass
@task
def foo():
return 4
f = foo()
print f.run()
#调用变麻烦了
print foo().run()
print foo.identify()
#查看类型
print type(foo),type(foo())
#无法直接调用类,因为类中没有添加__call__方法
print foo()

上述装饰器的改进版:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/env python
#_*_ coding:utf-8 _*_
class Task(object):
#追加了__call__方法,使得生成的类也可以直接调用,看起来更像函数了
def __call(self,*args,**kwargs):
return self.run(*args,**kwargs)
def run(self,*args,**kwargs):
raise NotImplementedError('Subclasses must implement `run`.')
def identify(self):
return 'I am task .'
def task(func):
class TaskSubclass(Task):
def run(self,*args,**kwargs):
return func(*args,**kwargs)
#返回的是调用,即类的实例
return TaskSubclass()
@task
def foo():
return 4
f = foo()
#可以像函数一样直接调用了
print foo()
print foo.identify()