Class w Python

0

Witam, czy mogłby ktoś mi wyjaśnić 'na chlopski rozum' jakie wartości(wiem jakie, bo moge sprawdzic przez IDE) i dlaczego zwroci poszczegolny fragment kodu?

class B1:
    i = 3
    def __init__(self):
        self.a = 4
    def f(self):
        return self.a
    def getI(self):
        return self.__class__.i

class B2:
    i = 5
    def __init__(self):
        self.a = 6
    def f(self):
        return self.a
    def getI(self):
        return self.__class__.i

class C(B2, B1):
    def __init__(self):
        B1.__init__(self)
        B2.__init__(self)
    def g(self):
        return self.f()
    def h(self):
        return self.getI()

c = C()
print 'a = ', c.a, ' i = ', c.__class__.i
print 'c.g() = ', c.g(), 'c.h() = ', c.h()
0

Poczytaj o MRO (ang. Method Resolution Order)

>>> C.__mro__
(<class 'sandbox.test.C'>, <class 'sandbox.test.B2'>, <class 'sandbox.test.B1'>, <type 'object'>)

MRO to algorytm "przeszukiwania" - wskazuje jaka jest kolejność dziedziczenia.
I tak jak wołasz coś na obiekcie o nazwie c klasy C to najpierwsz "szuka" odpowiednika w swojej klasie tj: C gdy nie znajdzie
to wtedy "szuka" tego w klasie B2 i potem B1 (w dużym uproszczeniu zawsze przeszukiwania idzie od lewej strony i w głąb ścieżki - DFS lub coś co bardzo przypomina DFS). Tak czy siak najlepiej sobie sprawdzić tak jak na powyższym przykładzie.

Dodam tutaj tylko że rozważania o MRO są z reguły czysto teoretyczne gdyż wielodziedziczenia należy się wystrzegać i trudno mi wymyśleć jakiś sensowny przykład kiedy ma to sens.

0

Pytanie czy to python2 czy python3, bo mają różne wersje przeszukiwania :).

3

Powiedzmy ze jest roznica w MRO w Pythonie 2 vs Pythonie 3. Ale ta różnica polega nie na wersji Pythona tylko bardziej na wersji algorytmu MRO. A algorytm MRO zalezy czy jest uzyte A czy A(object).
Tutaj jest tylko C(A, B) i A oraz B ( autor moim zdaniem uzyl tego nieświadomie , niezgodnie z konwencja bez wiedzy ocb).

Zaś wracając do samych różnic w MRO:

Python 3.6.1

class A:
    def who_am_i(self):
        print("I am a A")

class B(A):
    def who_am_i(self):
        print("I am a B")

class C(A):
    def who_am_i(self):
        print("I am a C")

class D(B,C):
    def who_am_i(self):
        print("I am a D")

d1 = D()
d1.who_am_i()
print(D.__mro__)

Python 3.6.1

(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

Python 2.7.13

class A(object):
    def who_am_i(self):
        print("I am a A")


class B(A):
    def who_am_i(self):
        print("I am a B")


class C(A):
    def who_am_i(self):
        print("I am a C")


class D(B,C):
    def who_am_i(self):
        print("I am a D")


d1 = D()
d1.who_am_i()
print(D.__mro__)

Python 2.7.13

(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)

Użycie samego A w Python 2.7 używa starego algorytmu MRO czyli przeszukiwanie nastapi w takiej kolejnosci -> D, B, A, C, A

  • nie mozna tego chyba latwo sprawdzić bo brak dziedziczenia po object zabiera mozliwosc uzycia MRO.
    Ale jest to DFS od lewej wgłąb.

Uzycie A(object) w Pythonie 2.7 odpala nowy algorytm MRO
Uzycie A w Pythonie 3 dziala jak A(object) w Pythonie 2.7 czyli z uzyciem nowego MRO
+
nowy algorytm MRO dziala podobnie jak stary czyli DFS od lewej wgłąb ale ma dodatkowy warunek
tj: sprawdza redundancje w tym wypadku redundancja jest przy dziedziczeniu po klasie A bo zarowno B jak i C dziedziczy po A
stad finalnie bedzie: D, B, C, A dla podanego przypadku tj: usuwa redundancje i bierze wedlug kolejnosci klasy D.

Stawiam że autor użył "błędnie" Pythona 2.x bo użycie A zamiast A(object) w wersji 2.7 jest błędem. Dlatego że należy dziedziczyć po object i używać "nowych klas".
Użycie Pythona 2.x potwierdza: print " " zamiast print("") dla Pythona 3.
Tak czy siak w przypadku autora niezależnie jakiej wersji Pythona i algorytmu MRO uzyje zawsze bedzie tak samo.

Dodatkowo różnica "nowy MRO" vs "stary MRO" jest taka że "nowy MRO" blokuje stworzenie struktury wzajemnie zależnych klas i rzuca błędem - zaś stary MRO na to zezwoli.

TypeError: Error when calling the metaclass bases
    Cannot create a consistent method resolution
order (MRO) for bases Y, X

Przyklad wzajemnice zaleznych klas ( Python 2.7.13)

class X(object):
    def who_am_i(self):
        print("I am a X")


class Y(object):
    def who_am_i(self):
        print("I am a Y")


class A(X, Y):
    def who_am_i(self):
        print("I am a A")


class B(Y, X):
    def who_am_i(self):
        print("I am a B")


class F(A, B):
    def who_am_i(self):
        print("I am a F")

1 użytkowników online, w tym zalogowanych: 0, gości: 1