在理解中使用python3的super似乎总是得到TypeError: super(type, obj): obj must be an instance or subtype of type(但使用Python2的super确实如预期的那样有效)

class A(object):
    def __repr__(self):
         return "hi!"  

class B(A):
    def __repr__(self):
         return "".join(super().__repr__() for i in range(2))  

repr(B())
#output: <repr(<__main__.B at 0x7f70cf36fcc0>) failed: TypeError: super(type, obj): obj must be an instance or subtype of type>

class C(A):
    def __repr__(self):
        s = ''
        for i in range(4):
            s += super().__repr__()
        return s     

repr(C())
#output: hi!hi!hi!hi!

class D(A):
    def __repr__(self):
        return "".join(super(D,self).__repr__() for i in range(4))

repr(D())
#output: hi!hi!hi!hi!

那么,为什么新super()在发电机理解上失败了呢?

增编:

In [28]: class E(A):
   ....:     def __repr__(self):
   ....:         def inner():
   ....:             print(repr(__class__))
   ....:         inner()
   ....:         return ''
In [29]: repr(E())
<class '__main__.E'>
Out[29]: ''

In [30]: class F(A):
   ....:     def __repr__(self):
   ....:         return "".join([super().__repr__() for i in range(4)])
   ....:     

In [31]: repr(F())

TypeError: super(type, obj): obj must be an instance or subtype of type

推荐答案

简单解释

请参阅super()的文档:

零参数表单只在类定义中起作用,因为编译器会填写必要的细节,以正确检索所定义的类,以及访问普通方法的当前实例.

inside a class definition是指inside class method scope.Inside class method scope解释器能够使用与Python 2中显式提供的参数相同的参数完成零形式.然而,列表理解创建了它自己的范围.这就是它失败的原因:您不是从类方法作用域调用super(),解释器无法使用所有参数完成它.

高级解释

根据Python data model:

如果类主体中的任何方法引用__class__super,则__class__是编译器创建的隐式闭包引用.这允许super()的零参数形式正确标识基于词法范围定义的类,而用于进行当前调用的类或实例则基于传递给方法的第一个参数进行标识.

Python能够从__class__变量中收集super()的第一个参数,即使在列表理解中也是如此(因为它和任何通常的闭包一样在所有子作用域中都可用).您可以使用以下方法进行测试:

class T:
    def test(self):
        print(__class__)
        print([__class__ for _ in range(1)][0])


T().test()

将输出:

<class '__main__.T'>
<class '__main__.T'>

但解释器错误地收集了super():self的第二个参数.它假设对super()的调用发生在方法范围内,并try 使用以下C code获取方法的第一个参数(为了清楚起见,省略了许多行):

PyFrameObject *f = PyThreadState_GET()->frame;
obj = f->f_localsplus[0];
if (obj != NULL) {
    obj_type = supercheck(type, obj);
    if (obj_type == NULL)
        return -1;
    Py_INCREF(obj);
}

无法从Python中访问f->f_localsplus[0],但根据代码中的注释,它是contains个"局部+堆栈".因此,我们可以利用locals()进行测试(不幸的是,缺少订单).让我们测试一下,在类内方法和列表理解中有哪些内容:

class T:
    def test(self):
        print(locals())
        print([locals() for _ in range(1)])


T().test()

将打印:

{'self': <__main__.T object at 0x100f1f8d0>}
{'_': 0, '.0': <range_iterator object at 0x100fbb2a0>}

在第一种情况下,有一个对我们的对象的引用,解释器将正确地找到它.在列表理解中,字典中没有对象,所以它将得到0range_iterator(记住,缺少顺序?).这两个都不是我们的目标.它将失败supercheck(),并给出错误obj must be an instance or subtype of type(例如T).


查看here了解super()是如何实现的,查看here了解为什么这样做的更多细节.

Python-3.x相关问答推荐

Python网页抓取:代码输出:汤未定义

如何获得给定列表中所有可能的元素组合?

S的两极是什么,相当于大Pandas 的`.ilo‘方法?

无法使用诗词安装PyYaml

动态范围内来自另外两列的列求和

使用Python抓取sofascore以获取有关球队阵容和投票的信息

以某种方式分割字符串

Python 列表求和所有出现的保留顺序

在 python pandas 中设置条件和分配新值

多进程:两个进程,一起杀死

使用 GEKKO 使用代码解决最佳时间控制问题时出现 IndexError

Jupyter Notebook 拒绝打印一些字符串

Pandas 将列格式化为货币

TimescaleDB:是否可以从 Python 调用create_hypertable?

运行 PyCharm 测试时如何解决django.core.exceptions.ImproperlyConfigured:找不到 GDAL 库?

如何在 Python 中计算 cohen 的 d?

Jupyter Notebook - 在函数内绘图 - 未绘制图形

pip install dryscrape 失败并显示错误:[Errno 2] 没有这样的文件或目录:'src/webkit_server'?

为什么排序列表比未排序列表大

我可以替换 Python 中对象的现有方法吗?