Python 3:super() 意外引发 TypeError

Posted

技术标签:

【中文标题】Python 3:super() 意外引发 TypeError【英文标题】:Python 3: super() raises TypeError unexpectedly 【发布时间】:2016-06-20 04:06:27 【问题描述】:

来自 Java,我在理解 Python 中的 OO 编程的继承、抽象类、静态方法和类似概念时有点吃力。

我有一个表达式树类的实现,由

给出(简化)
# Generic node class
class Node(ABC):
    @abstractmethod
    def to_expr(self):
        pass

    @staticmethod
    def bracket_complex(child):
        s = child.to_expr()
        return s if isinstance(child, Leaf) or isinstance(child, UnaryOpNode) else "(" + s + ")"


# Leaf class - used for values and variables
class Leaf(Node):
    def __init__(self, val):
        self.val = val

    def to_expr(self):
        return str(self.val)


# Unary operator node
class UnaryOpNode(Node):
    def __init__(self, op, child):
        self.op = op
        self.child = child

    def to_expr(self):
        return str(self.op) + super().bracket_complex(self.child)


# Binary operator node
class BinaryOpNode(Node):
    def __init__(self, op, lchild, rchild):
        self.op = op
        self.lchild = lchild
        self.rchild = rchild

    def to_expr(self):
        return super().bracket_complex(self.lchild) + " " + str(self.op) + " " + super().bracket_complex(self.rchild)


# Variadic operator node (arbitrary number of arguments)
# Assumes commutative operator
class VariadicOpNode(Node):
    def __init__(self, op, list_):
        self.op = op
        self.children = list_

    def to_expr(self):
        return (" " + str(self.op) + " ").join(super().bracket_complex(child) for child in self.children)

LeafUnaryOpNodeBinaryOpNode 的实例上调用to_expr() 方法可以正常工作,但在VariadicOpNode 的实例上调用时会引发TypeError

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

我在 super() 突然无法工作的特定课程中做错了什么?

在 Java 中,静态方法会被继承,所以我什至不需要 super 调用,但在 Python 中似乎不是这样。

【问题讨论】:

您使用的是 Python2 还是 Python 3? docs.python.org/2/library/functions.html#super @Jasper 因为他使用super 没有参数,所以我相信他使用的是python3.3+。 题外话:你为什么在to_expr中使用super()来调用bracket_complex?你应该使用self,否则你可能会在覆盖bracket_complex的子类中引入问题 正如标题所述,我使用的是 Python 3。在 Bakuriu 下面的回答之后,我已经修复了 super() -> self 问题。 【参考方案1】:

您在生成器表达式中使用不带参数的super()super() 很神奇——它依赖于调用者框架中的信息。由于生成器表达式创建了一个附加函数,因此没有参数的 super() 在那里不起作用。然而,由于您的超类不可能在方法执行过程中发生更改,您可以将其移出生成器表达式 - 这也应该加快速度:

def to_expr(self):
    bracket_complex = super().bracket_complex
    return (" " + str(self.op) + " ").join(bracket_complex(child) for child in self.children)

但是,由于静态方法在 Python 中是“继承的”,因此您可以通过 self 调用超级方法,前提是您没有在子类中覆盖它。因此,在这种简单的情况下,您可以编写:

def to_expr(self):
    return (" " + str(self.op) + " ").join(self.bracket_complex(child) for child in self.children)

实现细节是,如果没有提供参数,第一个参数应该是调用者框架的__class__ 单元格中的值,第二个应该是给调用者函数的第一个参数。通常在错误的地方使用super 时只会得到SystemError,但是生成器表达式被包装在一个隐式生成器函数中,该生成器函数会创建另一个调用框架。不幸的是,这个函数有一个参数,导致super() 抱怨这个异常。

所以通常super() 将作为第一个参数传递给Foo,但在生成器表达式中,传递了一个生成器对象 - 因此很明显需要引发TypeError

【讨论】:

【参考方案2】:

回答你隐含的问题:

在 Java 中,静态方法会被继承,所以我什至不需要 super 调用,但在 Python 中似乎并非如此。

staticmethods 继承:

class A:
    @staticmethod
    def a():
        print('Hello')

class B(A):
    def b(self):
        self.a()

b = B()
b.a()
b.b()

输出:

Hello
Hello

请注意,您不能简单地写:

class B(A):
    def b(self):
        a()

Python从不将简单名称解析为方法/静态方法;对于 Python a() 必须是一个函数调用,本地或全局。您必须使用self.a 引用实例或使用B.a 引用类。

在 python 中,self显式,就像当前的类引用一样。不要与 Java 的隐式 this 混淆。

【讨论】:

啊,很公平,有道理。 Upvoted :D 在调试这种现象背后的原因时,我没有看到super() 是不必要的

以上是关于Python 3:super() 意外引发 TypeError的主要内容,如果未能解决你的问题,请参考以下文章

Python3---常见函数---super()

Convert.ToDecimal 引发的意外异常

错误 C2062:类型 int 意外

super() 为新型类引发“TypeError:必须是类型,而不是 classobj”

类型索引意外工作并引发无法解释的错误

自定义 VBA 函数引发意外输出 [重复]