super()引发“TypeError:必须是type,而不是classobj”;新型课堂

下面使用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指出,作为一个新型类实例并不等同于object。我读过相反的内容很多次,因此我感到困惑(基于object实例测试的新型类实例测试示例:https://stackoverflow.com/revisions/2655651/3)。

143422 次浏览

如果你看一下继承树(在2.6版),HTMLParser继承自SGMLParser,后者继承自ParserBase,而继承自object。例如,HTMLParser是一个老式的类。

关于你用isinstance检查,我在ipython中做了一个快速测试:

In [1]: class A:
...:     pass
...:


In [2]: isinstance(A, object)
Out[2]: True

即使一个类是老式的类,它仍然是object的实例。

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

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

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

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

因此,解决方案是直接调用父类的初始化方法,就像这样:

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

好吧,这是通常的“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

OldStyle3是指在旧式类中,实例的OldStyle4与其OldStyle5是不同的。这里,OldStyle().__class__OldStyle,它不继承自object,而type(OldStyle())instance类型,OldStyle6继承自object。基本上,旧式类只创建类型为instance的对象(而新式类创建类型为类本身的对象)。这可能就是为什么实例OldStyle()是一个object:它的type()继承自object(它的类OldStyle7继承自object的事实不算数:旧式的类只构造类型为instance的新对象)。部分引用:OldStyle8。

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

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

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

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

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


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

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

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

但是:

>>> class newstyle(object): pass


>>> type(newstyle)
type

所以你的问题的解决方案将从object以及从HTMLParser继承。 但是要确保object在MRO:

类中排在最后
>>> class myclass(oldstyle, object):
...     def __init__(self): super(myclass, self).__init__()


>>> myclass().os
True

FWIW,虽然我不是Python大师,但我用这个解决了

>>> class TextParser(HTMLParser):
...    def handle_starttag(self, tag, attrs):
...        if tag == "b":
...            self.all_data.append("bold")
...        else:
...            self.all_data.append("other")
...
...
>>> p = TextParser()
>>> p.all_data = []
>>> p.feed(text)
>>> print p.all_data
(...)

刚把我需要的解析结果拿回来。