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


335

以下用法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)。


3
感谢您的提问和回答。我不知道为什么2.7 super.__doc__没有提及新旧样式!
开尔文2012年

谢谢。:)文档字符串通常比完整的HTML版本的文档包含更少的信息。这一事实super()仅适用于新样式类(和对象)的工作原理是在HTML文档(提到docs.python.org/library/functions.html#super)。
Eric O Lebigot 2012年


这不是重复的内容(请参阅更新后的问题和接受的答案)。
Eric O Lebigot 2014年

Answers:


246

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

关键的一点是,与老式类的的实例和它的类型是不同的。在这里,OldStyle().__class__is OldStyle,它不继承自object,而type(OldStyle())is instance类型,它确实继承自object。基本上,旧式类仅创建类型的对象instance(而新式类将创建类型为类本身的对象)。这大概就是为什么实例OldStyle()object:其type()从继承object(事实上,它的类并没有继承object不计数:老式类只是构建类型的新对象instance)。部分参考: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

(旧式类不是类型,因此它们不能是其实例的类型)。


11
这是原因之一,我们现在拥有的Python 3
史蒂芬Rumbalski

2
BTW:(Oldstyle().__class__ is Oldstyle)True
Tino 2012年

2
@Tino:的确如此,但重点OldStyle().__class__是要演示如何测试对象OldStyle())是否来自旧类。只考虑新式类,可能会诱使您使用该类进行测试isinstance(OldStyle(), object)
Eric O Lebigot

27
荒谬的是,2.7.x中仍然没有继承python标准库中的多少object,从而使您不知所措。
尼克·巴斯汀,

2
@NickBastin-这不是巧合。所有这些都是为了将​​所有人推向Python3。在这里“一切都很好”。但是-警告购买者-它只是诱饵和开关。
Tomasz Gandor 2014年

204

super()仅可用于新型类,这意味着根类需要从'object'类继承。

例如,顶级类需要像这样:

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

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

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

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

8
对我来说,我必须这样做:HTMLParser .__ init __(self)我很好奇您的上一个示例是否有效?
champ 2012年

1
@EOL是什么意思?jeffp刚刚指出,由于调用中缺少self参数,此答案中给出的代码是错误的HTMLParser.__init__()
Piotr Dobrogost

1
@PiotrDobrogost:对不起,我的评论是关于LittleQ的答案,而不是关于jeffp的(好)观点。
Eric O Lebigot

1
@jeffp抱歉,这是一个错字,我只是在SO上打出来,但没有测试,我的错。感谢您的更正
Colin Su

1
支持可与现有代码配合使用的修复程序,例如python2.6中的logging.Formatter
David Reynolds

28

您也可以使用class TextParser(HTMLParser, object):。这将创建TextParser一个新样式的类,并且super()可以使用。


我的反对,因为添加对象的继承是一个好主意。(也就是说,此答案并未解决理解该问题的TypeError的问题。)
Eric O Lebigot

23

问题是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    

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

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

>>> myclass().os
True

有效点,但是它们已经在先前的答案中。同样,代替检查type(myclass),重要的是是否myclass从对象继承(即,是否isinstance(myclass, object)为真-为假)。
Eric O Lebigot

17

如果您查看继承树(在2.6版中),则HTMLParser继承自SGMLParser,继承自ParserBase而不继承自object。即HTMLParser是一个老式的类。

关于您的检查isinstance,我在ipython中进行了快速测试:

在[1]中:A类:
   ...:通过
   ...: 

在[2]中:isinstance(A,object)
出[2]:是

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


2
我认为正确的测试应该是isinstance(A(), object),不是isinstance(A, object)吗?对于后者,你正在测试是否 A是一个object,而问题是,是否实例A是一个object,对不对?
Eric O Lebigot 2012年

5
PS:最好的测试似乎是issubclass(HTMLParser, object),它返回False。
Eric O Lebigot 2012年

5

正确的方法是在不继承自'object'的旧类中执行以下操作

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

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

1
在问题中,已经提到了这种方法:问题是要理解为什么会产生错误消息,而不是使错误消息消失(尤其是以这种方式)。
Eric O Lebigot 2014年

此外,在我们要调用A存储状态的类的实例的情况下,该解决方案将不起作用。
tashuhka 2015年

0

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
(...)

只是让我根据需要返回解析结果。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.