343 Stimmen

super() löst "TypeError: must be type, not classobj" für new-style class aus

Die folgende Verwendung von super() löst einen TypeError aus: warum?

>>> from  HTMLParser import HTMLParser
>>> class TextParser(HTMLParser):
...     def __init__(self):
...         super(TextParser, self).__init__()
...         self.all_data = []
...         
>>> TextParser()
(...)
TypeError: must be type, not classobj

Es gibt eine ähnliche Frage auf StackOverflow: Python super() löst TypeError aus Der Fehler ist darauf zurückzuführen, dass es sich bei der Benutzerklasse nicht um eine Klasse des neuen Stils handelt. Die obige Klasse ist jedoch eine Klasse des neuen Stils, da sie von object :

>>> isinstance(HTMLParser(), object)
True

Was übersehe ich? Wie kann ich die super() hier?

Verwendung von HTMLParser.__init__(self) 代わりに super(TextParser, self).__init__() funktionieren würde, aber ich würde gerne den TypeError verstehen.

PS: Joachim wies darauf hin, dass eine Instanz der neuen Stilklasse nicht gleichbedeutend ist mit einer object . Ich habe viele Male das Gegenteil gelesen, daher meine Verwirrung (Beispiel für einen Klasseninstanztest neuen Stils auf der Grundlage von object Instanztest: https://stackoverflow.com/revisions/2655651/3 ).

250voto

Eric O Lebigot Punkte 85676

Also gut, es ist das übliche " super() kann nicht mit einer Klasse alten Stils verwendet werden".

Der wichtigste Punkt ist jedoch, dass der richtige Test für "ist dies ein neuer Stil Instanz (d.h. Objekt)?" ist

>>> class OldStyle: pass
>>> instance = OldStyle()
>>> issubclass(instance.__class__, object)
False

und nicht (wie in der Frage):

>>> isinstance(instance, object)
True

Für Klassen ist der korrekte Test, ob es sich um eine Klasse neuen Stils handelt:

>>> issubclass(OldStyle, object)  # OldStyle is not a new-style class
False
>>> issubclass(int, object)  # int is a new-style class
True

Les entscheidender Punkt ist, dass bei Klassen alten Stils die Klasse einer Instanz und deren Typ sind unterschiedlich. Hier, OldStyle().__class__ es OldStyle , die nicht von object , während type(OldStyle()) ist die instance Typ, der fait erben von object . Im Grunde erzeugt eine Klasse alten Stils nur Objekte des Typs instance (wohingegen eine Klasse neuen Typs Objekte erzeugt, deren Typ die Klasse selbst ist). Dies ist wahrscheinlich der Grund, warum die Instanz OldStyle() ist ein object : seine type() erbt von object (die Tatsache, dass seine Klasse no erben von object zählt nicht: Klassen alten Stils konstruieren lediglich neue Objekte des Typs instance ). Teilweise Referenz: https://stackoverflow.com/a/9699961/42973 .

PS: Der Unterschied zwischen einer Klasse des neuen Stils und einer Klasse des alten Stils lässt sich auch an der Form erkennen:

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

(Klassen im alten Stil sind no Typen, sie können also nicht der Typ ihrer Instanzen sein).

212voto

Colin Su Punkte 4564

Super() kann nur in den New-Style-Klassen verwendet werden, d. h. die Root-Klasse muss von der "Objekt"-Klasse erben.

Zum Beispiel muss die Spitzenklasse so aussehen:

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

no

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

Die Lösung ist also, dass der Aufruf der übergeordneten init Methode direkt, etwa auf diese Weise:

class TextParser(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.all_data = []

29voto

Valentin Lorentz Punkte 9234

Sie können auch Folgendes verwenden class TextParser(HTMLParser, object): . Dies macht TextParser a neuartiges Klasse, und super() verwendet werden können.

25voto

user2070206 Punkte 371

Das Problem ist, dass super braucht eine object als Vorfahre:

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

Bei näherer Betrachtung wird man feststellen:

>>> type(myclass)
classobj

Aber:

>>> class newstyle(object): pass

>>> type(newstyle)
type    

Die Lösung für Ihr Problem wäre also, sowohl von object als auch von HTMLParser zu erben. Aber stellen Sie sicher, dass object in der Klasse MRO an letzter Stelle steht:

>>> class myclass(oldstyle, object):
...     def __init__(self): super(myclass, self).__init__()

>>> myclass().os
True

17voto

Some programmer dude Punkte 377919

Wenn Sie sich den Vererbungsbaum (in Version 2.6) ansehen, HTMLParser erbt von SGMLParser die erbt von ParserBase que nicht erbt von object . D.h. HTMLParser ist eine Klasse alten Stils.

Über Ihre Überprüfung mit isinstance Ich habe einen schnellen Test in ipython durchgeführt:

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

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

Selbst wenn eine Klasse eine Klasse alten Stils ist, ist sie immer noch eine Instanz von object .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X