super () вызывает «TypeError: должен быть типом, а не classobj» для класса нового стиля

335

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

Эрик О Лебиго
источник
3
Спасибо за ваш вопрос и ответ. Интересно, почему в 2.7 super.__doc__ничего не говорится о старом и новом стиле!
Кельвин
Спасибо. :) Строки документации обычно содержат меньше информации, чем полная HTML-версия документации. Тот факт, что super()работает только для классов (и объектов) нового стиля, упоминается в документе HTML ( docs.python.org/library/functions.html#super ).
Эрик О Лебигот
1
возможный дубликат python super () поднимает TypeError! Зачем?
Пользователь
Это не дубликат (см. Обновленный вопрос и принятый ответ).
Эрик О Лебиго

Ответы:

246

Хорошо, это обычно " 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

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

Эрик О Лебиго
источник
11
И это одна из причин, по которой у нас теперь есть Python 3.
Стивен Румбальски
2
Кстати: (Oldstyle().__class__ is Oldstyle)этоTrue
Тино
2
@Tino: действительно, но смысл в OldStyle().__class__том, чтобы показать, как проверить, происходит ли объект ( OldStyle()) из класса старого стиля. Имея в виду только классы нового стиля, можно было бы испытать искушение сделать тест isinstance(OldStyle(), object)вместо этого.
Эрик О Лебигот
27
Это нелепо, от того, какая часть стандартной библиотеки Python, все еще в 2.7.x, не наследуется object, таким образом, облажаясь прокси.
Ник Бастин
2
@NickBastin - это не совпадение. Это все для того, чтобы подтолкнуть всех в Python 3. Где «все уже хорошо». Но - будьте бдительны - это всего лишь приманка и рубильник.
Томаш Гандор
204

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 = []
Колин Су
источник
8
Для меня я должен был сделать это: HTMLParser .__ init __ (self) Мне интересно, сработал ли ваш последний пример?
шимпанзе
1
@EOL Что значит? Джефф только что указал, что код, приведенный в этом ответе, неверен из-за отсутствия selfпараметра в HTMLParser.__init__()вызове.
Петр Доброгост
1
@PiotrDobrogost: Извините, мое замечание было об ответе LittleQ, а не о (хорошем) замечании Джеффпа.
Эрик О Лебиго
1
@jeffp извините, это опечатка, я просто набрал его на SO, но не проверял, моя вина. спасибо за исправление
Колин Су
1
Upvote для исправления, которое работает с существующим кодом, таким как логирование. Форматирование в python2.6
Дэвид Рейнольдс
28

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

Валентин Лоренц
источник
Хороший ответ от меня, так как добавление наследования от объекта - хорошая идея. (Тем не менее, этот ответ не затрагивает проблему понимания ошибки типа вопроса.)
Эрик Лебиго,
23

Проблема в том, что 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
user2070206
источник
Действительные баллы, но они уже есть в предыдущих ответах. Кроме того, вместо проверки type(myclass)важно, myclassнаследовать ли от объекта (т. Е. isinstance(myclass, object)Является ли оно истинным - это ложное).
Эрик О Лебигот
17

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

Что isinstanceкасается вашей проверки , я сделал быстрый тест в ipython:

В [1]: класс А:
   ...: проходить
   ...: 

В [2]: isinstance (A, объект)
Out [2]: True

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

Какой-то программист чувак
источник
2
Я считаю, что правильный тест должен быть isinstance(A(), object), а не isinstance(A, object)нет? С последним, вы проверяете ли класс A является object, в то время как вопрос , является ли случаи из Aявляются object, не так ли?
Эрик О Лебиго
5
PS: лучший тест, кажется issubclass(HTMLParser, object), возвращает False.
Эрик О Лебигот
5

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

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

class B(A):
    def foo(self, name):
        return A.foo(self) + name
Джейкоб Абрахам
источник
1
В вопросе этот метод уже упоминался: проблема в том, чтобы понять, почему было выдано сообщение об ошибке, а не чтобы оно исчезло (в частности, таким образом).
Эрик О Лебиго
Кроме того, это решение не будет работать в случае, если мы хотим вызвать экземпляр класса, Aкоторый хранит состояние.
ташухка
0

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

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

qwerty_so
источник