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