I'm really confused by the following code sample:
class Meta_1(type):
def __call__(cls, *a, **kw): # line 1
print("entering Meta_1.__call__()")
print(cls) # line 4
print(cls.mro()) # line 5
print(super(Meta_1, cls).__self__) # line 6
rv = super(Meta_1, cls).__call__(*a, **kw) # line 7
print("exiting Meta_1.__call__()")
return rv
class Car(object, metaclass=Meta_1):
def __new__(cls, *a, **kw):
print("Car.__new__()")
rv = super(Car, cls).__new__(cls, *a, **kw)
return rv
def __init__(self, *a, **kw):
print("Car.__init__()")
super(Car,self).__init__(*a, **kw)
if __name__ == '__main__':
c = Car()
The print message for this code is:
entering Meta_1.__call__()
<class '__main__.Car'> # line 4
[<class '__main__.Car'>, <class 'object'>] # line 5
<class '__main__.Car'> # line 6
Car.__new__()
Car.__init__()
exiting Meta_1.__call__()
The result shows that cls
of line 4 is the Car
class and its MRO list is:
[<class '__main__.Car'>, <class 'object'>]
However, line 6 shows that super(Meta_1, cls).__self__
is also the Car
class.
I am really confused that:
- In line 7, It seems that
super(Meta_1, cls).__call__(*a, **kw)
eventually lead totype.__call__
. But, to my knowledge,super(arg1, arg2)
will look into the MRO of the second input argument to find the first input argument, and return the next class to it. But in line 6 and 7 of my code, the MRO for 2nd argument(Car
), does not contain the 1st input argument(Meta_1
), you cannot findMeta_1
in the MRO forCar
. so why wouldsuper(Meta_1, cos)
take us to invoketype.__call__
??
2. if super(Meta_1, cls).__self__
is the Car
class, then line 7 means it's Car
's __call__
that's being called? But calling the Car
class took us to line 1 in the first place, right? wouldn't that be a loop?
__call__
is an instance method ofMeta_1
, andCar
is an instance ofMeta_1
. That meansCar()
is really short forMeta_1.__call__(Car)
. – ObstreperousMeta_1
is not in the MRO ofcls
for the same reason thattype
is not in the MRO of an "ordinary" class. – ObstreperousMeta_1
as the first argument insuper()
? – DraytonMeta_1.__call__
. It's the same reason you usesuper(Car, self).__init__
inCar.__init__
. – ObstreperousMeta_1.__call__
is line 1, right? how does that relates tosuper()
function? – Draytontype.__call__
(which you are overriding inMeta_1
) is to ensure that the class's__new__
method gets called, so that you can writeCar()
instead ofCar.__new__()
. Usingsuper().__call__
ensures thattype.__call__
(eventually) gets called rather than you having to invokecls.__new__
yourself. – Obstreperoussuper(Meta_1, cls).__call__(*a, **kw)
in line 7 basically invokestype.__call__
, which then executesCar
's__new__
, is that accurate? This makes very good sense. I just don't see howsuper(Meta_1, cls)
leads us totype
. – Draytontype
is in the MRO ofMeta_1
. – Obstreperouscos
(which isCar
), and it does not containMeta_1
, right? – Draytonsuper()
will look into the MRO of the second input argument to find the first input argument, and return the next class to it. But in my case, the MRO for 2nd argument, a.k.aCar
, does not contain the 1st input argument, a.k.a.Meta_1
, you cannot findMeta_1
in the MRO forCar
. so why wouldsuper(Meta_1, cls)
take us totype.__call__
?? – Draytonsuper(Meta_1, cls)
does not usecls.mro()
.Meta_1
is not in the MRO forCar
, becauseCar
does not inherit fromMeta_1
. – Obstreperousmro
method returns the MRO that is used for instances of the class. The wording is tricky, butcls.mro()
returns the list of classes that get checked for instances ofcls
. – Obstreperoussuper(Meta_1, cls)
use?Meta_1
's MRO? – Draytonsuper(A, B)
would useB
's MRO, but in my case in line 6 and 7,super(A, B)
actually usesA
's MRO. We don't know why. The logic behind it remains a mystery. oh that's frustrating. – DraytonB
doesn't have an MRO if it's not a type;type(B)
does. There is no mystery here, only possibly confusion becausecls
(like any object) has a type, but also is a type. You need to be clear about what rolecls
is playing before you can think about which MRO is applicable. Whencls
is the second argument, you use the MRO determined by its type; when it is the first argument, you use the MRO determined by the type of the second argument (which is, very often, the first argument). – Obstreperous