ChatGPT解决这个技术问题 Extra ChatGPT

super() 为新型类引发“TypeError:必须是类型,而不是 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() raises TypeError,错误的原因是用户类不是新式类。然而,上面的类是一个新式类,因为它继承自 object

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

我错过了什么?我如何在此处使用 super()

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

PS:Joachim 指出,作为一个 new-style-class 实例并不等同于作为一个 object。我多次阅读相反的内容,因此感到困惑(基于object实例测试的新型类实例测试示例:https://stackoverflow.com/revisions/2655651/3)。

感谢您的提问和回答。我想知道为什么 2.7 的 super.__doc__ 没有提到任何关于新旧风格的内容!
谢谢。 :) 与文档的完整 HTML 版本相比,文档字符串包含的信息通常更少。 HTML 文档 (docs.python.org/library/functions.html#super) 中提到了 super() 仅适用于新样式类(和对象)的事实。
这不是重复的(请参阅更新的问题和接受的答案)。

C
Community

好吧,这是通常的“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__OldStyle,它不继承自 object,而 type(OldStyle())instance 类型, 继承自 object。基本上,旧式类只创建 instance 类型的对象(而新式类创建其类型是类本身的对象)。这可能就是实例 OldStyle()object 的原因:它的 type() 继承自 object(它的类 notobject 继承的事实不算数:旧的-样式类仅构造 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

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


这也是我们现在拥有 Python 3 的原因之一。
顺便说一句:(Oldstyle().__class__ is Oldstyle)True
@Tino:确实,但 OldStyle().__class__ 的重点是展示如何测试 object (OldStyle()) 是否来自旧式类。如果只考虑新式类,可能会想用 isinstance(OldStyle(), object) 来进行测试。
荒谬的是,2.7.x 中的 python 标准库 still 没有从 object 继承,从而使您被代理搞砸了。
@NickBastin - 这不是巧合。这一切都是为了让每个人都进入 Python 3。“一切都很好”。但是 - 买者自负 - 这只是诱饵和开关。
C
Colin Su

super() 只能在新式类中使用,这意味着根类需要从“对象”类继承。

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

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

不是

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

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

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

对我来说,我必须这样做: HTMLParser.__init__(self) 我很好奇你的最后一个例子是否有效?
@EOL 是什么意思? jeffp 刚刚指出,由于 HTMLParser.__init__() 调用中缺少 self 参数,此答案中给出的代码是错误的。
@PiotrDobrogost:抱歉,我的评论是关于 LittleQ 的回答,而不是关于 jeffp 的(好)观点。
@jeffp 对不起,这是一个错字,我只是在 SO 上输入它但没有测试它,我的错。谢谢指正
支持适用于现有代码的修复,例如 python2.6 中的 logging.Formatter
E
Eric O Lebigot

您也可以使用 class TextParser(HTMLParser, object):。这使得 TextParser 成为一个 new-style 类,并且可以使用 super()


我赞成,因为添加来自对象的继承是个好主意。 (也就是说,这个答案并没有解决理解问题的 TypeError 的问题。)
E
Eric O Lebigot

问题是 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) 是否为真——它是否为假)。
S
Some programmer dude

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

关于您对 isinstance 的检查,我在 ipython 中做了一个快速测试:

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

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

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


我相信正确的测试应该是isinstance(A(), object),而不是isinstance(A, object),不是吗?对于后者,您正在测试 class A 是否是 object,而问题是 Ainstances 是否是 object,对吧?
PS:最好的测试似乎是 issubclass(HTMLParser, object),它返回 False。
J
Jacob Abraham

正确的做法是在不继承自“对象”的旧式类中如下所示

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

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

在问题中,已经提到了这种方法:问题是要了解为什么会产生错误消息,而不是让它消失(尤其是这种方式)。
此外,在我们想要调用存储状态的类 A 的实例的情况下,此解决方案将不起作用。
q
qwerty_so

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

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