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.