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() вызывает TypeError, где ошибка объясняется тем, что класс пользователя не является новым стилем класс. Однако класс выше является классом нового стиля, поскольку он наследует от object
:
>>> isinstance(HTMLParser(), object)
True
Что мне не хватает? Как я могу использовать super()
здесь?
Использование HTMLParser.__init__(self)
вместо super(TextParser, self).__init__()
будет работать, но я хотел бы понять TypeError.
PS: Йоахим указал, что это экземпляр класса нового стиля не эквивалентен object
. Я читал противоположное много раз, следовательно, мое замешательство (пример теста экземпляра класса нового стиля, основанного на тесте экземпляра object
: https://stackoverflow.com/revisions/2655651/3).
Ответы
Ответ 1
Хорошо, это обычный "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
, не учитывается: классы старого стиля просто создают новые объекты тип instance
). Частичная ссылка: fooobar.com/questions/21120/....
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
(классы старого стиля не являются типами, поэтому они не могут быть типом их экземпляров).
Ответ 2
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 = []
Ответ 3
Вы также можете использовать class TextParser(HTMLParser, object):
. Это делает TextParser
класс нового стиля, и super()
можно использовать.
Ответ 4
Если вы посмотрите на дерево наследования (в версии 2.6), HTMLParser
наследует от SGMLParser
, который наследует от ParserBase
, который не наследует от object
. То есть HTMLParser - это класс старого стиля.
О вашей проверке с помощью isinstance
, я сделал быстрый тест в ipython:
In [1]: class A:
...: pass
...:
In [2]: isinstance(A, object)
Out[2]: True
Даже если класс является классом старого стиля, он все еще является экземпляром object
.
Ответ 5
Проблема в том, что 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
Ответ 6
правильный способ сделать будет следующим образом в классах старого стиля, которые не наследуются от "объекта"
class A:
def foo(self):
return "Hi there"
class B(A):
def foo(self, name):
return A.foo(self) + name