关于python:super()为新样式类引发must be type, not classobj” for new-style class

super() raises “TypeError: must be type, not classobj” for new-style class

下面使用super()会导致类型错误:为什么?

1
2
3
4
5
6
7
8
9
>>> 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

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

我错过了什么?这里我怎么用super()

使用HTMLParser.__init__(self)而不是super(TextParser, self).__init__()是可行的,但我想了解类型错误。

ps:joachim指出,作为一个新的样式类实例并不等同于一个object。我读了很多次相反的内容,因此我感到困惑(基于object实例测试的新型类实例测试示例:https://stackoverflow.com/revisions/2655651/3)。


好吧,这是通常的"super()不能与旧样式类一起使用"。

但是,重要的一点是正确测试"这是一个新的样式的实例(即对象)"?是

1
2
3
4
>>> class OldStyle: pass
>>> instance = OldStyle()
>>> issubclass(instance.__class__, object)
False

而不是(如问题所述):

1
2
>>> isinstance(instance, object)
True

对于类,正确的"这是新样式类吗"测试是:

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

关键点是,对于旧样式类,实例的类及其类型是不同的。此处,OldStyle().__class__OldStyle型,不继承objecttype(OldStyle())instance型,不继承object。基本上,旧样式类只创建instance类型的对象(而新样式类创建的对象类型是类本身)。这可能就是实例OldStyle()object的原因:它的type()继承自object(它的类不继承自object的事实不算在内:旧样式类只构造instance类型的新对象)。部分参考:https://stackoverflow.com/a/9699961/42973。

附言:一个新风格的班级和一个旧风格的班级之间的区别也可以通过以下方式看到:

1
2
3
4
5
6
>>> 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"类继承。

例如,顶级需要如下所示:

1
2
3
class SomeClass(object):
    def __init__(self):
        ....

1
2
3
class SomeClass():
    def __init__(self):
        ....

因此,解决方案是直接调用父级的init方法,如下所示:

1
2
3
4
class TextParser(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.all_data = []


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


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

1
2
3
4
5
6
7
8
>>> 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

仔细研究后,我们发现:

1
2
>>> type(myclass)
classobj

但是:

1
2
3
4
>>> class newstyle(object): pass

>>> type(newstyle)
type

因此,解决问题的方法是从对象继承,也从HTMLParser继承。但是确保对象在类mro中排在最后:

1
2
3
4
5
>>> class myclass(oldstyle, object):
...     def __init__(self): super(myclass, self).__init__()

>>> myclass().os
True


如果您查看继承树(在版本2.6中),那么HTMLParser继承自SGMLParser,而ParserBase继承自object,而ParserBase继承自object。也就是说,htmlparser是一个老式的类。

关于你和isinstance的核对,我在ipython做了一个快速测试:

1
2
3
4
5
6
In [1]: class A:
   ...:     pass
   ...:

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

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


正确的方法是在不从"object"继承的旧样式类中执行以下操作

1
2
3
4
5
6
7
class A:
    def foo(self):
        return"Hi there"

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


尽管我不是Python大师,但我还是挺过来了。

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> 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
(...)

根据需要把分析结果给我。