Очистка 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, которые используют этот метакласс, и проверьте их идентификаторы и типы.