问题描述
在理解中使用python3的super似乎总是会导致TypeError: super(type, obj): obj must be an instance or subtype of type
(但是使用python 2的super确实可以按预期工作)
Using python3's super in a comprehension seems to always result in TypeError: super(type, obj): obj must be an instance or subtype of type
(but using python 2's super does work as expected)
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()
生成器理解失败了?
So, why does new super()
fail in generator comprehensions?
附录:
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()
的文档:
Simple explanation
Look at the documentation for super()
:
在类定义内 表示在类方法范围内 .在 class方法作用域中,解释器能够使用与Python 2中明确提供的参数相同的参数来完成零形式.但是列表推导却创建了它自己的作用域.这就是它失败的原因:您不是从类方法范围中调用super()
,并且解释器无法使用所有参数来完成它.
By inside a class definition they meant inside class method scope. Inside class method scope interpreter is able to complete zero form with same parameters as you would explicitly provide it in Python 2. List comprehension however creates it's own scope. That's the reason why it fails: you call to super()
not from the class method scope and interpreter is not able to complete it with all parameters.
根据Python 数据模型:
According to Python data model:
Python甚至可以在列表理解内从__class__
变量中为super()
收集super()
的第一个参数(因为它在所有子作用域中都可以作为常规闭包使用).您可以使用以下方法进行测试:
Python is able to collect first parameter for super()
from __class__
variable even inside list comprehension (since it is available in all child scopes as any usual closure). You can test it using:
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()
的调用发生在方法范围内,并尝试使用以下 C代码(为清晰起见,省略了许多行):
But interpreter incorrectly collects second parameter for super()
: self
. It assumes that call to super()
happens inside method scope and tries to get first parameter for the method using following C code (many lines are omitted for clarity):
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]
,但是它包含根据代码中的注释"locals + stack".因此,我们可以利用locals()
进行测试(不幸的是缺少订单).让我们测试一下,在类方法和列表理解内的本地变量中可用的内容:
It's not possible to access f->f_localsplus[0]
from Python, but it contains "locals+stack" according to comment in code. So we can utilize locals()
for a test (unfortunately order is missing). Let's test, what is available in locals inside class method and list comprehension:
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>}
在第一种情况下,有一个对我们对象的引用,解释器会正确找到它.在列表理解内,字典内没有对象,因此它将得到0
或range_iterator
(还记得,命令丢失了吗?).这些都不是我们对象的实例.它会失败supercheck()
并给您一个错误obj must be an instance or subtype of type
(例如T
).
There is a reference to our object in the first case and it will be correctly found by interpreter. Inside list comprehension there is no object inside dictionary so it will get either 0
or range_iterator
(remember, order is missing?). Neither of those is instance of our object. It will fail supercheck()
and give you an error obj must be an instance or subtype of type
(e.g. T
).
在此处中查看有关如何实现super()
和以了解执行此操作的详细信息.
Take a look here for more information on how super()
is implemented and here for more details why it's done like this.
这篇关于Python3的超级技巧和理解力-> TypeError?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!