Python 3.6元类的__classcell__示例

21

根据3.6.0文档:

CPython实现细节:在CPython 3.6及更高版本中,__class__单元格以__classcell__条目的形式传递到元类中。如果存在,则必须将其传播到type.__new__调用中,以便正确初始化类。如果未能这样做,将在Python 3.6中产生一个DeprecationWarning,并在未来产生一个RuntimeWarning

有人能提供一个正确的示例吗?

需要实际使用它的示例?


一些想法:这个补丁有正确的关键词(我不知道这个补丁是否已经被采纳):http://bugs.python.org/file44533/classcell.patch - Dima Tisnek
1个回答

19

如果你使用依赖于__class__可用性或在类体内引用__class__的super,就会发出警告。

文本基本上是说,如果你定义了一个自定义元类并且篡改了在传递给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

提交给 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()的classmethod以前是不可能的。更有可能的是,Python 3.6中的新__init_subclass__机制需要它。 - jsbueno
@jsbueno 是的,使用自定义元类和 __init_subclass__。如果你使用 type 就没问题了。 - Dimitris Fasarakis Hilliard
@JimFasarakis-Hilliard,你能提供一个参考或简单的示例来说明自定义命名空间吗? - Dima Tisnek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接