本文实例讲述了Python类装饰器。分享给大家供大家参考,具体如下:
编写类装饰器
类装饰器类似于函数装饰器的概念,但它应用于类,它们可以用于管理类自身,或者用来拦截实例创建调用以管理实例。
单体类
由于类装饰器可以拦截实例创建调用,所以它们可以用来管理一个类的所有实例,或者扩展这些实例的接口。
下面的类装饰器实现了传统的单体编码模式,即最多只有一个类的一个实例存在。
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
|
instances = {} # 全局变量,管理实例
def getInstance(aClass, * args):
if aClass not in instances:
instances[aClass] = aClass( * args)
return instances[aClass] #每一个类只能存在一个实例
def singleton(aClass):
def onCall( * args):
return getInstance(aClass, * args)
return onCall
为了使用它,装饰用来强化单体模型的类:
@singleton # Person = singleton(Person)
class Person:
def __init__( self ,name,hours,rate):
self .name = name
self .hours = hours
self .rate = rate
def pay( self ):
return self .hours * self .rate
@singleton # Spam = singleton(Spam)
class Spam:
def __init__( self ,val):
self .attr = val
bob = Person( 'Bob' , 40 , 10 )
print (bob.name,bob.pay())
sue = Person( 'Sue' , 50 , 20 )
print (sue.name,sue.pay())
X = Spam( 42 )
Y = Spam( 99 )
print (X.attr,Y.attr)
|
现在,当Person或Spam类稍后用来创建一个实例的时候,装饰器提供的包装逻辑层把实例构建调用指向了onCall,它反过来调用getInstance,以针对每个类管理并分享一个单个实例,而不管进行了多少次构建调用。
程序输出如下:
Bob 400
Bob 400
42 42
在这里,我们使用全局的字典instances来保存实例,还有一个更好的解决方案就是使用Python3中的nonlocal关键字,它可以为每个类提供一个封闭的作用域,如下:
1
2
3
4
5
6
7
8
|
def singleton(aClass):
instance = None
def onCall( * args):
nonlocal instance
if instance = = None :
instance = aClass( * args)
return instance
return onCall
|
当然,我们也可以用类来编写这个装饰器——如下代码对每个类使用一个实例,而不是使用一个封闭作用域或全局表:
1
2
3
4
5
6
7
8
|
class singleton:
def __init__( self ,aClass):
self .aClass = aClass
self .instance = None
def __call__( self , * args):
if self .instance = = None :
self .instance = self .aClass( * args)
return self .instance
|
跟踪对象接口
类装饰器的另一个常用场景是每个产生实例的接口。类装饰器基本上可以在实例上安装一个包装器逻辑层,来以某种方式管理其对接口的访问。
前面,我们知道可以用__getattr__运算符重载方法作为包装嵌入到实例的整个对象接口的方法,以便实现委托编码模式。__getattr__
用于拦截未定义的属性名的访问。如下例子所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class Wrapper:
def __init__( self ,obj):
self .wrapped = obj
def __getattr__( self ,attrname):
print ( 'Trace:' ,attrname)
return getattr ( self .wrapped,attrname)
>>> x = Wrapper([ 1 , 2 , 3 ])
>>> x.append( 4 )
Trace: append
>>> x.wrapped
[ 1 , 2 , 3 , 4 ]
>>>
>>> x = Wrapper({ 'a' : 1 , 'b' : 2 })
>>> list (x.keys())
Trace: keys
[ 'b' , 'a' ]
|
在这段代码中,Wrapper类拦截了对任何包装对象的属性的访问,打印出一条跟踪信息,并且使用内置函数getattr来终止对包装对象的请求。
类装饰器为编写这种__getattr__
技术来包装一个完整接口提供了一个替代的、方便的方法。如下:
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
|
def Tracer(aClass):
class Wrapper:
def __init__( self , * args, * * kargs):
self .fetches = 0
self .wrapped = aClass( * args, * * kargs)
def __getattr__( self ,attrname):
print ( 'Trace:' + attrname)
self .fetches + = 1
return getattr ( self .wrapped,attrname)
return Wrapper
@Tracer
class Spam:
def display( self ):
print ( 'Spam!' * 8 )
@Tracer
class Person:
def __init__( self ,name,hours,rate):
self .name = name
self .hours = hours
self .rate = rate
def pay( self ):
return self .hours * self .rate
food = Spam()
food.display()
print ([food.fetches])
bob = Person( 'Bob' , 40 , 50 )
print (bob.name)
print (bob.pay())
print ('')
sue = Person( 'Sue' ,rate = 100 ,hours = 60 )
print (sue.name)
print (sue.pay())
print (bob.name)
print (bob.pay())
print ([bob.fetches,sue.fetches])
|
通过拦截实例创建调用,这里的类装饰器允许我们跟踪整个对象接口,例如,对其任何属性的访问。
Spam和Person类的实例上的属性获取都会调用Wrapper类中的__getattr__逻辑,由于food和bob确实都是Wrapper的实例,得益于装饰器的实例创建调用重定向,输出如下:
Trace:display
Spam!Spam!Spam!Spam!Spam!Spam!Spam!Spam!
[1]
Trace:name
Bob
Trace:pay
2000
Trace:name
Sue
Trace:pay
6000
Trace:name
Bob
Trace:pay
2000
[4, 2]
示例:实现私有属性
如下的类装饰器实现了一个用于类实例属性的Private声明,也就是说,属性存储在一个实例上,或者从其一个类继承而来。不接受从装饰的类的外部对这样的属性的获取和修改访问,但是,仍然允许类自身在其方法中*地访问那些名称。类似于Java中的private属性。
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
traceMe = False
def trace( * args):
if traceMe:
print ( '[' + ' ' .join( map ( str ,args)) + ']' )
def Private( * privates):
def onDecorator(aClass):
class onInstance:
def __init__( self , * args, * * kargs):
self .wrapped = aClass( * args, * * kargs)
def __getattr__( self ,attr):
trace( 'get:' ,attr)
if attr in privates:
raise TypeError( 'private attribute fetch:' + attr)
else :
return getattr ( self .wrapped,attr)
def __setattr__( self ,attr,value):
trace( 'set:' ,attr,value)
if attr = = 'wrapped' : # 这里捕捉对wrapped的赋值
self .__dict__[attr] = value
elif attr in privates:
raise TypeError( 'private attribute change:' + attr)
else : # 这里捕捉对wrapped.attr的赋值
setattr ( self .wrapped,attr,value)
return onInstance
return onDecorator
if __name__ = = '__main__' :
traceMe = True
@Private ( 'data' , 'size' )
class Doubler:
def __init__( self ,label,start):
self .label = label
self .data = start
def size( self ):
return len ( self .data)
def double( self ):
for i in range ( self .size()):
self .data[i] = self .data[i] * 2
def display( self ):
print ( '%s => %s' % ( self .label, self .data))
X = Doubler( 'X is' ,[ 1 , 2 , 3 ])
Y = Doubler( 'Y is' ,[ - 10 , - 20 , - 30 ])
print (X.label)
X.display()
X.double()
X.display()
print (Y.label)
Y.display()
Y.double()
Y.label = 'Spam'
Y.display()
# 这些访问都会引发异常
"""
print (X.size())
print (X.data)
X.data = [ 1 , 1 , 1 ]
X.size = lambda S: 0
print (Y.data)
print (Y.size())
|
这个示例运用了装饰器参数等语法,稍微有些复杂,运行结果如下:
[set: wrapped <__main__.Doubler object at 0x03421F10>]
[set: wrapped <__main__.Doubler object at 0x031B7470>]
[get: label]
X is
[get: display]
X is => [1, 2, 3]
[get: double]
[get: display]
X is => [2, 4, 6]
[get: label]
Y is
[get: display]
Y is => [-10, -20, -30]
[get: double]
[set: label Spam]
[get: display]
Spam => [-20, -40, -60]
希望本文所述对大家Python程序设计有所帮助。
原文链接:https://blog.csdn.net/gavin_john/article/details/50923988