python装饰器装饰类

来源:互联网 发布:esp8266与单片机通讯 编辑:程序博客网 时间:2024/04/20 12:19

装饰器可以用来像装饰函数一样装饰类(毕竟类也是可调用对象)
装饰类可以有多种用途。类装饰器可以和被装饰类的属性交互。一个类装饰器可以添加或增强属性,或者它可以修改类的API, 在类如何被定义和它的实例如何被使用之间提供区别。你可能 会问,添加或增强一个类的属性的合适做法不是通过子类么?通常,答案是这样。然而,在某些情况下,备选的方法可能更合适。例如,一个通用的可应用特性可能应用到你的应用的许多类上,在你的类结构的不同地方

举个例子,考虑类的这样一种特性,每个实例知道它是什么时候被实例化的,实例通过创建时间排序。这对许多不同的类有普遍的可应用性。
需要三个额外的属性—–实例化时间戳, __gt__ 和 __lt__ 方法。
有很多途径来进行添加. 你可以用一个类装饰器这样做:

import functoolsimport timedef sortable_by_creation_time(cls):  """Given a class, augment the class to have its instances   be  sortable  by the timestamp at which they were instantiated.  """  # Augment the class' original ‘__init__‘ method to alsostore a  # ‘_created‘ attribute on the instance, which corresponds     #to when it  # was instantiated.  original_init = cls.__init__  @functools.wraps(original_init)  def new_init(self, *args, **kwargs):    original_init(self, *args, **kwargs)    self._created = time.time()  cls.__init__ = new_init  #Add ‘__lt__‘ and ‘__gt__‘ methods that return True or    # False based on  # the created values in question.  cls.__lt__ = lambda self, other: self._created < other._created  cls.__gt__ = lambda self, other: self._created > other._created  # Done; return the class object.  return cls

这个装饰器做的第一件事情是保存了类的原始的__init__方法的副本。你不必担心这个类是否有这个方法。因为对象有__init__ 方法,这个属性的出现会有保证。下一步,创建一个新方法指派给__init__
这个方法首先调用原始的__init__然后做一部分额外工作, 保存实例的时间戳到 self._created.
值得注意的是这同前面的例子中的执行时包装代码非常相似的模式—-让一个函数包装另一个函数,它的主要职责是是运行被包装函数,但也会添加小片其它功能片段。
如果一个被@sortable_by_creation_time装饰的类定义了自己的__lt__ 和__gt__方法 ,那么这个装饰器会覆盖它们。
如果这个类没有意识到created是用来排序的,_created本身来讲就没什么用处。因此装饰器也添加 __lt_ 和 __gt__魔法方法.这些引起 < 和 > 操作符基于那些方法的结果返回True或者False。

这也会影响sorted和其它相似函数的行为
这是有必要的所有来让随便一个类的实例通过它们的实例化时间可排序。
这个装饰器可以被应用给任何类。包括许多有不相关的祖先的类。这有一个通过创建时间的可排序实例的简单类的例子

>>> @sortable_by_creation_time… class Sortable(object):... def __init__(self, identifier):... self.identifier = identifier… def _repr_(self):... return self.identifier…>>> first = Sortable('first')>>> second = Sortable('second')>>> third = Sortable('third')>>>>>> sortables = [second, first, third]>>> sorted(sortables)[first, second, third]

一定牢记,虽然装饰器可以解决一个问题,但不意味着它一定就是合适的解决方案。当说到这个例子,同样的事情能够通过使用一个“mixin,”或者只定义了合适的__init__, __lt__, 和 __gt__的很​小的类来完成。

一个使用了mixin的简单方法:

import timeclass SortableByCreationTime(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

应用mixin给一个类可以通过使用Python的多继承来做到:

class MyClass(MySuperclass, SortableByCreationTime):  pass

这个方式有不同的优点和缺点。一方面,它会毫不留情地犁过(plow over)由类或者它的超类(可能以后再阅读代码时,这会不太明显,装饰器正在“修理”两个方法)定义的__lt__ 和 __gt__方法。
另一方面,可能很容易陷入一种境地,由SortableByCreationTime提供的__init__方法不会运行
如果MyClass 或者
MySuperclass 或者任何在MySuperclass的祖先中中定义了一个__init__方法 ,这个方法会胜出。反转类的顺序无法解决这个问题;

相反,装饰器会很好地处理__init__的情况,只需要增强被装饰类的__init__方法的影响,否则不做任何变动。

哪种情况是正确的? 视情况而定。

原创粉丝点击