How can super() instantiate a class inside it's own __new__ method?

Question:

I recently had a use case for a singleton class and ended up using this definition:

class SubClient(BaseClient):

    def __new__(cls):
        if not hasattr(cls, 'instance'):
            cls.instance = super(SubClient, cls).__new__(cls)
        return cls.instance

After testing that this worked, some questions came up:

How is it possible that super(SubClient, cls).__new__(cls) returns an instance of SubClient within the definition of the SubClient.__new__ method?

__new__ is the method that creates a SubClient so how is it possible that within the definition of this method we can already create a SubClient ?

Asked By: jhnclvr

||

Answers:

The only way to create a new object is to call object.__new__. When you override __new__, the intent is to either return some other pre-existing object, or to use super to get an ancestor class to provide an object.

Note that __new__ is a static method that’s special-cased so that you don’t have to decorate it with @staticmethod. object.__new__ does not necessarily create an instance of object: object.__new__(foo) creates a new object of type foo, whatever foo might be. That’s why you need to explicitly pass the cls value as the first argument, rather than just being able to write super(SubClient, cls).__new__().


Beware of using __new__ like this to implement the singleton design pattern. When you call SubClient(...), it uses SubClient.__new__ to get an instance of SubClient. As long as SubClient.instance is an instance of SubClient, it will invoke cls.instance.__init__ again, even though it was initialized when first created. This is at best inefficient and unnecessary, at worst an unexpected reinitiization of an object that you didn’t expect.

Consider this code:

class A:
    instance = None

    def __init__(self, x):
        print("In __init__")
        self.x = x

    def __new__(cls, x):
        if cls.instance is None:
            cls.instance = super().__new__(cls)
        return cls.instance


a1 = A(3)
a1.x = 5
a2 = A(9)
assert a1 is a2
assert a1.x == 9  # Not 5

Two calls to A, one call to object.__new__, but two calls to A.instance.__init__ on the same object. Calling A(9) returns the same instance that a1 refers to, but causes it to be reinitialized, overwriting the previous change to a1.x made by a1.x = 5.

Instead of overriding __new__ use an explicit class method to access the class attribute.

class A:
    instance = None

    def __init__(self, x):
        self.x = x

    @classmethod
    def get_it(cls, x):
        if cls.instance is None:
            cls.instance = cls(x)
        return cls.instance

a1 = A.get_it(3)

Whether it makes sense for a function that returns a singleton to be parameterized in the first place is a question for another time. If it makes sense to parameterize, you may want a memoized function rather than a singleton (which is in some sense a special case of memoization).

Answered By: chepner
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.