super () вызывает "TypeError: должен быть типом, а не classobj" для класса нового стиля - PullRequest
331 голосов
/ 14 марта 2012

Следующее использование 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).

Ответы [ 7 ]

243 голосов
/ 15 марта 2012

Хорошо, обычно "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). Частичная ссылка: 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

(классы старого стиля , а не , поэтому они не могут быть типами своих экземпляров).

196 голосов
/ 14 марта 2012

super () может использоваться только в классах нового стиля, что означает, что корневой класс должен наследоваться от класса 'object'.

Например, верхний класс должен быть таким:

class SomeClass(object):
    def __init__(self):
        ....

not

class SomeClass():
    def __init__(self):
        ....

Таким образом, решение заключается в том, чтобы вызвать родительский метод init напрямую, например, так:

class TextParser(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.all_data = []
27 голосов
/ 04 августа 2012

Вы также можете использовать class TextParser(HTMLParser, object):. Это делает TextParser класс нового стиля , и можно использовать super().

21 голосов
/ 18 октября 2015

Проблема в том, что 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
17 голосов
/ 14 марта 2012

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

По поводу вашей проверки с помощью isinstance, я сделал быстрый тест в ipython:

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

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

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

5 голосов
/ 18 февраля 2014

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

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

class B(A):
    def foo(self, name):
        return A.foo(self) + name
0 голосов
/ 26 октября 2018

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

Только что вернул мне результаты анализа при необходимости.

...