为什么不能以声明方式覆盖类名,例如使用不是有效标识符的类名?
>>> class Potato:
... __name__ = 'not Potato'
...
>>> Potato.__name__ # doesn't stick
'Potato'
>>> Potato().__name__ # .. but it's in the dict
'not Potato'
我想这可能只是类定义块完成后被覆盖的情况。但似乎这不是真的,因为名称是可写的,但显然 not 在类字典中设置:
>>> Potato.__name__ = 'no really, not Potato'
>>> Potato.__name__ # works
'no really, not Potato'
>>> Potato().__name__ # but instances resolve it somewhere else
'not Potato'
>>> Potato.__dict__
mappingproxy({'__module__': '__main__',
'__name__': 'not Potato', # <--- setattr didn't change that
'__dict__': <attribute '__dict__' of 'no really, not Potato' objects>,
'__weakref__': <attribute '__weakref__' of 'no really, not Potato' objects>,
'__doc__': None})
>>> # the super proxy doesn't find it (unless it's intentionally hiding it..?)
>>> super(Potato).__name__
AttributeError: 'super' object has no attribute '__name__'
问题:
Potato.__name__
在哪里解决?Potato.__name__ = other
(在类定义块内部和外部)? 在哪里解决?Potato.__name__
大多数记录的 dunder 方法和属性实际上存在于对象的本机代码端。对于 CPython,它们被设置为对象模型中定义的 C 结构中槽中的指针。 (此处定义 - https://github.com/python/cpython/blob/04e82934659487ecae76bf4a2db7f92c8dbe0d25/Include/object.h#L346,但当人们实际在 C 中创建一个新类时,字段更容易可视化,如下所示: https://github.com/python/cpython/blob/04e82934659487ecae76bf4a2db7f92c8dbe0d25/Objects/typeobject.c#L7778,其中定义了“super”类型)
因此,
__name__
由type.__new__
中的代码设置,它是第一个参数。
如何处理
= other(在类定义块内部和外部)?Potato.__name__
类的
__dict__
参数不是一个普通的字典——它是一个特殊的映射代理对象,这样做的原因正是为了让类本身的所有属性设置不经过 __dict__
,而是走通过 type 中的 __setattr__
方法。在那里,对这些 slotted dunder 方法的赋值实际上填充在 C 对象的 C 结构中,然后反映在 class.__dict__
属性上。
因此,在类块之外,
cls.__name__
以这种方式设置 - 因为它是在创建类之后发生的。
在类块内部,所有属性和方法都收集到一个普通的字典中(尽管可以自定义)。该字典被传递给 type.__new__
和其他元类方法 - 但如上所述,该方法从显式传递的
__name__
参数(即在调用时传递的“name”参数)填充
name
槽。 type.__new__
)- 即使它只是更新类
__dict__
代理,并将字典中的所有名称用作命名空间。这就是为什么
cls.__dict__["__name__"]
可以从
cls.__name__
插槽中不同的内容开始,但后续分配会使两者同步。一个有趣的轶事是,三天前我遇到了一些代码,试图在类主体中显式重用
__dict__
名称,这也具有类似的令人费解的副作用。 我什至想知道是否应该有一个关于此的错误报告,并询问了Python开发人员 - 正如我所想到的,权威的答案是:
...所有它同样适用于尝试在类主体中定义
__dunder__
名称都是为实现而保留的,它们应该 只能根据文档使用。所以说确实不违法 但也不能保证任何事情都有效。 (G. 范罗森)
__name__
。
https://mail.python.org/pipermail/python-dev/2018-April/152689.html
__name__
重写为类主体中的一个属性,那么元类就很简单,就像元类一样:
class M(type):
def __new__(metacls, name, bases, namespace, **kw):
name = namespace.get("__name__", name)
return super().__new__(metacls, name, bases, namespace, **kw)