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

Posted

技术标签:

【中文标题】super() 为新型类引发“TypeError:必须是类型,而不是 classobj”【英文标题】:super() raises "TypeError: must be type, not classobj" for new-style class 【发布时间】:2012-03-30 17:08:59 【问题描述】:

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

*** 上有一个类似的问题: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://***.com/revisions/2655651/3)。

【问题讨论】:

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

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

根据需要将解析结果返回给我。

【讨论】:

【参考方案2】:

问题是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) 是否为真——它是否为假)。【参考方案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

关键点 是,对于老式类,实例的 和它的类型 是不同的。这里,OldStyle().__class__OldStyle,它不继承自 object,而 type(OldStyle())instance 类型,确实object 继承。基本上,旧式类只创建 instance 类型的对象(而新式类创建类型是类本身的对象)。这可能就是为什么实例OldStyle()object 的原因:它的type() 继承自object(它的类确实 继承自object 的事实不算:old-样式类只是构造instance 类型的新对象)。部分参考:https://***.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。“一切都很好”。但是 - 警告购买者 - 这只是诱饵和开关。【参考方案4】:

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

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

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

【讨论】:

问题中已经提到了这个方法:问题是理解为什么会产生错误信息,而不是让它消失(特别是这种方式) . 此外,如果我们想要调用存储状态的类A 的实例,此解决方案将不起作用。【参考方案5】:

super() 只能在 new-style 类中使用,这意味着根类需要继承自 '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 = []

【讨论】:

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

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

【讨论】:

我的支持,因为添加对象的继承是个好主意。 (也就是说,这个答案并没有解决理解问题的 TypeError 的问题。)【参考方案7】:

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

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

在 [1] 中:A 类: ...: 经过 ...: 在 [2] 中:isinstance(A, object) 出[2]:真

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

【讨论】:

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

以上是关于super() 为新型类引发“TypeError:必须是类型,而不是 classobj”的主要内容,如果未能解决你的问题,请参考以下文章

TypeError: super(type, obj): obj must be an instance or subtype of type

为 scikit-learn 估计器子类化 XGBoostRegressor 会收到“TypeError:super() 不接受关键字参数”。

Python多重继承引发的问题——牛逼的super

TypeError: super(type, obj): obj 必须是类型的实例或子类型,仅当我的元类被导入时

在继承元类的类中调用 super 会引发错误

为啥类创建会引发错误