modo corretto di usare super (passaggio di argomenti)


99

Quindi stavo seguendo Super Considered Harmful di Python e sono andato a testare i suoi esempi.

Tuttavia, l' Esempio 1-3 , che dovrebbe mostrare il modo corretto di chiamare superquando si gestiscono __init__metodi che si aspettano argomenti diversi, flat-out non funziona.

Questo è quello che ottengo:

~ $ python example1-3.py 
MRO: ['E', 'C', 'A', 'D', 'B', 'object']
E arg= 10
C arg= 10
A
D arg= 10
B
Traceback (most recent call last):
  File "Download/example1-3.py", line 27, in <module>
    E(arg=10)
  File "Download/example1-3.py", line 24, in __init__
    super(E, self).__init__(arg, *args, **kwargs)
  File "Download/example1-3.py", line 14, in __init__
    super(C, self).__init__(arg, *args, **kwargs)
  File "Download/example1-3.py", line 4, in __init__
    super(A, self).__init__(*args, **kwargs)
  File "Download/example1-3.py", line 19, in __init__
    super(D, self).__init__(arg, *args, **kwargs)
  File "Download/example1-3.py", line 9, in __init__
    super(B, self).__init__(*args, **kwargs)
TypeError: object.__init__() takes no parameters

Sembra che esso objectstesso violi una delle migliori pratiche menzionate nel documento, ovvero i metodi che superdevono essere utilizzati *argse **kwargs.

Ora, ovviamente Mr. Knight si aspettava che i suoi esempi funzionassero, quindi è qualcosa che è stato cambiato nelle recenti versioni di Python? Ho controllato 2.6 e 2.7 e fallisce su entrambi.

Allora qual è il modo corretto per affrontare questo problema?


2
Il mio metodo preferito è: gerarchie di ereditarietà piatte e semplici.
— millimoose

19
Dovresti anche leggere "Super () considerato super di Python" per avere una visione equilibrata :)
— Björn Pollex

@ BjörnPollex: Grazie! Il vostro collegamento fornisce una risposta alla domanda: È possibile scrivere una "classe root" che eredita da object, e fa in modo di chiamata object's __init__correttamente.
— cha0site

4
Nota che __init__on objectignora silenziosamente tutti i parametri su Python 2.5. Questo è cambiato in Python 2.6.
— Wilfred Hughes

1
@ Wilfred: Ehi, grazie per aver risposto alla vera domanda! Ora so perché il saggio non è aggiornato!
— cha0site

Risposte:


103

A volte due classi possono avere alcuni nomi di parametri in comune. In tal caso, non puoi **kwargsestrarre o rimuovere le coppie chiave-valore da *args. Invece, puoi definire una Baseclasse che object, a differenza , assorbe / ignora gli argomenti:

class Base(object):
    def __init__(self, *args, **kwargs): pass

class A(Base):
    def __init__(self, *args, **kwargs):
        print "A"
        super(A, self).__init__(*args, **kwargs)

class B(Base):
    def __init__(self, *args, **kwargs):
        print "B"
        super(B, self).__init__(*args, **kwargs)

class C(A):
    def __init__(self, arg, *args, **kwargs):
        print "C","arg=",arg
        super(C, self).__init__(arg, *args, **kwargs)

class D(B):
    def __init__(self, arg, *args, **kwargs):
        print "D", "arg=",arg
        super(D, self).__init__(arg, *args, **kwargs)

class E(C,D):
    def __init__(self, arg, *args, **kwargs):
        print "E", "arg=",arg
        super(E, self).__init__(arg, *args, **kwargs)

print "MRO:", [x.__name__ for x in E.__mro__]
E(10)

rendimenti

MRO: ['E', 'C', 'A', 'D', 'B', 'Base', 'object']
E arg= 10
C arg= 10
A
D arg= 10
B

Si noti che affinché funzioni, Basedeve essere la penultima classe nell'MRO.


4
Non dovresti Basechiamare super(Base, self).__init__()?
— cha0site

4
Perché questo funzioni, Basedeve arrivare alla fine dell'MRO (eccetto object). La chiamata object.__init__non fa nulla, quindi va bene non chiamare super(Base, self).__init__(). In effetti, penso che potrebbe essere più chiaro non includere super(Base, self).__init__()per guidare a casa il punto che Baseè la fine della linea.
— unutbu

25

Se hai molta eredità (questo è il caso qui) ti suggerisco di passare tutti i parametri usando **kwargs, e poi poploro subito dopo averli usati (a meno che tu non ne abbia bisogno nelle classi superiori).

class First(object):
    def __init__(self, *args, **kwargs):
        self.first_arg = kwargs.pop('first_arg')
        super(First, self).__init__(*args, **kwargs)

class Second(First):
    def __init__(self, *args, **kwargs):
        self.second_arg = kwargs.pop('second_arg')
        super(Second, self).__init__(*args, **kwargs)

class Third(Second):
    def __init__(self, *args, **kwargs):
        self.third_arg = kwargs.pop('third_arg')
        super(Third, self).__init__(*args, **kwargs)

Questo è il modo più semplice per risolvere questo tipo di problemi.

third = Third(first_arg=1, second_arg=2, third_arg=3)

6

Come spiegato in super () considerato super di Python , un modo è far mangiare alla classe gli argomenti che richiede e passare il resto. Quindi, quando la catena di chiamate raggiunge object, tutti gli argomenti sono stati mangiati e object.__init__verranno chiamati senza argomenti (come previsto). Quindi il tuo codice dovrebbe assomigliare a questo:

class A(object):
    def __init__(self, *args, **kwargs):
        print "A"
        super(A, self).__init__(*args, **kwargs)

class B(object):
    def __init__(self, *args, **kwargs):
        print "B"
        super(B, self).__init__(*args, **kwargs)

class C(A):
    def __init__(self, arg, *args, **kwargs):
        print "C","arg=",arg
        super(C, self).__init__(*args, **kwargs)

class D(B):
    def __init__(self, arg, *args, **kwargs):
        print "D", "arg=",arg
        super(D, self).__init__(*args, **kwargs)

class E(C,D):
    def __init__(self, arg, *args, **kwargs):
        print "E", "arg=",arg
        super(E, self).__init__(*args, **kwargs)

print "MRO:", [x.__name__ for x in E.__mro__]
E(10, 20, 30)

Allora, di cosa E(arg = 10)? Non mi piace questo metodo, ho bisogno di conoscere l'MRO in anticipo per poterlo utilizzare. L'altro metodo in cui scrivo una "classe radice" che eredita da objectsembra molto più pulito.
— cha0site

Questo metodo è utile se le funzioni chiamate non condividono i nomi degli argomenti. Ammetto di non avere molta esperienza pratica con super, quindi questo approccio potrebbe effettivamente essere piuttosto teorico.
— Björn Pollex
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.