以声明方式设置类__name__
问题内容:
为什么不能声明性地覆盖类名,例如,使用不是有效标识符的类名?
>>> 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
处理(在类定义块的内部和外部)?
问题答案:
在哪里
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
,其中定义了“超级”类型)
因此,__name__
通过中的代码在那里设置type.__new__
,它是第一个参数。
Potato.__name__
=如何处理其他问题(在类定义块的内部和外部)?
类的__dict__
参数不是普通的字典-
它是一个特殊的映射代理对象,其原因恰恰是使得该类本身的所有属性设置都不会通过__dict__
,而是通过__setattr__
type中的方法。在这里,对这些带槽的dunder方法的分配实际上是在C对象的C结构中填充的,然后反映在该class.__dict__
属性上。
因此, 在 类块 之外 ,cls.__name__
以这种方式设置-因为它是在创建类之后发生的。
在 类块内,所有属性和方法都收集到简单的字典中(尽管可以自定义)。该dict传递给了type.__new__
其他元类方法-
但如上所述,即使该方法只是更新了类代理,该方法也会__name__
从显式传递的name
参数(即,传递给的调用中传递的“
name”参数type.__new__
) 填充插槽__dict__
字典中的所有名称都用作名称空间。
这就是为什么cls.__dict__["__name__"]
可以从不同于cls.__name__
插槽中内容的内容开始,但是随后的分配却使两者同步的原因。
一个有趣的轶事是,三天前,我遇到了一些代码,试图__dict__
在类主体中显式地重用该名称,这同样具有令人费解的副作用。我什至想知道是否应该有关于此的错误报告,并询问Python开发人员-
正如我所想到的,权威的答案是:
...all __dunder__ names are reserved for the implementation and they should
only be used according to the documentation. So, indeed, it's not illegal,
but you are not guaranteed that anything works, either.
(G. van Rossum)
它与尝试__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)