在 python3.x 中显式继承“类型”以实现元类

Posted

技术标签:

【中文标题】在 python3.x 中显式继承“类型”以实现元类【英文标题】:Explicitly inheriting from 'type' to implement metaclass in python3.x 【发布时间】:2018-04-26 22:35:26 【问题描述】:

我试图对 Python 中的元类有一些直觉。我已经尝试过 Python2.7 和 Python3.5。在 Python3.5 中,我发现我们定义的每个类都是 <class 'type'> 类型,无论我们是否显式继承。但如果不是从 type 继承,我们就不能将该类用作另一个类的元类。

>>> class foo:
    pass

>>> class Metafoo(type):
    pass

>>> foo
<class '__main__.foo'>
>>> Metafoo
<class '__main__.Metafoo'>
>>> type(foo)
<class 'type'>
>>> type(Metafoo)
<class 'type'>
>>> 
>>> class foocls1(metaclass=foo):
    pass

执行上述操作会出现以下错误:

Traceback (most recent call last):
  File "<pyshell#52>", line 1, in <module>
    class foocls1(metaclass=foo):
TypeError: object() takes no parameters

但在使用 Metafoo 作为新类的元类时,情况并非如此

>>> class foocls3(metaclass=Metafoo):
    pass

>>> foocls3
<class '__main__.foocls3'>
>>> type(foocls3)
<class '__main__.Metafoo'>

如果我们想将一个类用作其他类中的元类,任何人都可以解释为什么我们需要显式继承这种情况。

【问题讨论】:

每个类都是type实例,但它们并不是type的所有子类 【参考方案1】:

"type" 是 Python 3 和 Python 2 2.2 之后的所有类对象的基类。 (只是在 Python 2 上,您应该从 object 继承。在 Python 2 中没有显式从“object”继承的类被称为“old style classes”,并出于向后兼容的目的而保留,但几乎没有用.)

所以,发生的事情就是继承:类的超类是什么,以及“由于在 Python 中类是对象本身,该对象的类是什么”的“元类”是两个不同的东西。您继承的类定义了对类实例的属性(和方法)查找顺序,因此您有一个共同的行为。

元类是“你的类所用的类”,虽然它可以用于其他目的,但最常用于修改类本身的构造步骤。搜索一下,您会看到主要实现__new____init__ 方法的元类。 (虽然可以有其他方法,但是你必须知道你在做什么)

碰巧构建一个类需要一些构建普通对象不需要的操作。这些操作在本机代码层(CPython 中的 C)上执行,甚至无法在纯 Python 代码中重现,例如填充类的特殊方法槽——指向实现 __add____eq__ 等的函数的指针方法。在 CPython 中这样做的唯一类是“类型”。因此,您想用来构建类的任何代码,即作为元类,都必须在某个时候调用type.__new__ 方法。 (就像你想在 Python 中创建一个新对象一样,在某些时候会调用object.__new__ 中的代码。)。

您的错误发生并不是因为 Python 会提前检查您是直接还是间接调用了 type.__new__。错误:“TypeError:object() 没有参数”仅仅是因为元类的__new__ 方法传递了 3 个参数(名称、基数、命名空间),而 object 中的相同方法没有传递参数。 (两者都获得额外的“cls”,也相当于 self,但这不计算在内)。

您可以将任何可调用对象用作元类,甚至是普通函数。只是它必须采用 3 个显式参数。从那时起,无论这个可调用返回什么都用作类,但如果在某些时候您不调用type.__new__(甚至是间接调用),那么您就没有要返回的有效类。

例如,可以创建一个简单的方法,以便能够将类主体用作字典声明:

def dictclass(name, bases, namespace):
    return namespace

class mydict(metaclass=dictclass):
    a = 1
    b = 2
    c = 3

mydict["a"] 

所以,一个有趣的事实是type 是它自己的元类。 (在 Python 实现中是硬编码的)。但是type 本身也从对象继承

In [21]: type.__class__
Out[21]: type

In [22]: type.__mro__
Out[22]: (type, object)

为了结束这一点:可能在不调用 type.__new__ 的情况下创建对象,在不调用 object.__new__ 的情况下创建对象,但通常不是从纯 Python 代码中创建,作为 C-必须为这两个操作填写 API 级别。可以使用本机代码实现函数来执行此操作,也可以使用 ctypes 对其进行破解。

【讨论】:

感谢您的解释

以上是关于在 python3.x 中显式继承“类型”以实现元类的主要内容,如果未能解决你的问题,请参考以下文章

在 Java 中显式调用默认方法

为啥在 Haskell 中显式推导 Show/Read?

有没有办法在 lambda 表达式中显式定义泛型参数类型?

C#:在实现的方法中显式指定接口

为什么在实例化模板时必须在参数包中显式显示类型?

在 terraform 模块中显式使用提供程序