如何在函数

时间:2017-04-26 21:01:15

标签: python python-3.x python-internals

在python 3.5中查看超类型的文档,它注意到super()与super(__class__,函数的第一个参数)相同。令我惊讶的是,我写了一个返回__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 被视为自由变量所需的条件是:

  • 参考代码块
  • 中的
  • 包含引用的def在词法上是一个类块。

我还想了解正确填充该变量所需的条件是什么。它听起来至少来自类型的python 3.6文档。 new 以某种方式涉及。我无法理解howe类型是如何起作用的,以及它如何与最终不会调用类型的元类进行交互。 new 。 我特别困惑,因为我不认为当命名空间的 setattr 方法将包含方法的属性分配给方法函数时,最终的类对象存在。我知道命名空间对象是存在的,因为它是由类隐式构造的,或者是由元类的 prepare 方法显式构造的,但我最好能告诉元类构造填充类的类对象将函数对象设置为命名空间中的值。

1 个答案:

答案 0 :(得分:1)

the docs for Python’s data model中,§ 3.3.3.6 –“创建类对象” –您将找到以下内容:

[The]类对象是将由 super()的零参数形式。 __class__是一个隐式闭包 如果类主体中的任何方法引用,则由编译器创建的引用 到__class__super 。这允许零参数形式 super()的值,以根据以下内容正确识别正在定义的类 词法作用域,而用于 当前调用是根据传递给的第一个参数确定的 方法。

…重点是我的。这确认了发生__class__关闭的两个假定条件:方法def中的“ __class__”引用,它本身是在class语句中定义的。

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

CPython实现的详细信息::在CPython 3.6和更高版本中,__class__单元作为__classcell__条目传递给元类。 在类名称空间中。如果存在,则必须传播到 进行type.__new__调用以初始化该类 正确地。否则将在Python中导致RuntimeError 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-新类对象之间,您可以检查或设置最终值__class__attributes['__classcell__']†的单元格变量。

现在,这是否有用:我不知道。我已经在编程了十年;我绝对总是在使用元类‡(无论好坏);在这样做的过程中,我从未做过以下任何事情:

  1. 重新分配了__class__属性;
  2. 检查了任何事物的__class__单元变量;也不
  3. 像其他任何容量一样迷住了这个假定的__classcell__名称空间条目

…当然,您的编程经验将与我的人不同,后者知道自己的工作经验。并非所有上述战略都必然是事实问题。但是,我一心想将Python的类型系统和元编程功能摆在首位,这并不陌生,而且这些特殊的事情从未表现出过明显的实用性,尤其是当您在元类的一般上下文中工作时,它们是如此。

我想说的是,tl; dr:您正在弄清楚元类的基本知识以及它们可以做什么–紧逼并进行实验,但是要深入,深入地研究这个话题。确实!


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

‡–…以及ABC,mixin和类修饰符以及__init_subclass__(…)和滥用__mro_entries__(…)谋取私利; 等,恶心