下面使用super()会引发TypeError:为什么?

>>> from  HTMLParser import HTMLParser
>>> class TextParser(HTMLParser):
...     def __init__(self):
...         super(TextParser, self).__init__()
...         self.all_data = []
...         
>>> TextParser()
(...)
TypeError: must be type, not classobj

在StackOverflow上也有类似的问题:Python super()引发TypeError,其中错误是由用户类不是新样式类这一事实解释的。然而,上面的类是一个新型的类,因为它继承自object:

>>> isinstance(HTMLParser(), object)
True

我错过了什么?我如何使用super(),在这里?

使用HTMLParser.__init__(self)而不是super(TextParser, self).__init__()将工作,但我想了解TypeError。

PS: Joachim指出,作为一个新型的类实例并不等同于一个对象。我读过很多次相反的文章,因此感到困惑(基于对象实例测试的新型类实例测试示例:https://stackoverflow.com/revisions/2655651/3)。


当前回答

好吧,这是常见的“super()不能用于旧式类”。

然而,重要的一点是,正确的测试“这是一个新风格的实例(即对象)吗?

>>> class OldStyle: pass
>>> instance = OldStyle()
>>> issubclass(instance.__class__, object)
False

而不是(在问题中):

>>> isinstance(instance, object)
True

对于类,正确的“这是一种新型类吗”测试是:

>>> issubclass(OldStyle, object)  # OldStyle is not a new-style class
False
>>> issubclass(int, object)  # int is a new-style class
True

The crucial point is that with old-style classes, the class of an instance and its type are distinct. Here, OldStyle().__class__ is OldStyle, which does not inherit from object, while type(OldStyle()) is the instance type, which does inherit from object. Basically, an old-style class just creates objects of type instance (whereas a new-style class creates objects whose type is the class itself). This is probably why the instance OldStyle() is an object: its type() inherits from object (the fact that its class does not inherit from object does not count: old-style classes merely construct new objects of type instance). Partial reference: https://stackoverflow.com/a/9699961/42973.

PS:新型类和老式类之间的区别还可以从以下方面看到:

>>> type(OldStyle)  # OldStyle creates objects but is not itself a type
classobj
>>> isinstance(OldStyle, type)
False
>>> type(int)  # A new-style class is a type
type

(老式的类不是类型,所以它们不能是它们实例的类型)。

其他回答

问题是super需要一个对象作为祖先:

>>> class oldstyle:
...     def __init__(self): self.os = True

>>> class myclass(oldstyle):
...     def __init__(self): super(myclass, self).__init__()

>>> myclass()
TypeError: must be type, not classobj

仔细观察就会发现:

>>> type(myclass)
classobj

But:

>>> class newstyle(object): pass

>>> type(newstyle)
type    

所以你的问题的解决方案是继承对象以及从HTMLParser。 但是要确保object在MRO类中排在最后:

>>> class myclass(oldstyle, object):
...     def __init__(self): super(myclass, self).__init__()

>>> myclass().os
True

正确的做法是在旧式的类中不继承object

class A:
    def foo(self):
        return "Hi there"

class B(A):
    def foo(self, name):
        return A.foo(self) + name

你也可以使用类TextParser(HTMLParser, object):。这使得TextParser成为一个新风格的类,并且可以使用super()。

好吧,这是常见的“super()不能用于旧式类”。

然而,重要的一点是,正确的测试“这是一个新风格的实例(即对象)吗?

>>> class OldStyle: pass
>>> instance = OldStyle()
>>> issubclass(instance.__class__, object)
False

而不是(在问题中):

>>> isinstance(instance, object)
True

对于类,正确的“这是一种新型类吗”测试是:

>>> issubclass(OldStyle, object)  # OldStyle is not a new-style class
False
>>> issubclass(int, object)  # int is a new-style class
True

The crucial point is that with old-style classes, the class of an instance and its type are distinct. Here, OldStyle().__class__ is OldStyle, which does not inherit from object, while type(OldStyle()) is the instance type, which does inherit from object. Basically, an old-style class just creates objects of type instance (whereas a new-style class creates objects whose type is the class itself). This is probably why the instance OldStyle() is an object: its type() inherits from object (the fact that its class does not inherit from object does not count: old-style classes merely construct new objects of type instance). Partial reference: https://stackoverflow.com/a/9699961/42973.

PS:新型类和老式类之间的区别还可以从以下方面看到:

>>> type(OldStyle)  # OldStyle creates objects but is not itself a type
classobj
>>> isinstance(OldStyle, type)
False
>>> type(int)  # A new-style class is a type
type

(老式的类不是类型,所以它们不能是它们实例的类型)。

Super()只能在新型类中使用,这意味着根类需要从'object'类继承。

例如,顶级类需要是这样的:

class SomeClass(object):
    def __init__(self):
        ....

not

class SomeClass():
    def __init__(self):
        ....

所以,解决方案是直接调用父类的init方法,像这样:

class TextParser(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.all_data = []