59

我想问一下这个with_metaclass()调用在类的定义中是什么意思。

例如:

class Foo(with_metaclass(Cls1, Cls2)):
  • 这是一个类从元类继承的特例吗?
  • 新类也是元类吗?
4

2 回答 2

87

with_metaclass()six提供的实用类工厂函数,可以更轻松地为 Python 2 和 3 开发代码。

它对临时元类使用了一些技巧(见下文),以与 Python 2 和 Python 3 交叉兼容的方式将元类附加到常规类。

从文档中引用:

使用基类基类和元类元类创建一个新类。这被设计用于像这样的类声明:

from six import with_metaclass
   
class Meta(type):
    pass

class Base(object):
    pass

class MyClass(with_metaclass(Meta, Base)):
    pass

这是必需的,因为附加元类的语法在 Python 2 和 3 之间发生了变化:

蟒蛇2:

class MyClass(object):
    __metaclass__ = Meta

蟒蛇 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)来创建新的派生类。

结果, using为您提供了一个没有其他基类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'>
于 2013-08-29T14:34:19.370 回答
28

更新:该six.with_metaclass()功能已经被修补了一个装饰器变体,即@six.add_metaclass(). 此更新修复了一些与基础对象相关的 mro 问题。新的装饰器将按如下方式应用:

import six

@six.add_metaclass(Meta)
class MyClass(Base):
    pass

这是补丁说明这里是使用装饰器替代方案的类似的详细示例和说明

于 2016-05-26T01:27:02.000 回答