如何在类方法中设置__class__单元格值



查看 Python 3.5 中super类型的文档,它注意到super(…)super(__class__, «first argument to function»)相同。 令我惊讶的是,我编写了一个返回__class__的方法 - 它确实有效:

>>> class c:
...   def meth(self): return __class__
... 
>>> c().meth()
<class '__main__.c'>

显然,__class__是由函数闭包分配的自由变量:

>>> c.meth.__code__.co_freevars
('__class__',)
>>> c.meth.__closure__
(<cell at 0x7f6346a91048: type object at 0x55823b17f3a8>,)

我想知道在什么情况下自由变量在闭包中相关联。 我知道,如果我在创建类的过程中将函数分配给变量,它不会发生。

>>> def meth2(self): return __class__
... 
>>> meth2.__code__.co_freevars
()

即使我创建了一个新类并作为该创建的一部分为meth2分配了一些属性,meth2也不会以某种方式神奇地获得一个被填充的自由变量。

这并不奇怪,因为其中一部分似乎取决于编译代码时编译器的词法状态。

我想确认__class__被视为自由变量的必要条件很简单:

  • 对代码块中__class__的引用;以及
  • 包含__class__引用的def在词法上位于class声明块中。

我进一步想了解正确填写该变量所需的条件是什么。看起来 - 至少从Python 3.6文档来看 - 以某种方式涉及type.__new__(…)之类的东西。 我无法确定type是如何发挥作用的,以及这一切如何与最终不调用type.__new__(…)的元类交互。

我特别困惑,因为当时我不认为命名空间的__setattr__方法用于将包含该方法的属性分配给方法函数(因为它存在于最终构造的类对象上)。 我知道这个命名空间对象之所以存在,是因为它要么是通过使用class语句隐式构造的,要么是由元类的__prepare__方法显式构造的——但据我所知,元类构造了在函数对象设置为类命名空间中的值填充__class__的类对象。

在 Python 数据模型的文档 § 3.3.3.6 – "创建类对象" – 你会发现以下内容:

[该] 类对象是将被super()的零参数形式 .__class__是隐式闭包 编译器创建的引用(如果类主体中的任何方法引用) 到__class__super.这允许零参数形式 正确识别所定义的类的super()词法范围,而用于使 当前调用根据传递给的第一个参数标识 方法。

。重点是我的。这证实了你对__class__闭包发生的两个假定标准:方法 def 中的"__class__"引用,它本身在class语句中定义。

但是,"创建类对象"中的下一个¶继续说:

CPython 实现细节:在 CPython 3.6 及更高版本中,__class__单元格作为__classcell__条目传递给元类 在类命名空间中。如果存在,则必须将其传播到type.__new__调用以便初始化类 正确。否则将导致 PythonRuntimeError3.8.

。重点是他们的。这意味着,如果您使用带有__new__方法的元类 - 为了指示创建如此指定的类的术语 - 例如:

class Meta(type):
def __new__(metacls, name, bases, attributes, **kwargs):
# Or whatever:
if '__slots__' not in attributes:
attributes['__slots__'] = tuple()
# Call up, creating and returning the new class:
return super().__new__(metacls, name,
bases,
attributes,
**kwargs)

。最后一个super(…).__new__(…)调用实际上是在呼叫type.__new__(…)。在现实生活中,__new__(…)如果你的元类继承自其他元类(例如,例如abc.ABCMeta)。但是,实际上,在Meta.__new__(…)方法内部,在方法入口点、super(…).__new__(…)调用和return新类对象之间,您可以通过attributes['__classcell__']†检查或设置最终__class__单元格变量的值。

现在至于这是否有用:我不知道。我已经用python编程十年了;我完全使用元类‡,就像,绝对一直(无论好坏);在此过程中,我从未做过以下任何一件事:

  1. 重新分配__class__属性;
  2. 检查了任何事物的__class__单元格变量;也没有
  3. 乱了这个所谓的__classcell__命名空间条目,就像任何容量一样

。当然,你的编程经验会和我不一样,谁知道自己是做什么的。这并不是说上述任何一种策略在事实上都必然存在问题。但是我对随心所欲地弯曲 Python 的类型系统和元编程工具并不陌生,这些特殊的东西从来没有表现出特别有用,特别是当你在元类的一般上下文中工作时,以及它们的作用。

我想我的意思是,tl;DR:你正处于弄清楚元类的基础知识以及它们能做什么的风口浪尖上——继续做实验,但要深入和呼吸地研究这个主题。事实上!


† – 在阅读此类代码示例时,您经常会发现我在这里的片段称为namespacens或类似attributes字典。都是一样的东西。

‡ – ...ABC和混合,班级装饰师和__init_subclass__(…)以及滥用__mro_entries__(…)谋取私利;等等,广告恶心

相关内容

  • 没有找到相关文章

最新更新