2012-03-14 17 views
306

Il seguente uso di super() solleva un'eccezione TypeError: perché?super() solleva "TypeError: deve essere type, non classobj" per la classe new-style

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

C'è una domanda simile su StackOverflow: Python super() raises TypeError, dove l'errore si spiega con il fatto che la classe utente non è un classe di nuovo stile. Tuttavia, la classe di cui sopra è un classe di nuovo stile, visto che eredita da object:

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

Che cosa mi manca? Come posso usare super(), qui?

Utilizzare HTMLParser.__init__(self) invece di super(TextParser, self).__init__() funzionerebbe, ma mi piacerebbe capire l'errore TypeError.

PS: Joachim ha sottolineato che essere un'istanza di classe new-style non equivale a object. Ho letto il contrario molte volte, quindi la mia confusione (esempio di test di istanze di classi di nuovo stile basato sul test di istanza object: https://stackoverflow.com/revisions/2655651/3).

+3

Grazie per la vostra domanda e risposta. Mi chiedo perché il 'super .__ doc__' di 2.7 non menzioni nulla di vecchio vs nuovo stile! – Kelvin

+0

Grazie. :) Le docstring in genere contengono meno informazioni rispetto alla versione HTML completa della documentazione. Il fatto che 'super()' funzioni solo per classi (e oggetti) di nuovo stile è menzionato nel documento HTML (http: //docs.python.org/library/functions.html # super). – EOL

+1

possibile duplicato di [python super() solleva TypeError! Perché?] (Http://stackoverflow.com/questions/489269/python-super-raises-typeerror-why) – user

risposta

231

OK, è il solito "super() non può essere utilizzato con una classe vecchio stile ".

Tuttavia, il punto importante è che il criterio corretto per "è questo un nuovo stile esempio (cioè oggetto)?"È

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

e non (come nella questione):

>>> isinstance(instance, object) 
True 

Per classi, il corretto "Si tratta di una classe di nuovo stile" di prova è:

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

Il punto cruciale è quello con classi vecchio stile, la classe di un'istanza e il suo tipo sono distinti. Qui, OldStyle().__class__ è OldStyle, che non eredita da object, mentre type(OldStyle()) è il tipo instance, che fa ereditare da object. Fondamentalmente, una classe vecchio stile crea solo oggetti di tipo instance (mentre una classe di nuovo stile crea oggetti il ​​cui tipo è la classe stessa). Questo è probabilmente il motivo per l'istanza OldStyle() è un object: i suoi type() eredita dal object (il fatto che la sua classe non non ereditare da object non conta: le classi vecchio stile semplicemente costruiscono nuovi oggetti di tipo instance). Riferimento parziale: https://stackoverflow.com/a/9699961/42973.

PS: La differenza tra una classe di nuovo stile e un vecchio-stile si può anche essere visto con:

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

(classi vecchio stile sono non tipi, quindi non possono essere il tipo delle loro istanze).

+10

E questo è uno dei motivi per cui ora abbiamo Python 3. –

+2

BTW: '(Oldstyle() .__ class__ is Oldstyle)' is ' True' – Tino

+2

@Tino: in effetti, il punto di 'OldStyle() .__ class__' è mostrare come testare se un * oggetto * (' OldStyle() ') proviene da una classe vecchio stile. Con solo lezioni di stile nuovo in mente, si potrebbe essere tentati di fare il test con 'isinstance (OldStyle(), object)'. – EOL

17

Se si guarda l'albero di ereditarietà (in versione 2.6), HTMLParser eredita da SGMLParser che eredita da ParserBase che non eredita da object. Cioè HTMLParser è una classe vecchio stile.

Circa il vostro controllo con isinstance, ho fatto un test rapido in ipython:

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

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

Anche se una classe è classe di vecchio stile, è ancora un esempio di object.

+2

Credo che il test corretto dovrebbe essere 'isinstance (A(), object)', non 'isinstance (A, object)', no? Con quest'ultimo, si sta verificando se la * classe * 'A' sia un' oggetto', mentre la domanda è se * istanze * di 'A' siano un' oggetto', giusto? – EOL

+5

PS: il test migliore sembra essere "issubclass (HTMLParser, object)", che restituisce False. – EOL

180

super() può essere utilizzato solo nelle classi di nuovo stile, il che significa che la classe radice deve ereditare dalla classe 'oggetto'.

Ad esempio, la classe ha bisogno di essere in questo modo:

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

non

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

Quindi, la soluzione è quella chiamata metodo init del genitore direttamente, come in questo modo:

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

Per me, ho dovuto fare questo: HTMLParser .__ init __ (self) Sono curioso di sapere se il tuo ultimo esempio ha funzionato? – chaimp

+0

La domanda riguarda * perché * tu * hai * per farlo (vedi la prima PS). – EOL

+1

@EOL Cosa significa? jeffp ha appena fatto notare che il codice dato in questa risposta è sbagliato a causa della mancanza del parametro 'self' nella chiamata' HTMLParser .__ init __() '. –

24

È inoltre possibile utilizzare class TextParser(HTMLParser, object):. Questo rende TextParser a nuovo stile classe e super() può essere utilizzato.

+0

Un voto positivo da parte mia, in quanto l'aggiunta dell'eredità dall'oggetto è una buona idea. (Ciò detto, questa risposta non affronta la questione della comprensione dell'errore TypeError della domanda.) – EOL

3

il modo corretto di fare non sarà come in seguito nelle classi vecchio stile, che non ereditano da 'oggetto'

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

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

Nella domanda, questo metodo è già menzionato: il problema è quello di * capire * perché è stato prodotto un messaggio di errore, non per farlo andare lontano (in particolare in questo modo). – EOL

+0

Inoltre, questa soluzione non funzionerà nel caso in cui vogliamo chiamare una istanza della classe 'A' che memorizza uno stato. – tashuhka

17

Il problema è che ha bisogno di un superobject come un antenato:

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

A ben un esame trova:

>>> type(myclass) 
classobj 

Ma:

>>> class newstyle(object): pass 

>>> type(newstyle) 
type  

Quindi la soluzione al vostro problema sarebbe quello di ereditare da oggetti come pure da HTMLParser. Ma assicuratevi che l'oggetto arrivi per ultimo nelle classi MRO:

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

>>> myclass().os 
True 
+0

Punti validi, ma sono già in risposte precedenti. Inoltre, invece di controllare 'type (myclass)', ciò che importa è se 'myclass' erediti dall'oggetto (cioè se' isinstance (myclass, object) 'sia vero-è falso). – EOL