查看 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__
调用以便初始化类 正确。否则将导致 PythonRuntimeError
3.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编程十年了;我完全使用元类‡,就像,绝对一直(无论好坏);在此过程中,我从未做过以下任何一件事:
- 重新分配
__class__
属性; - 检查了任何事物的
__class__
单元格变量;也没有
弄 - 乱了这个所谓的
__classcell__
命名空间条目,就像任何容量一样
。当然,你的编程经验会和我不一样,谁知道自己是做什么的。这并不是说上述任何一种策略在事实上都必然存在问题。但是我对随心所欲地弯曲 Python 的类型系统和元编程工具并不陌生,这些特殊的东西从来没有表现出特别有用,特别是当你在元类的一般上下文中工作时,以及它们的作用。
我想我的意思是,tl;DR:你正处于弄清楚元类的基础知识以及它们能做什么的风口浪尖上——继续做实验,但要深入和呼吸地研究这个主题。事实上!
† – 在阅读此类代码示例时,您经常会发现我在这里的片段称为namespace
或ns
或类似attributes
字典。都是一样的东西。
‡ – ...ABC和混合,班级装饰师和__init_subclass__(…)
以及滥用__mro_entries__(…)
谋取私利;等等,广告恶心