web-dev-qa-db-fra.com

super () déclenche "TypeError: doit être un type, pas classobj" pour la classe new-style

L'utilisation suivante de super() génère une erreur TypeError: pourquoi?

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

Il existe une question similaire sur StackOverflow: Python super () déclenche TypeError , où l'erreur est expliquée par le fait que la classe user n'est pas une classe de style nouveau. Cependant, la classe ci-dessus est une classe de style nouveau, car elle hérite de object:

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

Qu'est-ce que je rate? Comment utiliser super() ici?

Utiliser HTMLParser.__init__(self) au lieu de super(TextParser, self).__init__() fonctionnerait, mais j'aimerais comprendre le TypeError.

PS: Joachim a souligné qu'être une instance de classe de style nouvelle n'équivaut pas à être un object. J'ai lu l'inverse plusieurs fois, d'où ma confusion (exemple de test d'instance de classe de style nouveau basé sur le test d'instance object: https://stackoverflow.com/revisions/2655651/ ) .

331
Eric O Lebigot

Bon, c'est l'habituel "super() ne peut pas être utilisé avec une classe de style ancien".

Cependant, le point important est que le test correct pour "est-ce une instance de nouveau style (objet)? " est

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

et non (comme dans la question):

>>> isinstance(instance, object)
True

Pour classes , le test correct "est-ce une classe de style nouveau" est:

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

Le point crucial est que, avec les classes de style ancien, la classe d'une instance et ses type sont distincts. Ici, OldStyle().__class__ est OldStyle, qui n’hérite pas de object, alors que type(OldStyle()) est le type instance, que fait hériter de object. En gros, une classe de style ancien crée simplement des objets de type instance (alors qu'une classe de style nouveau crée des objets dont le type est la classe elle-même). C'est probablement pourquoi l'instance OldStyle() est une object: sa type() hérite de object (le fait que sa classe ne ne soit pas hériter de object ne compte pas: les classes de style ancien construisent simplement de nouveaux objets de type instance). Référence partielle: https: //stackoverflow.com/a/9699961/42973 .

PS: La différence entre une classe de style nouveau et une classe de style ancien peut également être vue avec:

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

(Les classes de style ancien sont pas des types , elles ne peuvent donc pas être le type de leurs instances).

244
Eric O Lebigot

super () ne peut être utilisé que dans les classes new-style, ce qui signifie que la classe racine doit hériter de la classe 'object'.

Par exemple, la classe supérieure doit être comme ceci:

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

ne pas

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

La solution consiste donc à appeler directement la méthode init du parent, comme suit:

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

Vous pouvez également utiliser class TextParser(HTMLParser, object):. Cela fait de TextParser une classe new-style, et super() peut être utilisé.

28
Valentin Lorentz

Le problème est que super a besoin d'un object en tant qu'ancêtre:

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

En regardant de plus près, on trouve:

>>> type(myclass)
classobj

Mais:

>>> class newstyle(object): pass

>>> type(newstyle)
type    

La solution à votre problème serait donc d’hériter d’objet aussi bien que de HTMLParser. Mais assurez-vous que l'objet arrive en dernier dans les classes MRO:

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

>>> myclass().os
True
22
user2070206

Si vous regardez l'arbre de l'héritage (dans la version 2.6), HTMLParser hérite de SGMLParser qui hérite de ParserBase qui ne fait pas hérite de object. C'est à dire. HTMLParser est une classe de style ancien.

A propos de votre vérification avec isinstance, j’ai fait un test rapide sous ipython:

 Dans [1]: classe A: 
 ...: passer 
 ...: 
 
 Dans [2]: isinstance (A , objet) 
 Out [2]: Vrai 

Même si une classe est une classe de style ancien, elle reste une instance de object.

17

la bonne façon de faire sera comme suit dans les classes de style ancien qui n'héritent pas de 'objet'

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

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

FWIW et bien que je ne sois pas un Python gourou je me suis débrouillé avec ça

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

Je viens de recevoir les résultats d'analyse au besoin.

0
qwerty_so