为什么不能声明性地覆盖类名,例如使用一个不是有效标识符的类名?
>>> class Potato:
... __name__ = 'not Potato'
...
>>> Potato.__name__ # doesn't stick
'Potato'
>>> Potato().__name__ # .. but it's in the dict
'not Potato'
字符串
我想这可能只是一个简单的情况,这是在类定义块完成后被覆盖的。但似乎不是真的,因为名称是可写的,但显然 * 没有 * 设置在类dict中:
>>> 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
是如何处理的(在类定义块的内部和外部)?
1条答案
按热度按时间z4iuyo4d1#
Potato.__name__
在哪里解析?大多数文档中的dunder方法和属性实际上存在于对象的本机代码端。在CPython的情况下,它们被设置为对象模型中定义的C Struct中的插槽中的指针。(定义在这里-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__
中的代码设置,它是第一个参数。如何处理
Potato.__name__
= other(在类定义块的内部和外部)?一个类的
__dict__
参数不是一个普通的字典--它是一个特殊的Map代理对象,其原因正是为了使类本身的所有属性设置都不通过__dict__
,而是通过类型中的__setattr__
方法。在那里,对这些开槽dunder方法的赋值实际上填充在C对象的C结构中,然后反映在class.__dict__
属性上。因此,在类块的外部,
cls.__name__
是以这种方式设置的-因为它发生在类创建之后。在类块内,所有属性和方法都被收集到一个普通的dict中这个dict被传递给
type.__new__
和其他元类方法--但是如上所述,这个方法从显式传递的name
参数填充__name__
槽(即,在调用type.__new__
时传递的“name”参数)-即使它只是使用dict中的所有名称作为命名空间来更新类__dict__
代理。这就是为什么
cls.__dict__["__name__"]
可以从cls.__name__
插槽中不同的内容开始,但随后的分配会使两者同步。一个有趣的轶事是,三天前我遇到了一些代码,试图在类体中显式地重用
__dict__
名称,这也有类似的令人困惑的副作用。我甚至想知道是否应该有一个bug报告,并询问Python开发人员-正如我所想的那样,权威的答案是:.所有的
__dunder__
名称都是为实现保留的,它们应该只根据文档使用。所以,确实,这不是非法的,但你也不能保证任何东西都能工作。(G.货车Rossum)这同样适用于在类体中定义
__name__
。https://mail.python.org/pipermail/python-dev/2018-April/152689.html
如果一个人真的想在类体中覆盖
__name__
作为一个属性,那么一个简单的元类可以是:字符串