What Does `super()` Mean In `__new__`
Solution 1:
To begin with super()
in itself is simply shorthand for super(A, B)
, where A
is the class wherein the code occurs, and B
is the first argument to the function in which the code occurs; so in your particular case, super().__new__(cls)
expands to super(CarModel, cls).__new__(cls)
.
In turn, super(T, O)
returns a "super object". To understand what a super object does, you need to understand how attribute references on instances and classes work in Python.
Assuming no __getattr__
or __getattribute__
methods are involved, referencing attribute A
on an object O
(that is, evaluating O.A
or getattr(O, "A")
) proceeds through the following steps:
- If
"A"
is defined inO
's instance dict (O.__dict__
), then the value on that dict is returned directly, precisely as it is. - Otherwise, each of the classes in
O
's method resolution order are checked in turn, looking for"A"
in each of their dicts. If found, call the valueD
. - If
D
, in turn, does not define a__get__
, then it is returned as it is. If it does, however, thenD
is referred to as a "descriptor", and its__get__
method is called withO
as the first argument, andtype(O)
as the second argument.
An attribute reference on a class works about the same, substituting the class being reference for the instance, with the following differences:
- Step 1 doesn't apply.
- The
__get__
method is called withNone
as the first argument, and the class being referenced as the second.
Python uses descriptors to implement such things as instance methods, class methods, static methods, and properties.
A super object created with super(T, O)
, then, is a (built-in) object with a __getattribute__
method which is called on every attribute reference on it, and looks up the attribute in the dicts of the only classes following T in O
's MRO. The value it then finds, it calls __get__
on as usual.
The process is a bit complex, so as an example, here's how it would work on your specific case. Since CarModel
is defined as it is, its MRO is [CarModel, object]
.
super().__new__(cls)
expands tosuper(CarModel, cls).__new__(cls)
, as described above.super(CarModel, cls)
is evaluated to yield a super objectS
.- Python fetches the attribute
"__new__"
onS
(the equivalent of callinggetattr(S, "__new__")
in Python code). - Since
S
was created on theCarModel
class, it considers the classes followingCarModel
in the MRO ofCarModel
, and finds"__new__"
in the dict of theobject
class itself. Its value, a static method, has a__get__
method, which is called with the argumentsNone
andcls
. Since__new__
is a static method, its__get__
method simply returns the function as it is, unmodified. Therefore,super(CarModel, cls).__new__
is precisely the same asobject.__new__
. - The function obtained in the last step (that is,
object.__new__
) is called with thecls
argument, wherecls
is probablyCarModel
, finally a new instance of theCarModel
class.
I hope that was at all understandable.
(For the sake of completeness, it should be mentioned that the actual __new__
function on the object
class is not actually a static method, but a special built-in function that simply has no __get__
method at all, but since the __get__
method on static methods just return the function they were defined with, the effect is the same.)
Solution 2:
super()
is used to reference the superclass (i.e. the parent class from which you are subclassing).
__new__
is a method that is invoked to create a new instance of the class, if it is defined.
Solution 3:
I believe, you are using Python3, where the super does not need to be provided with the same class name. Super refers to the Base classes of the current class and does the proper Method Resolution Order for you to invoke the method from the correct base class. __new__
is the method which is invoked to create an instance. It is the first step in the instance creation.
Solution 4:
I think part of this answer is confused about the relationship between __new__
and __init__
. I was confused about this myself. Here's how to answer these kind of questions.
Before we begin it is important to note that defining
__new__
always has an invisible/magic@classmethod
decorator applied to it.
from inspect import signature # find function signature
class JustInit(object):
def __init__(self, a):
self.a = a
print("JustInit")
print(signature(JustInit.__new__))
print(signature(JustInit.__init__))
JustInit
(*args, **kwargs)
(self, a, b)
Well, that's not helpful. Let's inspect those *args
and **kwargs
by printing them and passing them through with super
as suggested by @Dolda2000
class PrintNew(object):
def __new__(cls, *args, **kwargs):
print("new args:", args, "new kwargs:", kwargs)
return super().__new__(cls, *args, **kwargs)
def __init__(self, a):
self.a = a
print("PrintNew")
print(signature(PrintNew.__new__))
print(signature(PrintNew.__init__))
pnew = PrintNew(42)
print(pnew.a)
Results in:
PrintNew
(cls, *args, **kwargs)
(self, a)
new args: (42,) new kwargs: {}
Traceback (most recent call last):
File "/home/rett/notes/python/super_new.py", line 21, in <module>
pnew = PrintNew(42)
File "/home/rett/notes/python/super_new.py", line 12, in __new__
return super().__new__(cls, *args, **kwargs)
TypeError: object.__new__() takes exactly one argument (the type to instantiate)
Wait... I thought the signature of new was *args, **kwargs
... oh, the *args
must really be just the cls
?
While this is true, it is extremely important to point out that:
object
only takes thecls
variable (it doesn't really take*args/**kwargs
)- your implementation of
__new__
can take whatever values are passed to your__init__
and do whatever you want with them. - it seems like maybe
__new__
returns a non-instantiated class, which then gets__init__
called on it.
With these theories, let's try again by replacing return super().__new__(cls, *args, **kwargs)
with return super().__new__(cls)
It now behaves as expected:
PrintNew
(cls, *args, **kwargs)
(self, a)
new args: (42,) new kwargs: {}
42
This might give you some ideas on how you can do your implementation.
Post a Comment for "What Does `super()` Mean In `__new__`"