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/reitions/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
BTW: (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) Мені цікаво, чи працював ваш останній приклад?
chaimp

1
@EOL Що означають? jeffp щойно зазначив, що код, вказаний у цій відповіді, неправильний через відсутність selfпараметра у HTMLParser.__init__()виклику.
Пьотр Доброгост

1
@PiotrDobrogost: Вибачте, моє зауваження стосувалося відповіді LittleQ, а не щодо (доброго) пункту jeffp.
Ерік О Лебігот

1
@jeffp Вибачте, це друкарська помилка, я просто набираю її так, але не перевіряю, моя вина. дякую за виправлення
Колін Су

1
Оновлення для виправлення, яке працює з існуючим кодом, таким як ведення журналів. Формат в 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]: речовина (A, об'єкт)
Вихід [2]: Правда

Навіть якщо клас є класом старого стилю, він все одно є екземпляром object.


2
Я вважаю, що правильним тестом має бути isinstance(A(), object), ні isinstance(A, object), ні? З останнім, ви перевіряєте клас A є object, в той час як питання , чи є випадки з Aє object, НЕ так?
Ерік О Лебігот

5
PS: Здається, найкращим тестом є те issubclass(HTMLParser, object), що повертає False.
Ерік О Лебігот

5

правильний спосіб зробити це буде наступним чином у класах старого стилю, який не успадковує від 'object'

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

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

1
У питанні цей метод вже згадується: проблема полягає в тому, щоб зрозуміти, чому було створено повідомлення про помилку, а не змусити його відходити (зокрема, таким чином).
Ерік О Лебігот

Крім того, це рішення не буде працювати в тому випадку, якщо ми хочемо викликати екземпляр класу, Aякий зберігає стан.
tashuhka

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.