Очистка MetaClass Singleton

Я создал Singleton, используя MetaClass, как описано в методе 3 этого ответа

 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() метод 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 не MySing,

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__ в обычном классе: __new__ это особый случай как метод класса, без необходимости украшать его как таковой. Для метакласса немного сложно определить метод экземпляра для его экземпляров, поэтому мы просто используем __call__ (поскольку type.__call__ не делает ничего, если вообще что-то, кроме вызова правильного __new__ метод).

Я вижу три полезных теста для этого метакласса на первый взгляд.

  • Проверьте, правильно ли работает создание одного класса.
  • Проверьте, не создан ли новый класс после исходного.
  • Проверьте, могут ли несколько классов использовать этот метакласс совместно.

Все эти тесты могут быть выполнены без кнопки "перезагрузки". После чего вы охватите большую часть своих баз. (Я мог бы забыть один).

Просто создайте несколько разных TestClass, которые используют этот метакласс, и проверьте их идентификаторы и типы.

Другие вопросы по тегам