15

Utworzono klasę podklasową ListView i dwie niestandardowe składanki, które zaimplementowały funkcję get_context_data. Chciałem, aby zastąpić tę funkcję w klasie dziecko:Nadpisywanie funkcji wielu dziedziczeń w języku Python i ListView w 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 

Kiedy wykonać MyListView to tylko drukuje "ListSortedMixin". Z jakiegoś powodu Python wykonuje ListSortedMixin.get_context_data zamiast MyListView.get_context_data. Czemu?

Jeśli zmienię kolejność dziedziczenia na ListPaginatedMixin, ListSortedMixin, ListView, zostanie wykonana ListPaginatedMixin.get_context_data.

Jak mogę przesłonić funkcję get_context_data?

Odpowiedz

16

To jest stare pytanie, ale uważam, że odpowiedź jest nieprawidłowa. W twoim kodzie jest błąd.Powinno być:

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

Kolejność, w jakiej get_context_data będzie nazwane następująco takiej samej kolejności jak określono w deklaracji MyListView. Zauważ, że argumentem super jest MyListView, a nie super klasy.

UPDATE:

brakowało mi się, że wstawek nie nazywają Super. Oni powinni. Tak, nawet jeśli one dziedziczą z obiektu, ponieważ bardzo wywołuje następną metodę w MRO, niekoniecznie rodzica klasie to jest w

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) 

Dla MyListView MRO jest potem.

  1. MyListView
  2. ListSortedMixin
  3. ListPaginatedMixin
  4. ListView
  5. Cokolwiek jest powyżej ListView ... n. obiekt

Dzwonienie do nich pojedynczo może działać, ale nie jest przeznaczone do użycia.

UPDATE 2

Kopiowanie i wklejanie przykład udowodnić mój punkt widzenia.

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

W tym przypadku 'super (MyListView, self) .get_context_data (kontekst **) jest taki sam jak' ListSortedMixin.get_context_data (self, ** context) '. Myślę, że [poprzednia odpowiedź] (http://stackoverflow.com/a/9939867/959819) jest poprawna: muszę wywoływać funkcje rodziców jeden po drugim. –

+0

Problem polega na tym, że Twoje mixiny nie nazywają się super. Nawet jeśli mixiny dziedziczą po 'obiekcie', powinny wywoływać super. Super delegaci do następnego obiektu w MRO (kolejność rozstrzygania metod), która zależy od kolejności, w jakiej się znajdowali są określone w deklaracji MyListView. Zaktualizuję powyższą odpowiedź, aby była bardziej przejrzysta. –

+0

Dokładnie. Dlatego wywoływana metoda w twoim przykładzie to tylko 'ListSortedMixin'. Muszę zadzwonić ręcznie wszystkie funkcje rodziców. –

10

Jeśli próbujesz zrobić, to wywołać nadpisane metody w ustalonej kolejności. Użyj tej składni:

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 

Super nie zadziała w tym przypadku. Zobacz instrukcję dla super(type[, object]):

Return obiekt proxy, który deleguje metoda nazywa się rodzica lub klasy rodzeństwem typu. Jest to przydatne do uzyskiwania dostępu do odziedziczonych metod, które zostały nadpisane w klasie. Kolejność wyszukiwania jest taka sama, jak w przypadku getattr(), z wyjątkiem tego, że sam typ jest pomijany.

Istnieją dwa typowe przypadki użycia dla super. W hierarchii klas z pojedynczym dziedziczeniem super można używać do odwoływania się do klas nadrzędnych bez nazywania ich jawnie, dzięki czemu kod jest łatwiejszy do utrzymania. To użycie ściśle pokrywa się z używaniem super w innych językach programowania .

Drugim przypadkiem użycia jest wspieranie dziedzicznego dziedziczenia wielokrotnego w dynamicznego środowiska wykonawczego. Ten przypadek użycia jest unikalny dla Pythona, a nie występuje w statycznie skompilowanych językach lub językach, które obsługują pojedyncze dziedziczenie tylko w postaci statycznie skompilowanej. Umożliwia to realizację "diagramy diamentów", w których wiele klas bazowych realizuje ten sam model . Dobry projekt nakazuje, aby ta metoda zawsze miała taką samą sygnaturę wywołania (ponieważ kolejność wywołań jest określana na etapie wykonawczym , ponieważ kolejność dostosowuje się do zmian w hierarchii klas, , a ponieważ ta kolejność może zawierać klasy rodzeństwa, które są nieznane przed uruchomieniem).

Więc argumentem super jest klasa, której rodzic lub rodzeństwo klasa proxy chcesz uzyskać. super(ListSortedMixin,self).get_context_data(**context) niekoniecznie będzie wywoływać get_context_data z ListSortedMixin. Zależy to od kolejności rozstrzygania metod (MRO), którą można uzyskać, używając print MyListView.__mro__ , która zadzwoni pod numer get_context_data rodzica lub rodzeństwa. Kolejność wykonywania dostosowuje się do zmian w hierarchii klas, a ponieważ to zamówienie może zawierać klasy rodzeństwa, które są nieznane przed uruchomieniem.

+0

Dzięki za szybką odpowiedź! Ładne wyjaśnienie, rozumiem to teraz :) –