destroy()
as Singleton
's classmethod
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]
@classmethod
def destroy(metacls, cls):
if cls in metacls._instances:
del metacls._instances[cls]
class MockObject(metaclass=Singleton):
def __init__(self, *args, **kwargs):
pass
if __name__ == '__main__':
m = MockObject()
print(Singleton._instances)
# call `destroy()` at metaclass level with subclass as argument
Singleton.destroy(MockObject)
print(Singleton._instances)
# when called after destroy()
# a new instance is created at a different memory location
m = MockObject()
print(Singleton._instances)
Output:
{<class '__main__.MockObject'>: <__main__.MockObject object at 0x1004b6660>}
{}
{<class '__main__.MockObject'>: <__main__.MockObject object at 0x1004b6690>}
destroy()
as subclass's classmethod
Simply without @classmethod
:
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]
def destroy(cls):
# NOTE: cls._instances is reference to Singleton._instances
if cls in cls._instances:
del cls._instances[cls]
class MockObject(metaclass=Singleton):
def __init__(self, *args, **kwargs):
pass
if __name__ == '__main__':
m = MockObject()
print(Singleton._instances)
# call `destroy()` at subclass level
MockObject.destroy()
print(Singleton._instances)
m = MockObject()
print(Singleton._instances)
Output:
{<class '__main__.MockObject'>: <__main__.MockObject object at 0x104696600>}
{}
{<class '__main__.MockObject'>: <__main__.MockObject object at 0x104696630>}
The classes defined use metaclass
are instances of metaclass, so the instance method of metaclass are inherited as classmethod of regular class, i.e. classmethod
of MockObject
is instance method of Singleton
.
destroy()
as subclass's instance method
If you need define a destroy method in MockObject
's instances, you'd have to operate in the __new__
method of metaclass.
class Singleton(type):
_instances = {}
def __new__(cls, name, bases, namespace):
namespace.update(destroy=Singleton.destroy)
return super().__new__(cls, name, bases, namespace)
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
@staticmethod
def destroy(self):
cls = self.__class__
if cls in cls._instances:
del cls._instances[cls]
class MockObject(metaclass=Singleton):
def __init__(self, *args, **kwargs):
pass
if __name__ == '__main__':
m = MockObject()
print(Singleton._instances)
# call `destroy()` at instance level
m.destroy()
print(Singleton._instances)
m = MockObject()
print(Singleton._instances)
Output:
{<class '__main__.MockObject'>: <__main__.MockObject object at 0x10431aa20>}
{}
{<class '__main__.MockObject'>: <__main__.MockObject object at 0x10431aa50>}
destroy
method onMockObject
, where it belongs (or make aMockBase
that does this). But there's no point in trying to destroy the instance anyway; if there is any other reference to it anywhere, it will stay alive. – Madison__del__
method on the metaclass? – Topleveldel Singleton._instances[cls]
maybe. You could also use thegc
module to make sure there's only one reference and throw an error otherwise. – Madisongc
– Croon__new__
on the metaclass instead of__call__
, which should call both__new__
and__init__
? That's basically whatbool
andNoneType
do in Python. – Toplevel