我想问在类的定义中,
例如:
with_metaclass()
调用的含义是什么。例如:
class Foo(with_metaclass(Cls1, Cls2)):
- 一个类从元类继承是一种特殊情况吗?
- 新的类也是元类吗?
with_metaclass()
调用的含义是什么。class Foo(with_metaclass(Cls1, Cls2)):
with_metaclass()
是由six
库提供的一个实用的类工厂函数,可使Python 2和3代码的开发更加容易。
它使用了一些巧妙的手法(见下文),通过一个临时元类将元类附加到常规类上,以一种与Python 2和Python 3都兼容的方式。
引用文档:
创建一个新类,其基类为base,元类为metaclass。这是设计用于类声明的,如下所示:
from six import with_metaclass class Meta(type): pass class Base(object): pass class MyClass(with_metaclass(Meta, Base)): pass
这是必需的,因为Python 2和3之间附加元类的语法已更改:
Python 2:
class MyClass(object): __metaclass__ = Meta
Python 3:
class MyClass(metaclass=Meta): pass
with_metaclass()
函数利用元类可以 a) 被子类继承,b) 可以用于生成新类,c) 当您从具有元类的基类派生时,创建实际的子类对象被委托给元类。它有效地创建一个新的临时基类,带有一个临时的metaclass
元类,当用于创建子类时,交换 临时基类和元类组合与您选择的元类。
def with_metaclass(meta, *bases): """Create a base class with a metaclass.""" # This requires a bit of explanation: the basic idea is to make a dummy # metaclass for one level of class instantiation that replaces itself with # the actual metaclass. class metaclass(type): def __new__(cls, name, this_bases, d): return meta(name, bases, d) @classmethod def __prepare__(cls, name, this_bases): return meta.__prepare__(name, bases) return type.__new__(metaclass, 'temporary_class', (), {})
分解上面的内容:
type.__new__(metaclass, 'temporary_class', (), {})
使用metaclass
元类创建一个名为temporary_class
的新类对象,除此之外该类对象为空。使用type.__new__(metaclass,...)
而不是metaclass(...)
来避免使用特殊的metaclass.__new__()
实现,这种实现在下一步的操作中需要巧妙地运用。- 仅适用于Python 3,当
temporary_class
作为基类时,Python首先调用metaclass.__prepare__()
(将派生类名称(temporary_class,)
作为this_bases
参数传入)。然后使用既定的元类meta
调用meta.__prepare__()
,忽略this_bases
并传入bases
参数。- 接下来,在使用
metaclass.__prepare__()
的返回值作为类属性的基本命名空间之后(或在Python 2上只是使用普通字典),Python调用metaclass.__new__()
来创建实际的类。再次将(temporary_class,)
作为this_bases
元组传递给它,但上面的代码会忽略它并使用bases
代替,调用meta(name, bases, d)
来创建新的派生类。因此,使用
with_metaclass()
会给你一个新的类对象,没有额外的基类:
>>> class FooMeta(type): pass ... >>> with_metaclass(FooMeta) # returns a temporary_class object <class '__main__.temporary_class'> >>> type(with_metaclass(FooMeta)) # which has a custom metaclass <class '__main__.metaclass'> >>> class Foo(with_metaclass(FooMeta)): pass ... >>> Foo.__mro__ # no extra base classes (<class '__main__.Foo'>, <type 'object'>) >>> type(Foo) # correct metaclass <class '__main__.FooMeta'>
更新: six.with_metaclass()
函数现已通过修补程序添加装饰器版本,即 @six.add_metaclass()
。此更新解决了与基础对象相关的一些 mro 问题。新的装饰器应按如下方式应用:
import six
@six.add_metaclass(Meta)
class MyClass(Base):
pass
这里是修补程序注释,并且这里提供了一个类似的、详细的示例和解释,用于使用装饰器替代。
isinstance
没有问题,而其他所有 Py2/3 兼容的元类方式都有问题。 - Jérémie
class MyClass(with_metaclass(Meta, object)): pass
(其中object是可选的)。 - Andy HaydenBase
类,那么在声明MyClass
后,只需执行MyClass = with_metaclass(Meta, MyClass)
,对吗?我认为这与下面的add_metaclass
解决方案具有相同的注意事项(双重类创建)。 - DylanYoungwith_metaclass()
在我写这篇答案大约一年后进行了更新,以避免在类继承层次结构中有一个额外的类。仍然会创建一个临时类,但只是为了拦截类工厂过程。 - Martijn Pieters