Python 元类:理解“with_metaclass()”

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18513821/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 10:55:31  来源:igfitidea点击:

Python Metaclass : Understanding the 'with_metaclass()'

pythonmetaclasssix-python

提问by Zakos

I want to ask what the with_metaclass()call means in the definition of a class.

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

E.g.:

例如:

class Foo(with_metaclass(Cls1, Cls2)):
  • Is it a special case where a class inherits from a metaclass?
  • Is the new class a metaclass, too?
  • 类继承自元类是一种特殊情况吗?
  • 新类也是元类吗?

采纳答案by Martijn Pieters

with_metaclass()is a utility class factory function provided by the sixlibraryto make it easier to develop code for both Python 2 and 3.

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

It uses a little slight of hand (see below) with a temporary metaclass, to attach a metaclass to a regular class in a way that's cross-compatible with both Python 2 and Python 3.

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

Quoting from the documentation:

从文档中引用:

Create a new class with base class base and metaclass metaclass. This is designed to be used in class declarations like this:

from six import with_metaclass

class Meta(type):
    pass

class Base(object):
    pass

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

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

from six import with_metaclass

class Meta(type):
    pass

class Base(object):
    pass

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

This is needed because the syntax to attach a metaclass changed between Python 2 and 3:

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

Python 2:

蟒蛇2:

class MyClass(object):
    __metaclass__ = Meta

Python 3:

蟒蛇3:

class MyClass(metaclass=Meta):
    pass

The with_metaclass()function makes use of the fact that metaclasses are a) inherited by subclasses, and b) a metaclass can be used to generate new classes and c) when you subclass from a base class with a metaclass, creating the actual subclass object is delegated to the metaclass. It effectively creates a new, temporary base class with a temporary metaclassmetaclass that, when used to create the subclass swaps outthe temporary base class and metaclass combo with the metaclass of your choice:

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', (), {})

Breaking the above down:

分解上述内容:

  • type.__new__(metaclass, 'temporary_class', (), {})uses the metaclassmetaclass to create a new class object named temporary_classthat is entirely empty otherwise. type.__new__(metaclass, ...)is used instead of metaclass(...)to avoid using the special metaclass.__new__()implementation that is needed for the slight of hand in a next step to work.
  • In Python 3 only, when temporary_classis used as a base class, Python first calls metaclass.__prepare__()(passing in the derived class name, (temporary_class,)as the this_basesargument. The intendedmetaclass metais then used to call meta.__prepare__(), ignoring this_basesand passing in the basesargument.
  • next, after using the return value of metaclass.__prepare__()as the base namespace for the class attributes (or just using a plain dictionary when on Python 2), Python calls metaclass.__new__()to create the actual class. This is again passed (temporary_class,)as the this_basestuple, but the code above ignores this and uses basesinstead, calling on meta(name, bases, d)to create the new derived 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)以创建新的派生类。

As a result, using with_metaclass()gives you a new class object with no additional base classes:

因此, usingwith_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'>

回答by pylang

UPDATE: the six.with_metaclass()function has since been patched with a decorator variant, i.e. @six.add_metaclass(). This update fixes some mro issues related to the base objects. The new decorator would be applied as follows:

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

import six

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

Here are the patch notesand here is a similar, detailed example and explanationfor using a decorator alternative.

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