为 Python 3.6 元类提供 __classcell__ 示例

Posted

技术标签:

【中文标题】为 Python 3.6 元类提供 __classcell__ 示例【英文标题】:Provide __classcell__ example for Python 3.6 metaclass 【发布时间】:2017-05-11 15:04:09 【问题描述】:

根据 3.6.0 文档:

CPython 实现细节:在 CPython 3.6 及更高版本中,__class__ 单元格作为类中的__classcell__ 条目传递给元类 命名空间。如果存在,则必须将其传播到 type.__new__ 调用以便正确初始化类。没有做 所以会在 Python 3.6 中产生一个 DeprecationWarning ,以及一个 RuntimeWarning 未来。

有人可以提供一个正确执行此操作的示例吗?

实际需要的示例?

【问题讨论】:

一些想法:这个补丁有正确的关键词(我不知道这个补丁是否成功):bugs.python.org/file44533/classcell.patch 【参考方案1】:

如果您使用依赖于 __class__ 可用的 super 或在类主体内引用 __class__,则会引发警告。

文本本质上说的是,如果您定义一个自定义元类并在将其传递给type.__new__ 之前篡改您获得的命名空间,则需要这样做。您需要小心并始终确保在您的metaclass.__new__ 中将__classcell__ 传递给type.__new__

也就是说,如果你创建一个新的花哨的命名空间来传递,请始终检查__classcell__是否在创建的原始命名空间中定义并添加:

class MyMeta(type):
    def __new__(cls, name, bases, namespace):
        my_fancy_new_namespace = ....  
        if '__classcell__' in namespace:
             my_fancy_new_namespace['__classcell__'] = namespace['__classcell__']
        return super().__new__(cls, name, bases, my_fancy_new_namespace)

您在评论中链接的文件实际上是许多尝试的补丁中的第一个,issue23722_classcell_reference_validation_v2.diff 是最后一个补丁,来自Issue 23722。

在pull request made to Django 中可以看到一个正确执行此操作的示例,该示例使用它来解决 Python 3.6 中引入的问题:

new_attrs = '__module__': module
classcell = attrs.pop('__classcell__', None)
if classcell is not None:
    new_attrs['__classcell__'] = classcell
new_class = super_new(cls, name, bases, new_attrs)

__classcell__ 在传递给 type.__new__ 之前只是添加到新命名空间。

【讨论】:

我花了一段时间才发现如果类中的任何方法使用super() 形式,它就会被创建。这很可能是因为之前不可能调用任何在元类方法本身中使用super() 的类方法。更有可能的是,Python 3.6 中的新 __init_subclass__ 机制需要它。 @jsbueno 是的,__init_subclass__ 带有自定义元类。如果你使用type 就可以了。 @JimFasarakis-Hilliard 能否提供参考或简单示例来说明自定义命名空间?

以上是关于为 Python 3.6 元类提供 __classcell__ 示例的主要内容,如果未能解决你的问题,请参考以下文章

python 面向对象编程 之 元类

[TimLinux] Python 元类

关于 Python3 元类的一些问题

python 元类

python中的元类(metaclass)

何时在 Python 中内联元类的定义?