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中做了一个快速测试:
即使一个类是老式类,它仍然是object
的一个实例。
【讨论】:
我认为正确的测试应该是isinstance(A(), object)
,而不是isinstance(A, object)
,不是吗?对于后者,您正在测试 class A
是否是 object
,而问题是 A
的 instances 是否是 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() 不接受关键字参数”。