温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

使用Python怎么实现一个类装饰器

发布时间:2021-05-19 18:01:24 来源:亿速云 阅读:189 作者:Leah 栏目:开发技术

使用Python怎么实现一个类装饰器?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

编写类装饰器

类装饰器类似于函数装饰器的概念,但它应用于类,它们可以用于管理类自身,或者用来拦截实例创建调用以管理实例。

单体类

由于类装饰器可以拦截实例创建调用,所以它们可以用来管理一个类的所有实例,或者扩展这些实例的接口。

下面的类装饰器实现了传统的单体编码模式,即最多只有一个类的一个实例存在。

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关键字,它可以为每个类提供一个封闭的作用域,如下:

def singleton(aClass):  instance = None  def onCall(*args):  nonlocal instance  if instance == None:   instance = aClass(*args)  return instance  return onCall

当然,我们也可以用类来编写这个装饰器——如下代码对每个类使用一个实例,而不是使用一个封闭作用域或全局表:

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__用于拦截未定义的属性名的访问。如下例子所示:

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__技术来包装一个完整接口提供了一个替代的、方便的方法。如下:

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属性。

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的五大特点是什么

python的五大特点:1.简单易学,开发程序时,专注的是解决问题,而不是搞明白语言本身。2.面向对象,与其他主要的语言如C++和Java相比, Python以一种非常强大又简单的方式实现面向对象编程。3.可移植性,Python程序无需修改就可以在各种平台上运行。4.解释性,Python语言写的程序不需要编译成二进制代码,可以直接从源代码运行程序。5.开源,Python是 FLOSS(自由/开放源码软件)之一。

看完上述内容,你们掌握使用Python怎么实现一个类装饰器的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注亿速云行业资讯频道,感谢各位的阅读!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI