Note: Part of a flyweight implementation with Python
import weakref
class CarModel:
_models = weakref.WeakValueDictionary()
def __new__(cls, model_name, *args, **kwargs):
model = cls._models.get(model_name)
if not model:
model = super().__new__(cls)
cls._models[model_name] = model
return model
def __init__(self, model_name, air=False):
if not hasattr(self, "initted"):
self.model_name = model_name
self.air = air
self.initted=True
Question 1> what does super()
mean? Does it mean the parent class of CarModel
?
Question 2> I also have difficulties to understand the how the function __new__
works? Specifically, the following line.
model = super().__new__(cls)
Description for __new__
:
The constructor function is called
__new__
as opposed to__init__
, and accepts exactly one argument, the class that is being constructed (it is called before the object is constructed, so there is no self argument). It also has to return the newly created object.
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:
"A"
is defined in O
's instance dict (O.__dict__
), then the value on that dict is returned directly, precisely as it is.O
's method resolution order are checked in turn, looking for "A"
in each of their dicts. If found, call the value D
.D
, in turn, does not define a __get__
, then it is returned as it is. If it does, however, then D
is referred to as a "descriptor", and its __get__
method is called with O
as the first argument, and type(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:
__get__
method is called with None
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 to super(CarModel, cls).__new__(cls)
, as described above.super(CarModel, cls)
is evaluated to yield a super object S
."__new__"
on S
(the equivalent of calling getattr(S, "__new__")
in Python code).S
was created on the CarModel
class, it considers the classes following CarModel
in the MRO of CarModel
, and finds "__new__"
in the dict of the object
class itself. Its value, a static method, has a __get__
method, which is called with the arguments None
and cls
. 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 as object.__new__
.object.__new__
) is called with the cls
argument, where cls
is probably CarModel
, finally a new instance of the CarModel
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.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With