我正在写一个元类,不小心这样做了:
class MetaCls(type):
def __new__(cls, name, bases, dict):
return type(name, bases, dict)
......而不是像这样:
class MetaCls(type):
def __new__(cls, name, bases, dict):
return type.__new__(cls, name, bases, dict)
这两个元类之间究竟有什么区别?更具体地说,导致第一个不能正常工作的原因(某些类没有被元类调用)?
答案 0 :(得分:7)
在第一个示例中,您将创建一个全新的类:
>>> class MetaA(type):
... def __new__(cls, name, bases, dct):
... print 'MetaA.__new__'
... return type(name, bases, dct)
... def __init__(cls, name, bases, dct):
... print 'MetaA.__init__'
...
>>> class A(object):
... __metaclass__ = MetaA
...
MetaA.__new__
>>>
而在第二种情况下,您正在呼叫父母的__new__
:
>>> class MetaA(type):
... def __new__(cls, name, bases, dct):
... print 'MetaA.__new__'
... return type.__new__(cls, name, bases, dct)
... def __init__(cls, name, bases, dct):
... print 'MetaA.__init__'
...
>>> class A(object):
... __metaclass__ = MetaA
...
MetaA.__new__
MetaA.__init__
>>>
答案 1 :(得分:7)
您需要弄清楚的第一件事是object.__new__()
如何运作。
object.__new__(cls[, ...])
调用以创建类
cls
的新实例。__new__()
是静态的 需要的方法(特殊的,所以你不需要声明它) 请求实例作为其第一个参数的类。 其余参数是传递给对象构造函数的参数 表达式(对类的调用)。__new__()
的返回值 应该是新的对象实例(通常是cls
的实例)。典型实现通过调用创建类的新实例 超类的
__new__()
方法使用super(currentclass, cls).__new__(cls[, ...])
和适当的参数然后修改 在返回之前必要时新创建的实例。如果
__new__()
返回cls
的实例,则会调用新实例的__init__()
方法__init__(self[, ...])
,其中self
是新实例和其余参数是相同的 传递给__new__()
。如果
__new__()
未返回cls
的实例,则不会调用新实例的__init__()
方法。
__new__()
主要用于允许不可变类型的子类(如int
,str
或tuple
)来自定义实例创建。也是 通常在自定义元类中重写以自定义类 创建
因此,在mg。answer中,前者不会调用函数__init__
,而后者在调用__init__
后调用函数__new__
。
答案 2 :(得分:4)
请参阅下面的注释,希望这有用。
class MetaCls(type):
def __new__(cls, name, bases, dict):
# return a new type named "name",this type has nothing
# to do with MetaCls,and MetaCl.__init__ won't be invoked
return type(name, bases, dict)
class MetaCls(type):
def __new__(cls, name, bases, dict):
# return a new type named "name",the returned type
# is an instance of cls,and cls here is "MetaCls", so
# the next step can invoke MetaCls.__init__
return type.__new__(cls, name, bases, dict)
答案 3 :(得分:3)
return type(name, bases, dict)
您从中获得的是新的type
,而不是MetaCls
实例。因此,无法调用MetaCls
(包括__init__
)中定义的方法。
type.__new__
将作为创建新类型的一部分进行调用,是的,但进入该函数的cls
的值将是type
而不是MetaCls
答案 4 :(得分:3)
class MyMeta(type):
def __new__(meta, cls, bases, attributes):
print 'MyMeta.__new__'
return type.__new__(meta, cls, bases, attributes)
def __init__(clsobj, cls, bases, attributes):
print 'MyMeta.__init__'
class MyClass(object):
__metaclass__ = MyMeta
foo = 'bar'
实现相同结果的另一种方法:
cls = "MyClass"
bases = ()
attributes = {'foo': 'bar'}
MyClass = MyMeta(cls, bases, attributes)
MyMeta
是可调用的,因此Python将使用特殊方法__call__
。
Python将在__call__
的类型中查找MyMeta
(在我们的案例中为type
)
“对于新式类,特殊方法的隐式调用是 只保证在对象的类型上定义时才能正常工作,而不是 在对象的实例字典“
中
MyClass = MyMeta(...)
被解释为:
my_meta_type = type(MyMeta)
MyClass = my_meta_type.__call__(MyMeta, cls, bases, attributes)
在type.__call__()
内,我想象这样的事情:
MyClass = MyMeta.__new__(MyMeta, cls, bases, attributes)
meta_class = MyClass.__metaclass__
meta_class.__init__(MyClass, cls, bases, attributes)
return MyClass
MyMeta.__new__()
将决定MyClass
的构建方式:
type.__new__(meta, cls, bases, attributes)
将为MyMeta
MyClass
)
type(cls, bases, attributes)
将为MyClass
设置默认元类(类型)
答案 5 :(得分:1)
所有这些都很好描述here。
如果没有返回正确类型的对象,则无需定义自定义元类。