【问题标题】:super() raises "TypeError: must be type, not classobj" for new-style classsuper() 为新型类引发“TypeError:必须是类型,而不是 classobj”
【发布时间】: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

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() 仅适用于新型类(和对象)这一事实。
  • 这不是重复的(请参阅更新的问题和接受的答案)。

标签: python superclass super typeerror


【解决方案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://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。“一切都很好”。但是 - 警告购买者 - 这只是诱饵和开关。
    【解决方案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。
    猜你喜欢
    • 2020-11-09
    • 1970-01-01
    • 2017-10-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多