Очистка MetaClass Singleton - PullRequest
       20

Очистка MetaClass Singleton

0 голосов
/ 27 апреля 2018

Я создал 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 ]

0 голосов
/ 27 апреля 2018

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

0 голосов
/ 27 апреля 2018

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

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

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

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

...