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
?
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).
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
?
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).