问题
I found a weird behavior with cpython 2.5, 2.7, 3.2 and pypy with metaclass that override __new__
when using the python 2 / python 3 compatible way of using metaclass :
Given a module m1:
class C1Meta(type):
def __new__(cls, name, bases, dct):
return type.__new__(cls, name, bases, dct)
C1 = C1Meta('C1', (object,), {})
class C2Meta(type):
pass
C2 = C2Meta('C2', (object,), {})
And the following main program:
import m1
C10 = m1.C1Meta('C10', (m1.C1,), {})
class C11Meta(m1.C1Meta):
pass
C11 = C11Meta('C11', (m1.C1,), {})
class C12Meta(m1.C1Meta):
def __new__(cls, name, bases, dct):
return m1.C1Meta.__new__(cls, name, bases, dct)
C12 = C12Meta('C12', (m1.C1,), {})
class C13Meta(m1.C1Meta):
def __new__(cls, name, bases, dct):
return type.__new__(cls, name, bases, dct)
C13 = C13Meta('C13', (m1.C1,), {})
C20 = m1.C2Meta('C20', (m1.C2,), {})
class C21Meta(m1.C2Meta):
pass
C21 = C21Meta('C21', (m1.C2,), {})
class C22Meta(m1.C2Meta):
def __new__(cls, name, bases, dct):
return m1.C2Meta.__new__(cls, name, bases, dct)
C22 = C22Meta('C22', (m1.C2,), {})
class C23Meta(m1.C2Meta):
def __new__(cls, name, bases, dct):
return type.__new__(cls, name, bases, dct)
C23 = C23Meta('C23', (m1.C2,), {})
print(C10)
print(C11)
print(C12)
print(C13)
print(C20)
print(C21)
print(C22)
print(C23)
Running the script will produce the following output (with all the mentioned python versions) :
<class 'm1.C10'>
<class 'm1.C11'>
<class 'm1.C12'>
<class '__main__.C13'>
<class '__main__.C20'>
<class '__main__.C21'>
<class '__main__.C22'>
<class '__main__.C23'>
-> the C10, C11 and C12 classes module is wrong !
Is it an expected behavior ?
Is there a way to override new that will not raise the issue ?
Thanks,
Christophe
回答1:
Apparently the __module__
attribute is set when the metaclass executes. In your case, the metaclass executes inside m1
. With a normally defined class, the __module__
attribute is automatically generated and passed to the metaclass. However, you are creating your classes with a manual call to the metaclass, and passing in an empty attribute dictionary. Thus your classes do not provide a __module__
attribute. The __module__
attribute is created automatically, but not until type.__new__
is called. Since this happens inside module m1
, the __module__
created at that time refers to m1
.
I'm not exactly sure why you're calling the metaclass explicitly. The whole point of metaclasses is to allow you to customize what happens when you use a class
statement to define a class. If you don't want to use a class
statement, you can just use regular functions to create your classes, and don't need to bother with a metaclass at all.
That said, I believe you can get your desired behavior, by doing, e.g.,
C11 = C11Meta('C11', (m1.C1,), {'__module__': __name__})
来源:https://stackoverflow.com/questions/11209521/base-metaclass-overriding-new-generates-classes-with-a-wrong-module