python 以声明方式设置class __name__

yfjy0ee7  于 5个月前  发布在  Python
关注(0)|答案(1)|浏览(58)

为什么不能声明性地覆盖类名,例如使用一个不是有效标识符的类名?

>>> 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__'


问题:

  1. Potato.__name__在哪里解析?
  2. Potato.__name__ = other是如何处理的(在类定义块的内部和外部)?
z4iuyo4d

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__作为一个属性,那么一个简单的元类可以是:

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

字符串

相关问题