Understanding metaclasses in Python

可紊 提交于 2019-12-12 04:21:18

问题


I am trying to get my head around metaclass but i still can't really get the concept of it.

For all i know:

Any class is itself an instance of type "type" - therefore "calling" a class just calls the method __call__ on its class - which happens to be type's __call__. The effect of type.__call__ is exactly: on code like:

class A: pass b = A()

The sequence of steps i know here is:

1.type.__call__ receives the class A itself as its first parameter.

  1. It calls the A.__new__ - in pseudocode we could write instance = A.__new__(cls) as what runs.

3.That returns an instance of the "A" class

4.Then it calls __init__ on the instance(instance.__init__()) ...and returns that instance return instance

But now consider the below code:

class MetaOne(type):
    def __new__(meta, classname, supers, classdict):
        print('In MetaOne.new:', meta, classname, supers, classdict, sep='\n...')
        return type.__new__(meta, classname, supers, classdict)

class Eggs:
    pass

print('making class')

class Spam(Eggs, metaclass=MetaOne): 
    data = 1 
    def meth(self, arg): 
        return self.data + arg

print('making instance')
X = Spam()
print('data:', X.data, X.meth(2))

The output from this script is as follows:

making class
In MetaOne.new:
...<class '__main__.MetaOne'>
...Spam
...(<class '__main__.Eggs'>,)
...{'__qualname__': 'Spam', '__module__': '__main__', 'meth': <function Spam.met
h at 0x00000000010C1D08>, 'data': 1}
making instance
data: 1 3

So as per my understanding this is the sequence of steps:

  1. Since Spam is an instance of MetaOne, calling X = Spam() would try to call the __call__ method of MetaOne class which is not there .

  2. Since MetaOne inherits from type it would call the __call__ method of type class with Spam as the first argument.

After that the call lands up in the __new__ method of MetaOne class but it should contain Spam as the first param.

From where does meta argument of MetaOne class come into picture.

Please help me in my understanding.


回答1:


Since Spam is an instance of MetaOne, calling X = Spam() would try to call the __call__ method of MetaOne class which is not there .

That is the soruce of your confusion - the __call__ (or __new__ and __init__) of the metaclass is not called when you create ordinary instances of the class.

Also, since there is no __call__ method for MetaOne, the usual inheritance rules apply: the __call__ method on MetaOne's superclass is used (and it is type.__call__)

The metaclass' __new__ and __init__ methods are invoked when the class body itself is executed (as you can see in your example, the "print" in the metaclass' __new__ shows up before the "making instance" text).

When creating an instance of Span itself, the metaclass methods __new__ and __init__ are not called - the metaclass __call__ is called - and it is that that executes the class's(Span's) __new__ and __init__. In other words: the metaclass __call__ is responsible for calling the "ordinary" class's __new__ and __init__.

Since MetaOne inherits from type it would call the call method of type class with Spam as the first argument.

And it does, but you made no print statements to "view" this happening:

class MyMeta(type):
    def __new__(metacls, name, bases, namespace):
        print("At meta __new__")
        return super().__new__(metacls, name, bases, namespace)   
    def __call__(cls, *args, **kwd):
        print ("at meta __call__")
        return super().__call__(*args, **kwd)

def Egg(metaclass=MyMeta):
    def __new__(cls):
        print("at class __new__")

If I paste this at the ineractive console, at this point it prints:

At meta __new__

Then, going on the interactive session:

In [4]: fried = Egg()
at meta __call__
at class __new__

And the extra mind-twisting thing is that: "type is type's own metaclass": meaning that type's __call__ is also responsible for running the __new__ and __init__ methods on the metaclass itself, when a new (non-meta) class body is executed.



来源:https://stackoverflow.com/questions/44196281/understanding-metaclasses-in-python

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!