3

Why foo is printed twice?

class A:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self, *, var):
        print('foo')
        self.var = var

a = A(var=1)
b = A(var=2)

assert a == b
assert a.var == b.var
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
johanson
  • 147
  • 1
  • 7

1 Answers1

3

When you write A(...), you are calling

type.__call__(A, ...)

That's because type is the metaclass of A. type.__call__ will call __new__ and __init__ in sequence. __init__ will always be called if __new__ returns an instance of the class. Here is a simplified view:

def __call__(cls, *args, **kwargs):
    self = cls.__new__(cls, *args, **kwargs)
    if isinstance(self, cls):
        cls.__init__(self, *args, **kwargs)
    return self

The simplest way I can think of making a singleton work this way is to put all your initialization logic into __new__:

class A:
    _instance = None

    def __new__(cls, *, var):
        if cls._instance is None:
            self = super().__new__(cls)
            self.var = var
            cls._instance = self

        return cls._instance

    def __init__(self, *args, **kwargs):
        print('foo')

Sure, foo still gets printed every time you ask for a new instance, but it's well and truly a singleton.

A more comprehensive approach is to override the behavior of your metaclass __call__ method. That will avoid calling __init__ except once:

class Singleton(type):
    def __call__(cls, *args, **kwargs):
        if hasattr(cls, '_instance'):
            return cls._instance
        return super().__call__(*args, **kwargs)

class A(metaclass=Singleton):
    def __init__(self, *, var):
        print('foo')
        self.var = var
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264