Reputation: 1205
In a file named exp.py (below), I am trying to understand metaclasses in Python. It seems like when the metaclass's __new__
method uses the 'type' constructor to construct a class, its __new__
method does not get called by a subclass of a class using it as the metaclass:
class A(type):
def __new__(cls, name, bases, dct):
print "cls is: ", cls
print "name is: ", name
print "bases is: ", bases
print "dct is: ", dct
print
return super(A, cls).__new__(cls, name, bases, dct)
class B(object):
__metaclass__ = A
class C(B): pass
class D(type):
def __new__(cls, name, bases, dct):
print "cls is: ", cls
print "name is: ", name
print "bases is: ", bases
print "dct is: ", dct
return type(name, bases, dct)
class E(object):
__metaclass__ = D
class F(E): pass
In the terminal:
>>> from exp import *
cls is: <class 'exp.A'>
name is: B
bases is: (<type 'object'>,)
dct is: {'count': 0, '__module__': 'exp', '__metaclass__': <class 'exp.A'>, '__init__': <function __init__ at 0x107eb9578>}
cls is: <class 'exp.A'>
name is: C
bases is: (<class 'exp.B'>,)
dct is: {'__module__': 'exp'}
cls is: <class 'exp.D'>
name is: E
bases is: (<type 'object'>,)
dct is: {'count': 0, '__module__': 'exp', '__metaclass__': <class 'exp.D'>, '__init__': <function __init__ at 0x107ebdb18>}
>>>
As you can see, the __new__
method of the metaclass D does not get called when class F's definition is loaded. (If it had been called, then information about the class F would have been printed too.)
Could someone help me explain this?
Related post: I was reading What is a metaclass in Python?, and seem to encounter something similar in the sentence, "Be careful here that the __metaclass__
attribute will not be inherited, the metaclass of the parent (Bar.__class__
) will be. If Bar used a __metaclass__
attribute that created Bar with type()
(and not type.__new__()
), the subclasses will not inherit that behavior." But I did not fully understand this.
Upvotes: 3
Views: 1278
Reputation: 250951
In the second case instead of returning an instance of the metaclass you're actually returning an instance of type
, this in turn sets the __class__
attribute of the newly created class E
as <type 'type'>
instead of D
. Hence as per the rule 2 Python checks for base class's __class__
attribute(or type(E)
or E.__class__
) and decides to use type
as F
's metaclass, so D
's __new__
is never called in this case.
dict['__metaclass__']
exists, it is used.__class__
attribute first and if not found, uses its type).So, the proper way is to call type's __new__
method in metaclass's __new__
method:
class D(type):
def __new__(cls, name, bases, dct):
print "cls is: ", cls
print "name is: ", name
print "bases is: ", bases
print "dct is: ", dct
return type.__new__(cls, name, bases, dct)
class E(object):
__metaclass__ = D
class F(E): pass
class A(object):
pass
Output:
cls is: <class '__main__.D'>
name is: E
bases is: (<type 'object'>,)
dct is: {'__module__': '__main__', '__metaclass__': <class '__main__.D'>}
cls is: <class '__main__.D'>
name is: F
bases is: (<class '__main__.E'>,)
dct is: {'__module__': '__main__'}
Upvotes: 4