Python 以声明方式设置类名称

Python 以声明方式设置类名称,python,metaclass,python-datamodel,python-descriptors,python-object,Python,Metaclass,Python Datamodel,Python Descriptors,Python Object,为什么不能以声明方式重写类名,例如使用无效标识符的类名 >>> class Potato: ... __name__ = 'not Potato' ... >>> Potato.__name__ # doesn't stick 'Potato' >>> Potato().__name__ # .. but it's in the dict 'not Potato' 我想这可能只是在类定义块完成后被覆盖的一种情况。但似乎

为什么不能以声明方式重写类名,例如使用无效标识符的类名

>>> 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__'
土豆。_u name__;='不,真的,不是土豆' >>>土豆。名字有效 “不,真的,不是土豆” >>>马铃薯()。_uname_#但实例在其他地方解析它 “不是土豆” >>>土豆__ mappingproxy({''''uuuuuuuuuuuuuuuuuuuu模块:''uuuuuuuuuuuuuuuuuuuuuuu main', “uuu name_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu 马铃薯的名称在哪里解析

大多数文档化的dunder方法和属性实际上存在于对象的本机代码端。在CPython的情况下,它们被设置为对象模型中定义的C结构中的插槽中的指针。(定义在此处-,但在实际创建C中的新类时,字段更容易可视化,如此处:,其中“super”类型(已定义)

因此,
\uu name
类型中的代码设置。它是第一个参数

如何处理
Potato.\uuuuu name\uuuuu
=other(类定义块的内部和外部)

类的
\uuuuu dict\uuuu
参数不是一个简单的字典-它是一个特殊的映射代理对象,其原因正是这样,类本身上的所有属性设置都不会通过
\uuuu dict\uuuu
,而是通过类型中的
\uuu setattr\uuuu
方法dunder方法实际上是在C对象的C结构中填充的,然后反映在
类上

因此,在类块之外,
cls.\uuuuu name\uuuuu
是这样设置的,因为它发生在类创建之后

在类块内部,所有属性和方法都被收集到一个普通的dict中(尽管可以自定义)。该dict被传递到
类型、\uuuuuu新的\uuuuuuu
和其他元类方法-但是如上所述,该方法从显式传递的
名称
参数中填充
\uuuu名称
槽(也就是说,在调用
type.\uuuuu new\uuuuu
时传递的“name”参数)-即使它只是用dict中的所有名称作为命名空间更新类
\uuuuuuu dict\uuu
代理

这就是为什么
cls.\uuuuu dict\uuuu[“\ uuuu name\uuuuu”]
可以从
cls.\uuuu name\uuuuuu
槽中的内容开始,但随后的分配会使两者同步

一个有趣的轶事是,三天前我遇到一些代码试图在类主体中显式地重用
\uu dict\uuu
名称,这同样有令人费解的副作用。 我甚至想知道是否应该有关于这个问题的bug报告,并询问了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.
(范罗斯姆)

它同样适用于试图在类主体中定义
\uuu name\uuu


如果一个人真的想在类体中重写
\uuuuu name\uuuuu
作为属性,那么元类就是一个简单的元类:

class M(type):
    def __new__(metacls, name, bases, namespace, **kw):
         name = namespace.get("__name__", name)
         return super().__new__(metacls, name, bases, namespace, **kw)

啊。在类定义期间设置它是不起作用的,因为它不会调用元类上的描述符(没有“点”)。这感觉很像是通过观察镜中的名称和所谓的讨论。