Python3的超级技巧和理解力-> TypeError? [英] Python3's super and comprehensions -> TypeError?

查看:78
本文介绍了Python3的超级技巧和理解力-> TypeError?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在理解中使用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():

零参数形式仅在类定义内起作用,因为编译器会填写必要的详细信息以正确检索要定义的类,以及为普通方法访问当前实例.

The zero argument form only works inside a class definition, as the compiler fills in the necessary details to correctly retrieve the class being defined, as well as accessing the current instance for ordinary methods.

在类定义内 表示在类方法范围内 .在 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:

如果类主体中的任何方法引用__class__super,则

__class__是编译器创建的隐式闭包引用.这允许super()的零参数形式根据词法作用域正确识别正在定义的类,而用于进行当前调用的类或实例是根据传递给该方法的第一个参数来识别的.

__class__ is an implicit closure reference created by the compiler if any methods in a class body refer to either __class__ or super. This allows the zero argument form of super() to correctly identify the class being defined based on lexical scoping, while the class or instance that was used to make the current call is identified based on the first argument passed to the method.

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>}

在第一种情况下,有一个对我们对象的引用,解释器会正确找到它.在列表理解内,字典内没有对象,因此它将得到0range_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?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆