为Python 3.6元类提供__classcell__示例

Provide __classcell__ example for Python 3.6 metaclass

根据3.6.0文件:

CPython implementation detail: In CPython 3.6 and later, the __class__
cell is passed to the metaclass as a __classcell__ entry in the class
namespace. If present, this must be propagated up to the type.__new__
call in order for the class to be initialized correctly. Failing to do
so will result in a DeprecationWarning in Python 3.6, and a
RuntimeWarning in the future.

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

一个实际需要它的例子?


如果使用依赖于__class__可用的零参数super super().__method__(args),或在类体内引用__class__,则会引发警告。

本文本质上说的是,如果您定义了一个自定义的元类,并在将其传递给type.__new__之前篡改了所得到的名称空间,那么就需要这样做。你需要小心,并确保在你的metaclass.__new__中把__classcell__传给type.__new__

也就是说,如果要创建一个新的奇特名称空间来传递,请始终检查是否在创建的原始名称空间中定义了__classcell__,然后添加它:

1
2
3
4
5
6
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是从23722版开始的最后一个修补程序。

正确执行此操作的示例可以在向Django发出的拉请求中看到,该请求使用它来修复Python 3.6中引入的问题:

1
2
3
4
5
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)

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