how does metaclass work with the mro list when super() is called?

  • Last Update :
  • Techknowledgy :

In the class

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

The MRO of Meta_1 itself can be seen with

>>> Meta_1.mro(Meta_1)
[<class '__main__.Meta_1'>, <class 'type'>, <class 'object'>]

Suggestion : 2

(mro is an instance method of the type anycodings_metaclass class, and so requires the seemingly anycodings_metaclass redundant instance of type as an anycodings_metaclass argument. Keep in mind that cls.mro() is anycodings_metaclass equivalent to type(cls).mro(cls).),An MRO is always defined by a class; anycodings_metaclass when performing method resolution on an anycodings_metaclass instance, we use the MRO of the class of anycodings_metaclass which that instance is a type.,The result shows that cls of line 4 is the anycodings_super Car class and its MRO list is: [<class anycodings_super '__main__.Car'>, <class 'object'>] ,So line 7 is a call to type.__call__, in anycodings_metaclass order to create an instance of cls that anycodings_metaclass Meta_1.__call__ can return.

I'm really confused by the following code anycodings_super 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__()

In the class

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

The MRO of Meta_1 itself can be seen anycodings_metaclass with

>>> Meta_1.mro(Meta_1)
[<class '__main__.Meta_1'>, <class 'type'>, <class 'object'>]

Suggestion : 3

Typical implementations create a new instance of the class by invoking the superclass’s __new__() method using super().__new__(cls[, ...]) with appropriate arguments and then modifying the newly created instance as necessary before returning it.,When using the default metaclass type, or any metaclass that ultimately calls type.__new__, the following additional customization steps are invoked after creating the class object:,In addition to bypassing any instance attributes in the interest of correctness, implicit special method lookup generally also bypasses the __getattribute__() method even of the object’s metaclass:,If a is an instance of super, then the binding super(B, obj).m() searches obj.__class__.__mro__ for the base class A immediately following B and then invokes the descriptor with the call: A.__dict__['m'].__get__(obj, obj.__class__).

def __hash__(self):
   return hash((self.name, self.nick, self.color))
import sys
from types
import ModuleType

class VerboseModule(ModuleType):
   def __repr__(self):
   return f 'Verbose {self.__name__}'

def __setattr__(self, attr, value):
   print(f 'Setting {attr}...')
super().__setattr__(attr, value)

sys.modules[__name__].__class__ = VerboseModule
class Philosopher:
   def __init_subclass__(cls, /, default_name, **kwargs):
      super().__init_subclass__( ** kwargs) cls.default_name = default_name

      class AustralianPhilosopher(Philosopher, default_name = "Bruce"):
      pass
class A:
   x = C() # Automatically calls: x.__set_name__(A, 'x')
class A:
   pass

c = C()
A.x = c # The hook is not called
c.__set_name__(A, 'x') # Manually invoke the hook
class Meta(type):
   pass

class MyClass(metaclass = Meta):
   pass

class MySubclass(MyClass):
   pass

Suggestion : 4

Let's start with the juiciest bit: you can subtype built-in types like dictionaries and lists. All you need is a name for a base class that is a built-in type and you're in business. ,You cannot use slots with "variable-length" built-in types as base class. Variable-length built-in types are long, str and tuple. ,We can also use the new type in contexts where classic only allows "real" dictionaries, such as the locals/globals dictionaries for the exec statement or the built-in function eval(): ,When using multiple inheritance, you can mix classic classes and built-in types (or types derived from built-in types) in the list of base classes. (This is new in Python 2.2b2; in earlier versions you couldn't.)

    class A:
       def foo(self): pass

    class B(A): pass

    class C(A):
       def foo(self):
       B.foo(self)

Suggestion : 5

In order to understand super(), we first need to understand MRO (method resolution order) in Python. MRO means how to find a method for an object along the class inheritance hierarchy.,To check a class’s MRO, we can use MyClass.__mro__. It will print out a linear list of class for method resolution.,If we use super(), it will try to find the next class in the MRO list and try to call the corresponding method in that class.,This post provides another similar example to illustrate the idea of super(). So in order to run the initialization method the parent classes correctly and not rely on some chance, we can use super() to the rescue.

class MyClass(Base):
   def __init__(self):
   super().__init__()
class PrettyType(type):
   ""
"make the repr of the classes look nice when finally listed"
""
def __repr__(self):
   return self.__name__

# subclasses of O will also have the metaclass:
   class O(metaclass = PrettyType): 'O, object'

class H(O): 'H, O, object'
# H 's parent is O

class G(H): 'G, H, O, object'
# G 's linearization is itself followed by its parent'
s linearization.

class I(G): 'I, G, H, O, object'
# I 's linearization is I followed by G'
s

class F(H): 'F, H, O, object'

class E(H): 'E, H, O, object'

class D(F): 'D, F, H, O, object'

class C(E, F, G): 'C, E, F, G, H, O, object'
# C 's linearization is C followed by a consistent linearization of
# its parents, left to right.
# First C, then E - then you might be tempted to put H after E,
   # but H must come after F and G(see class F and G)
# so we
try F 's linearization, noting that H comes after G,
# so we
try G 's linearization, H then consistently comes next, then object

class B(O): 'B, O, object'

class A(B, C, D): 'A, B, C, E, D, F, G, H, O, object'
L(O) = [O]

L(H) = [H] + L(O) = [H, O]

L(B) = [B, O]

L(E) = [E] + L[H] = [E, H, O]

L(F) = [F, H, O]

L(G) = [G, H, O]

L(C) = [C] + merge(L(E), L(F), L(G), [E, F, G]) = [C] + merge([E, H, O], [F, H, O], [G, H, O], [E, F, G]) # choose E = [C, E] + merge([H, O], [F, H, O], [G, H, O], [F, G]) # can not choose, choose F = [C, E, F] + merge([H, O], [H, O], [G, H, O], [G]) # can not choose H, choose G = [C, E, F, G] + merge([H, O], [H, O], [H, O]) # choose H = [C, E, F, G, H] + merge([O], [O], [O]) = [C, E, F, G, H, O]

L(D) = [D] + L(F) = [D, F, H, O]

L(A) = [A] + merge(L(B), L(C), L(D), [B, C, D]) = [A] + merge([B, O], [C, E, F, G, H, O], [D, F, H, O], [B, C, D]) # choose B = [A, B] + merge([O], [C, E, F, G, H, O], [D, F, H, O], [C, D]) # can not choose O, choose C = [A, B, C] + merge([O], [E, F, G, H, O], [D, F, H, O], [D]) # can not choose O, choose E(it is the first viable node after O) = [A, B, C, E] + merge([O], [F, G, H, O], [D, F, H, O], [D]) # can not choose O and F, choose D = [A, B, C, E, D] + merge([O], [F, G, H, O], [F, H, O]) # can not choose O, choose F = [A, B, C, E, D, F] + merge([O], [G, H, O], [H, O]) # can not choose O, choose G = [A, B, C, E, D, F, G] + merge([O], [H, O], [H, O]) # can not choose O, choose H = [A, B, C, E, D, F, G, H] + merge([O], [O], [O]) = [A, B, C, E, D, F, G, H, O]
class Base:
   def __init__(self):
   print("class Base init called")

class B(Base):
   def __init__(self):
   print("class B init called")
Base.__init__(self)

class C(Base):
   def __init__(self):
   print("class C init called")
super().__init__()

# class D(C, B):
   class D(B, C):
   def __init__(self):
   print("class D init called")
super().__init__()

def main():
   d = D()

if __name__ == "__main__":
   main()
Base.__init__(self)
super().__init__()

Suggestion : 6

In v3, while the v2 syntax is still OK (and so the preceding snippet runs fine), super’s semantics have been strengthened so you can replace each of the calls to it with just super(), without arguments.,This idiom uses generator-expression (genexp) syntax and assumes that your class’s __init__ method can be called with an iterable argument to create a suitable new instance of the class.,When __init__ is absent (and is not inherited from any base), you must call the class without arguments, and the new instance has no instance-specific attributes.,And here’s how to implement it with a callable instance (an instance whose class supplies the special method __call__):

The class statement is the most common way to create a class object. class is a single-clause compound statement with the following syntax:

class classname(base - classes):
   statement(s)

You normally specify an attribute of a class object by binding a value to an identifier within the class body. For example:

class C1(object):
   x = 23
print(C1.x) # prints: 23

You can also bind or unbind class attributes outside the class body. For example:

class C2(object): pass
C2.x = 23
print(C2.x) # prints: 23

The class statement implicitly sets some class attributes. Attribute __name__ is the classname identifier string used in the class statement. Attribute __bases__ is the tuple of class objects given as the base classes in the class statement. For example, using the class C1 we just created:

print(C1.__name__, C1.__bases__)
# prints: C1 (<type 'object'>,)

However, in statements in methods defined in a class body, references to attributes of the class must use a fully qualified name, not a simple name. For example:

class C4(object):
   x = 23
def amethod(self):
   print(C4.x) # must use C4.x or self.x, not just x!

Here’s an example of a class that includes a method definition:

class C5(object):
   def hello(self):
   print('Hello')

A descriptor is any object whose class supplies a special method named __get__. Descriptors that are class attributes control the semantics of accessing and setting attributes on instances of that class. Roughly speaking, when you access an instance attribute, Python gets the attribute’s value by calling __get__ on the corresponding descriptor, if any. For example:

class Const(object): # an overriding descriptor, see later
def __init__(self, value):
   self.value = value
def __set__(self, * _): # ignore any attempt at setting
pass
def __get__(self, * _): # always
return the constant value
return self.value

class X(object):
   c = Const(23)

x = X()
print(x.c) # prints: 23
x.c = 42
print(x.c) # prints: 23

To create an instance of a class, call the class object as if it were a function. Each call returns a new instance whose type is that class:

an_instance = C5()

When a class defines or inherits a method named __init__, calling the class object implicitly executes __init__ on the new instance to perform any needed per-instance initialization. Arguments passed in the call must correspond to the parameters of __init__, except for parameter self. For example, consider:

class C6(object):
   def __init__(self, n):
   self.x = n

Here’s how you can create an instance of the C6 class:

another_instance = C6(42)

You can give an instance object an arbitrary attribute by binding a value to an attribute reference. For example:

class C7: pass
z = C7()
z.x = 23
print(z.x) # prints: 23

Creating an instance implicitly sets two instance attributes. For any instance z, z.__class__ is the class object to which z belongs, and z.__dict__ is the mapping that z uses to hold its other attributes. For example, for the instance z we just created:

print(z.__class__.__name__, z.__dict__) # prints: C7 {
   'x': 23
}

When a class defines or inherits a method named __init__, calling the class object implicitly executes __init__ on the new instance to perform any needed per-instance initialization. Arguments passed in the call must correspond to the parameters of __init__, except for parameter self. For example, consider:

class C6(object):
   def __init__(self, n):
   self.x = n

Here’s how you can create an instance of the C6 class:

another_instance = C6(42)

Suggestion : 7

In this tutorial, we will learn about method resolution order, which is also known as the MRO. It is an essential concept of Python inheritance.,As we can see in the above output, we get the order of the method resolution order. In such a way, C3 linearization algorithm work for multiple inheritance.,Method resolution order describes the search path of the class which Python uses to get the appropriate method in classes that contain the multi-inheritance.,Python creates a list of classes while implementing the multiple inheritance between the classes. That list is used to determine which method has to be called one is invoked by instances.

 I am a class C
I am a class B
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
               [<class '__main__.C'>, <class '__main__.A'>, <class 'object'>]