清除元类单例

Clearing a MetaClass Singleton

我已经使用 Method 3 of this answer

中讨论的 MetaClass 创建了一个单例
 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): ...

我希望能够在 unittest.TestCasesetUp() 方法中清除 Singleton,以便每个测试都以干净的 Singleton 开始。

我想我不太明白这个元类在做什么,因为我无法获得 clear() 方法的正确咒语:

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

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

sing=MySing()
sing.clear()

上面的 type 调用 returns Singleton 不是 MySing.

乍一看,我看到了这个元class三个有用的测试用例。

  • 测试单个 class 创建是否正常。
  • 测试在第一个class之后是否没有创建新的
  • 测试多个 class 是否可以结合使用此元class。

所有这些测试都可以在没有 "reset" 按钮的情况下实现 。之后,您将涵盖大部分基础知识。 (我可能忘记了一个)。

只需创建几个使用此元的不同 TestClassesclass 并检查它们的 ID 和类型。

让我们来看看 Singleton 的(更正)定义和使用它定义的 class。我正在用 Singleton 替换 cls 的使用,无论如何都要通过查找。

 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:
             pass

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 是 metaclass 的一个 class 属性,用于将 class 映射到 class 的唯一实例。

__call__是metaclass的一个实例方法;它的目的是使 metaclass(即 classes)的实例可调用。 cls 这里是定义的 class, 不是 元 class。所以每次调用 MyClass() 时,都会转换为 Singleton.__call__(MyClass).

clear 也是 metaclass 的实例方法,这意味着它还采用 meta class 的实例(即再次 class)作为一个参数(不是用 metaclass 定义的 class 的实例)这意味着 MyClass.clear()Singleton.clear(MyClass) 相同。 (这也意味着你可以,但为了清楚起见,可能不应该写成 s1.clear()。)

metaclass实例方法与“常规”classclass方法的标识也解释了为什么需要在metaclass中使用__call__ ] 在常规 class 中使用 __new__ 的地方: __new__ 是特殊情况下的 class 方法,无需这样修饰它。 metaclass 为 its 实例定义 instance 方法有点棘手,所以我们只使用 __call__ (因为 type.__call__ 除了调用正确的 __new__ 方法之外没有做太多事情(如果有的话))。