声明性地设置类__name__ [英] Setting a class __name__ declaratively

查看:166
本文介绍了声明性地设置类__name__的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Why can't you override a class name declaratively, e.g. to use a class name which is not a valid identifier?

>>> class Potato:
...     __name__ = 'not Potato'
...     
>>> Potato.__name__  # doesn't stick
'Potato'
>>> Potato().__name__  # .. but it's in the dict
'not Potato'

我认为在类定义块完成之后,可能只是覆盖了这种情况.但这似乎是不对的,因为该名称是可写的,但显然在类dict中设置了 not :

I thought maybe it was simply a case that this was overwritten after the class definition block completes. But seems that's not true, because the name is writable yet apparently not set in the class 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的处理方式(在类定义块的内部和外部)?
  1. Where does Potato.__name__ resolve?
  2. How is Potato.__name__ = other handled (inside and outside of a class definition block)?

推荐答案

Potato.__name__在哪里解析?

大多数文档记录的dunder方法和属性实际上存在于对象的本机代码侧.对于CPython,它们被设置为对象模型中定义的C Struct中插槽中的指针. (在此处定义- https://github.com/python/cpython/blob/04e82934659487ecae76bf4a2db7f92c8dbe0d25/Include/object.h#L346 ,但是当人们在C中实际创建新类时,字段更容易可视化,例如:

Most documented dunder methods and attributes actually exist in the native code side of the object. In the case of CPython, they are set as pointers in a slot in a C Struct defined in the object model. (defined here - https://github.com/python/cpython/blob/04e82934659487ecae76bf4a2db7f92c8dbe0d25/Include/object.h#L346 , but with fields easier to visualize when one actually creates a new class in C, like here: https://github.com/python/cpython/blob/04e82934659487ecae76bf4a2db7f92c8dbe0d25/Objects/typeobject.c#L7778 , where the "super" type is defined)

因此,__name__是通过type.__new__中的代码设置的,它是第一个参数.

Therefore, __name__ is set there by the code in type.__new__, to which it is the first parameter.

Potato.__name__ =其他如何处理(在类定义块的内部和外部)?

How is Potato.__name__ = other handled (inside and outside of a class definition block)?

一个类的__dict__参数不是简单的字典-它是一个特殊的映射代理对象,其原因恰恰是使得该类本身的所有属性设置都不会通过__dict__,并且而是通过类型中的__setattr__方法.在那里,对这些带槽的dunder方法的分配实际上是在C对象的C结构中填充的,然后反映在class.__dict__属性上.

A class's __dict__ parameter is not a plain dictionary - it is an special mapping proxy object, and the reason for that is exactly so that all attribute settings on the class itself don't go through the __dict__, and instead go through the __setattr__ method in type. In there, assignments to these slotted dunder methods are actually filled in the C object's C structure, and then reflected on the class.__dict__ attribute.

因此,外部一个类块cls.__name__是以这种方式设置的-它是在创建类之后发生的.

So, outside a class block, cls.__name__ is set in this way - as it takes place after the class has been created.

内部的一个类块中,所有属性和方法都收集到一个简单的字典中(尽管可以自定义).该dict传递给type.__new__和其他元类方法-但如上所述,该方法从显式传递的name参数(即,在对type.__new__)-即使它只是将dict中用作名称空间的所有名称更新类__dict__代理.

Inside a class block, all attributes and methods are collected into a plain dict (though that can be customized). This dict is passed to type.__new__ and other metaclass methods - but as said above, this method fills in the __name__ slot from the explicit passed name parameter (that is, the "name" argument passed in the call to type.__new__)- even though it just updates the class __dict__ proxy with all names in the dict used as namespace.

这就是为什么cls.__dict__["__name__"]可以从与cls.__name__插槽中不同的内容开始的原因,但是随后的分配却使两者同步.

That is why cls.__dict__["__name__"] can start with a different content from what is in the cls.__name__ slot, but subsequent assignments put both in sync.

一个有趣的轶事是,三天前,我遇到了一些代码,试图在类主体中显式地重用__dict__名称,这同样具有令人费解的副作用. 我什至想知道是否应该有关于此的错误报告,并询问Python开发人员-正如我所想到的,权威的答案是:

An interesting anecdote is that three days ago I came across some code trying to reuse the __dict__ name explicitly in the class body, which has similarly puzzling side-effects. I even wondered whether there should be a bug report on that, and queried the Python developers - and as I had thought of, the authoritative answer was:

...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)

(G. van Rossum)

它与尝试在类主体中定义__name__的方法相同.

And it applies just the same to trying to define __name__ in the class body.

https://mail.python.org/pipermail /python-dev/2018-April/152689.html

如果实际上要覆盖__name__作为类主体中的属性,则该类的元类很简单,就像元类可以是:

And if one actually wants to override __name__ as an attribute in the classbody, a metaclass for that is a simple as a metaclass can be:

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

这篇关于声明性地设置类__name__的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆