Reputation: 5723
Below is the well known code for creating a singleton 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)
cls.x = 5
return cls._instances[cls]
class MyClass(metaclass=Singleton):
pass
m = MyClass()
v = MyClass()
print (m.x)
m.x = 420
print (v.x)
My question is why do we need to use the call function of type class again to initialize the class? Why can't we call the init method to do that like normal class initialization. Something like this :
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = cls(*args, **kwargs)
cls.x = 5
return cls._instances[cls]
class MyClass(metaclass=Singleton):
pass
m = MyClass()
v = MyClass()
print (m.x)
m.x = 420
print (v.x)
This is getting into an infinite loop anyways.
Upvotes: 0
Views: 3934
Reputation: 110746
Because trying to creating an instance of a class by just calling it as you do in the line cls._instances[cls] = cls(*args, **kwargs)
just by itself calls the metaclass __call__
which is the exact method where you attempt the call, as is explained here.
Now, if one thing, you should not really be using metaclasses just for creating singletons.
The Metaclass mechanism in Python is complicated - the problem you've hit on this question shows you are grasping now how simple inheritance and call to methods on super-classes work - and metaclasses are an order of magnitude more complicated than that.
And, beyond been complicated, classes with a custom metaclass can't be ordinarily combined with other classes that feature custom metaclasses, so the general rule is keeping their usage to a minimum anyway.
But for creatign a singleton, you can just place all your checks in the class ordinary __new__
method. No need to feedle with metaclasses - just plain class inheritance:
_instances = {}
class Singleton(object):
def __new__(cls, *args, **kw):
if not cls in _instances:
instance = super().__new__(cls)
_instances[cls] = instance
return _instances[cls]
And just inherit your singleton classes from this one.
Upvotes: 2