关于继承:python 3:super()意外引发typeerror

Python 3: super() raises TypeError unexpectedly

从Java开始,我在Python中挣扎着继承继承、抽象类、静态方法和类似的OO编程概念。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 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

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

super()突然不起作用的特定类中,我做错了什么?

在Java中,静态方法将继承,所以我甚至不需要超级调用,但在Python中,情况似乎并非如此。


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

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

但是,由于静态方法在python中是"继承"的,如果没有在子类中重写它,那么可以通过self调用超级方法。因此,在这个简单的例子中,您可以编写:

1
2
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


回答你隐含的问题:

In Java the static method would get inherited so I wouldn't even need
the super call, but in Python this does not seem to be the case.

staticmethods继承:

1
2
3
4
5
6
7
8
9
10
11
12
class A:
    @staticmethod
    def a():
        print('Hello')

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

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

输出:

1
2
Hello
Hello

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

1
2
3
class B(A):
    def b(self):
        a()

python永远不会将简单名称解析为方法/静态方法;对于python a(),必须是局部或全局函数调用。您必须引用使用self.a的实例或使用B.a的类。

在python中,self和当前的类引用一样是显式的。不要混淆Java的隐式EDOCX1 16Ω。