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 ).
Эрик О Лебигот


Это не дубликат (см. Обновленный вопрос и принятый ответ).
Эрик О Лебиго

Ответы:


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

Действительные баллы, но они уже есть в предыдущих ответах. Кроме того, вместо проверки 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
(...)

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.