0

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.

1 Answer 1

2

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.

How to create a singleton class:

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.

Sign up to request clarification or add additional context in comments.

3 Comments

One thing to be careful about when using __new__() to implement the singleton pattern is that __init__() will always be called on the instance that gets returned, even if it's already previously been initialized. Using a metaclass lets you control if/when __init__() gets called. So even though metaclasses are more complicated and do have the downsides mentioned here, I think they're the only option if there's any possibility of subclasses implementing __init__().
"metaclasses" are far from the only option for singletons. People have a recipe, and that is it. It is easy to circunvent a repeated call to __init__, the easier being: do not write an __init__, and put all code in __new__.
Still, the simplest way to do a singleton in Python is simply: to create the needed instance as a module variable, and import that instance where its needed instead of importing the class. Marking the class itself as private with a "_" prefix is optional.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.