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).

6 320

6 ответов:

ладно, все как обычно"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

The ключевой момент заключается в том, что с старые классы, в класс экземпляра и его тип различны. Вот,OldStyle().__class__ и OldStyle, который не наследует от object, а type(OldStyle()) - это instance тип, который тут наследовать от object. В принципе, класс старого стиля просто создает объекты типа instance (в то время как класс нового стиля создает объекты, типом которых является сам класс). Вероятно, поэтому экземпляр OldStyle() это object: его type() наследует от object (тот факт, что его класс не наследовать от 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 () может использоваться только в классах нового стиля, что означает, что корневой класс должен наследовать от класса '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 = []

вы также можете использовать class TextParser(HTMLParser, object):. Это делает TextParser a новый стиль класс, а 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

если вы посмотрите на дерево наследования (в версии 2.6), HTMLParser наследует от SGMLParser, который наследует от ParserBase, который не наследует от object. То есть HTMLParser-это класс старого стиля.

о проверке с isinstance, Я сделал быстрый тест в IPython:

In [1]: class A:
   ...:     pass
   ...: 

In [2]: isinstance(A, object)
Out[2]: True

даже если класс является классом старого стиля, это все равно экземпляр object.

правильный способ сделать это будет следующим образом в классах старого стиля, который не наследуется от 'object'

class A:
    def foo(self):
        return "Hi there"

class B(A):
    def foo(self, name):
        return A.foo(self) + name