以下对 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)。
super.__doc__
没有提到任何关于新旧风格的内容!
super()
仅适用于新样式类(和对象)的事实。
好吧,这是通常的“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
(它的类 not 从 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
(旧式类不是类型,因此它们不能是其实例的类型)。
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
参数,此答案中给出的代码是错误的。
您也可以使用 class TextParser(HTMLParser, object):
。这使得 TextParser
成为一个 new-style 类,并且可以使用 super()
。
问题是 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)
是否为真——它是否为假)。
如果您查看继承树(在 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
,而问题是 A
的 instances 是否是 object
,对吧?
issubclass(HTMLParser, object)
,它返回 False。
正确的做法是在不继承自“对象”的旧式类中如下所示
class A:
def foo(self):
return "Hi there"
class B(A):
def foo(self, name):
return A.foo(self) + name
A
的实例的情况下,此解决方案将不起作用。
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
(...)
只是根据需要让我返回解析结果。
(Oldstyle().__class__ is Oldstyle)
是True
OldStyle().__class__
的重点是展示如何测试 object (OldStyle()
) 是否来自旧式类。如果只考虑新式类,可能会想用isinstance(OldStyle(), object)
来进行测试。object
继承,从而使您被代理搞砸了。