问题
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.
- It calls the
A.__new__
- in pseudocode we could writeinstance = 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:
Since Spam is an instance of MetaOne, calling
X = Spam()
would try to call the__call__
method of MetaOne class which is not there .Since MetaOne inherits from type it would call the
__call__
method of type class withSpam
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