15

Ho creato una classe che suddivide le sottoclassi ListView e due mixin personalizzati che hanno implementato una funzione get_context_data. Ho voluto ignorare questa funzione sulla classe bambino:Override della funzione di ereditarietà multiplo di Python e ListView in django

from django.views.generic import ListView 

class ListSortedMixin(object): 
    def get_context_data(self, **kwargs): 
     print 'ListSortedMixin' 
     return kwargs 

class ListPaginatedMixin(object): 
    def get_context_data(self, **kwargs): 
     print 'ListPaginatedMixin' 
     return kwargs 

class MyListView(ListSortedMixin, ListPaginatedMixin, ListView): 
    def get_context_data(self, **context): 
    super(ListSortedMixin,self).get_context_data(**context) 
    super(ListPaginatedMixin,self).get_context_data(**context) 
    return context 

Quando eseguire MyListView esso stampa solo "ListSortedMixin". Per qualche ragione Python sta eseguendo ListSortedMixin.get_context_data al posto di MyListView.get_context_data. Perché?

Se si modifica l'ordine di ereditarietà su ListPaginatedMixin, ListSortedMixin, ListView, viene eseguito ListPaginatedMixin.get_context_data.

Come si ignora la funzione get_context_data?

risposta

16

Questa è una vecchia domanda, ma credo che la risposta non sia corretta. C'è un errore nel tuo codice.Esso dovrebbe contenere:

class MyListView(ListSortedMixin, ListPaginatedMixin, ListView): 
    def get_context_data(self, **context): 
     super(MyListView,self).get_context_data(**context) 
     return context 

L'ordine in cui sarà chiamato il get_context_data segue lo stesso ordine specificato nella dichiarazione di MyListView. Si noti che l'argomento di super è MyListView e non le super classi.

UPDATE:

mi mancava che i mixin non chiamano eccellente. Loro dovrebbero. Sì, anche se ereditano da oggetto, perché super-chiama il metodo successivo nella MRO, non necessariamente il genitore di classe è in

from django.views.generic import ListView 

class ListSortedMixin(object): 
    def get_context_data(self, **kwargs): 
     print 'ListSortedMixin' 
     return super(ListSortedMixin,self).get_context_data(**context) 

class ListPaginatedMixin(object): 
    def get_context_data(self, **kwargs): 
     print 'ListPaginatedMixin' 
     return super(ListPaginatedMixin,self).get_context_data(**context) 

class MyListView(ListSortedMixin, ListPaginatedMixin, ListView): 
    def get_context_data(self, **context): 
     return super(MyListView,self).get_context_data(**context) 

Per MyListView l'MRO è quindi:.

  1. MyListView
  2. ListSortedMixin
  3. ListPaginatedMixin
  4. ListView
  5. Tutto ciò che è al di sopra ListView ... n. oggetto

Chiamarli uno per uno può funzionare, ma non è il modo in cui è stato progettato.

UPDATE 2

Copia e incolla esempio per dimostrare il mio punto.

class Parent(object): 
    def get_context_data(self, **kwargs): 
     print 'Parent' 

class ListSortedMixin(object): 
    def get_context_data(self, **kwargs): 
     print 'ListSortedMixin' 
     return super(ListSortedMixin,self).get_context_data(**kwargs) 

class ListPaginatedMixin(object): 
    def get_context_data(self, **kwargs): 
     print 'ListPaginatedMixin' 
     return super(ListPaginatedMixin,self).get_context_data(**kwargs) 

class MyListView(ListSortedMixin, ListPaginatedMixin, Parent): 
    def get_context_data(self, **kwargs): 
     return super(MyListView,self).get_context_data(**kwargs) 


m = MyListView() 
m.get_context_data(l='l') 
+0

In questo caso 'super (MyListView, self) .get_context_data (** context)' è lo stesso di 'ListSortedMixin.get_context_data (self, ** context)'. Penso che [la risposta precedente] (http://stackoverflow.com/a/9939867/959819) sia corretta: devo chiamare le funzioni dei genitori uno per uno. –

+0

Il problema è che i tuoi mixin non chiamano super. Anche se i mixin ereditano da 'object' dovrebbero chiamare super. Super delegati all'oggetto successivo nell'MRO (ordine di risoluzione dei metodi), che dipende dall'ordine in cui sono stati specificati nella dichiarazione di MyListView. Aggiornerò la mia risposta sopra per renderla più chiara. –

+0

Esattamente. Pertanto il metodo chiamato nel tuo esempio è solo "ListSortedMixin". Devo chiamare manualmente tutte le funzioni dei genitori. –

10

Se quello che stai cercando di fare è chiamare i metodi sovrascritti in ordine fisso. Utilizzare questa sintassi:

class MyListView(ListSortedMixin, ListPaginatedMixin, ListView): 
    def get_context_data(self, **context): 
    ListSortedMixin.get_context_data(self, **context) 
    ListPaginatedMixin.get_context_data(self, **context) 
    return context 

In questo caso, Super non funziona. Vedere il manuale per super(type[, object]):

restituire un oggetto proxy che il metodo delegati chiama a un genitore o classe fratello del tipo. Questo è utile per accedere ai metodi ereditati che sono stati sovrascritti in una classe. L'ordine di ricerca è uguale a quello utilizzato da getattr(), tranne per il fatto che il tipo stesso viene saltato.

Esistono due casi di utilizzo tipici per super. In una gerarchia di classi con ereditarietà singola , super può essere utilizzato per fare riferimento alle classi parent senza denominarli esplicitamente, rendendo così il codice più mantenibile. Questo uso è strettamente correlato all'uso di super in altri linguaggi di programmazione .

Il secondo caso d'uso è supportare l'ereditarietà multipla cooperativa in un ambiente di esecuzione dinamico. Questo caso d'uso è univoco per Python e non viene trovato in lingue o lingue compilate staticamente che solo supporta l'ereditarietà singola. Ciò consente di implementare i "diagrammi a diamante" in cui più classi base implementano lo stesso metodo . Il buon design impone che questo metodo abbia la stessa chiamata firma in ogni caso (perché l'ordine delle chiamate è determinato al runtime , perché quell'ordine si adatta alle modifiche nella gerarchia di classi, e perché quell'ordine può includere classi di pari livello sconosciute prima del runtime).

Quindi argomento di super è la classe di cui si desidera ottenere il proxy di classe padre o fratello. super(ListSortedMixin,self).get_context_data(**context) non chiama necessariamente get_context_data di ListSortedMixin. Dipende dall'ordine di risoluzione dei metodi (MRO), che è possibile ottenere usando print MyListView.__mro__

Quindi get_context_data chiamerà get_context_data di genitore o fratello. L'ordine di esecuzione si adatta alle modifiche nella gerarchia di classi e poiché tale ordine può includere classi di pari livello sconosciute prima del runtime.

+0

Grazie per la risposta veloce! Bella spiegazione, ora capisco :) –