清除MetaClass Singleton

时间:2018-04-27 15:05:22

标签: python singleton metaclass

我使用MetaClass创建了一个Singleton,如Method 3 of this answer中所述

 class Singleton(type):
      _instances = {}
      def __call__(cls, *args, **kwargs):
         if cls not in cls._instances:
             cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
         return cls._instances[cls]


class MySing(metaclass=Singleton): ...

我希望能够在setUp()的{​​{1}}方法中清除Singleton,以便每个测试都以一个干净的Singleton开始。

我想我真的不明白这个metaClass正在做什么,因为我无法获得unittest.TestCase方法的正确咒语:

clear()

对我在这里做错了什么的想法?我的单身人士没有被清除。

     def clear(self):
       try:
          del(Singleton._instances[type(self)]
       except KeyError:
          pass   #Sometimes we clear before creating

上面的sing=MySing() sing.clear() 调用会返回type而不是Singleton

2 个答案:

答案 0 :(得分:2)

让我们来看看Singleton的一个(更正的)定义和一个使用它定义的类。我正在将cls的使用替换为Singleton,无论如何都会在其中传递查询。

 class Singleton(type):
     _instances = {}

     # Each of the following functions use cls instead of self
     # to emphasize that although they are instance methods of
     # Singleton, they are also *class* methods of a class defined
     # with Singleton
     def __call__(cls, *args, **kwargs):
         if cls not in Singleton._instances:
             Singleton._instances[cls] = super().__call__(*args, **kwargs)
         return Singleton._instances[cls]

     def clear(cls):
         try:
             del Singleton._instances[cls]
         except KeyError:
             continue

class MySing(metaclass=Singleton):
    pass

s1 = MySing()   # First call: actually creates a new instance
s2 = MySing()   # Second call: returns the cached instance
assert s1 is s2 # Yup, they are the same
MySing.clear()  # Throw away the cached instance
s3 = MySing()   # Third call: no cached instance, so create one
assert s1 is not s3  # Yup, s3 is a distinct new instance

首先,_instances是元类的类属性,用于将类映射到该类的唯一实例。

__call__是元类的实例方法;其目的是使元类(即类)的实例可调用。 cls这里是要定义的类,不是元类。因此,每次拨打MyClass()时,都会转换为Singleton.__call__(MyClass)

clear也是元类的实例方法,这意味着它还将元类的实例(即,类)作为参数(不是使用元类定义的类的实例)。这意味着MyClass.clear()Singleton.clear(MyClass)相同。 (这也意味着你可以,但可能不应该清楚,写s1.clear()。)

使用“常规”类类方法识别元类实例方法也解释了为什么需要在元类中使用__call__,在常规类中使用__new__:{{1}作为一个类方法是特殊的,而不必像这样装饰它。元类为实例定义实例方法有点棘手,所以我们只使用__new__(因为__call__没有做太多,如果有的话,除了调用正确的type.__call__方法之外)。

答案 1 :(得分:-1)

我乍一看这个元类有三个有用的测试用例。

  • 测试单个类创建是否正常工作。
  • 测试在初始类之后是否没有创建新类。
  • 测试多个类是否可以结合使用此元类。

所有这些测试都可以在没有“重置”按钮的情况下实现。在此之后,您将涵盖大部分基础。 (我可能已经忘记了一个)。

只需创建一些使用此元类的不同TestClasses并检查其Id和类型。