Dziedziczenie i polimorfizm

Dziedziczenie i polimorfizm - to bardzo ważna koncepcja w Pythonie. Musisz to lepiej zrozumieć, jeśli chcesz się uczyć.

Dziedzictwo

Jedną z głównych zalet programowania obiektowego jest ponowne wykorzystanie. Dziedziczenie jest jednym z mechanizmów umożliwiających osiągnięcie tego samego. Dziedziczenie pozwala programiście najpierw utworzyć klasę ogólną lub bazową, a następnie rozszerzyć ją na bardziej wyspecjalizowaną. Pozwala programiście pisać lepszy kod.

Korzystając z dziedziczenia, możesz używać lub dziedziczyć wszystkie pola danych i metody dostępne w Twojej klasie bazowej. Później możesz dodać własne metody i pola danych, dzięki czemu dziedziczenie zapewnia sposób organizacji kodu, zamiast przepisywania go od podstaw.

W terminologii zorientowanej obiektowo, gdy klasa X rozszerza klasę Y, wówczas Y nazywany jest klasą nadrzędną / nadrzędną / podstawową, a X nazywany jest klasą podrzędną / potomną / pochodną. Należy tu zauważyć, że tylko pola danych i metody, które nie są prywatne, są dostępne dla klas podrzędnych. Prywatne pola i metody danych są dostępne tylko wewnątrz klasy.

składnia do utworzenia klasy pochodnej to -

class BaseClass:
   Body of base class
class DerivedClass(BaseClass):
   Body of derived class

Dziedziczenie atrybutów

Teraz spójrz na poniższy przykład -

Wynik

Najpierw utworzyliśmy klasę o nazwie Date i przekazaliśmy obiekt jako argument, here-object jest klasą wbudowaną dostarczaną przez Pythona. Później stworzyliśmy kolejną klasę o nazwie time i nazwaliśmy klasę Date jako argument. Poprzez to wywołanie uzyskujemy dostęp do wszystkich danych i atrybutów klasy Date w klasie Time. Z tego powodu, gdy próbujemy pobrać metodę get_date z utworzonego wcześniej obiektu klasy Time tm, jest to możliwe.

Hierarchia wyszukiwania atrybutów

  • Instancja
  • Klasa
  • Dowolna klasa, z której dziedziczy ta klasa

Przykłady dziedziczenia

Przyjrzyjmy się bliżej przykładowi dziedziczenia -

Stwórzmy kilka klas, aby wziąć udział w przykładach -

  • Animal - klasa symulująca zwierzę
  • Kot - podklasa zwierzęcia
  • Pies - podklasa zwierzęcia

W Pythonie konstruktor klasy służący do tworzenia obiektu (instancji) i przypisywania wartości atrybutom.

Konstruktor podklas jest zawsze wywoływany do konstruktora klasy nadrzędnej w celu zainicjowania wartości atrybutów w klasie nadrzędnej, a następnie zaczyna przypisywać wartość swoim atrybutom.

Wynik

W powyższym przykładzie widzimy atrybuty lub metody poleceń, które umieściliśmy w klasie nadrzędnej, aby wszystkie podklasy lub klasy potomne dziedziczyły tę właściwość z klasy nadrzędnej.

Jeśli podklasa spróbuje dziedziczyć metody lub dane z innej podklasy, spowoduje to błąd, co widzimy, gdy klasa Dog próbuje wywołać metody swatstring () z tej klasy cat, zgłasza błąd (jak w naszym przypadku AttributeError).

Polimorfizm („WIELE KSZTAŁTÓW”)

Polimorfizm jest ważną cechą definicji klas w Pythonie, która jest używana, gdy masz powszechnie nazwane metody w klasach lub podklasach. Pozwala to funkcjom na używanie jednostek różnych typów w różnym czasie. Dzięki temu zapewnia elastyczność i luźne powiązania, dzięki czemu kod można rozszerzać i łatwo utrzymywać w czasie.

Umożliwia to funkcjom używanie obiektów dowolnej z tych klas polimorficznych bez konieczności zdawania sobie sprawy z różnic między klasami.

Polimorfizm można przeprowadzić poprzez dziedziczenie, z podklasami wykorzystującymi metody klas bazowych lub nadpisującymi je.

Zrozummy pojęcie polimorfizmu w naszym poprzednim przykładzie dziedziczenia i dodajmy jedną wspólną metodę o nazwie show_affection w obu podklasach -

Z przykładu, który widzimy, odnosi się do projektu, w którym obiekt o odmiennym typie może być traktowany w ten sam sposób, a dokładniej dwie lub więcej klas z metodą o tej samej nazwie lub wspólnym interfejsie, ponieważ ta sama metoda (show_affection w poniższym przykładzie) jest wywoływana z każdym typem obiektów.

Wynik

Tak więc wszystkie zwierzęta okazują uczucia (show_affection), ale robią to inaczej. Zachowania „show_affection” są zatem polimorficzne w tym sensie, że zachowywały się różnie w zależności od zwierzęcia. Tak więc abstrakcyjna koncepcja „zwierzęcia” w rzeczywistości nie oznacza „okazywania uczucia”, ale określone zwierzęta (takie jak psy i koty) mają konkretną implementację akcji „show_affection”.

Sam Python ma klasy, które są polimorficzne. Na przykład funkcja len () może być używana z wieloma obiektami i wszystkie zwracają prawidłowe dane wyjściowe na podstawie parametru wejściowego.

Nadrzędny

W Pythonie, gdy podklasa zawiera metodę, która przesłania metodę z nadklasy, możesz również wywołać metodę nadklasy, wywołując

Metoda Super (Subclass, self). Zamiast self.method.

Przykład

class Thought(object):
   def __init__(self):
      pass
   def message(self):
      print("Thought, always come and go")

class Advice(Thought):
   def __init__(self):
      super(Advice, self).__init__()
   def message(self):
      print('Warning: Risk is always involved when you are dealing with market!')

Dziedziczenie konstruktora

Jeśli widzimy z naszego poprzedniego przykładu dziedziczenia, __init__ znajdował się w klasie nadrzędnej w górę, ponieważ pies lub kot klasy potomnej nie zawierał metody __init__. Python użył wyszukiwania atrybutu dziedziczenia, aby znaleźć __init__ w klasie zwierząt. Kiedy stworzyliśmy klasę potomną, najpierw będzie wyglądać jak metoda __init__ w klasie psa, potem jej nie znajdzie, potem zajrzał do klasy nadrzędnej Animal, znalazł tam i nazwał. Ponieważ nasz projekt klasy stał się skomplikowany, możemy chcieć zainicjować instancję, najpierw przetwarzając ją przez konstruktor klasy nadrzędnej, a następnie za pomocą konstruktora klasy potomnej.

Wynik

W powyższym przykładzie wszystkie zwierzęta mają imię, a wszystkie psy określonej rasy. Nazwaliśmy konstruktora klasy nadrzędnej za pomocą super. Więc pies ma swój własny __init__, ale pierwszą rzeczą, która się zdarza, jest to, że nazywamy go super. Super ma wbudowaną funkcję i jest przeznaczony do powiązania klasy z jej superklasą lub klasą nadrzędną.

W tym przypadku mówimy, że pobierz superklasę psa i przekaż instancję psa do dowolnej metody, którą tu podamy, konstruktor __init__. Innymi słowy, za pomocą obiektu psa nazywamy klasę nadrzędną Animal __init__. Możesz zapytać, dlaczego nie powiemy po prostu Animal __init__ z instancją psa, moglibyśmy to zrobić, ale gdyby nazwa klasy zwierząt miała się zmienić, kiedyś w przyszłości. A gdybyśmy chcieli zmienić hierarchię klas, żeby pies odziedziczył po innej klasie. Używanie super w tym przypadku pozwala nam zachować modułowość i łatwość zmiany i konserwacji.

W tym przykładzie jesteśmy w stanie połączyć ogólną funkcjonalność __init__ z bardziej szczegółową funkcjonalnością. Daje nam to możliwość oddzielenia zwykłej funkcjonalności od konkretnej funkcjonalności, co może wyeliminować powielanie kodu i powiązać klasy ze sobą w sposób odzwierciedlający ogólny projekt systemu.

Wniosek

  • __init__ jest jak każda inna metoda; może być dziedziczona

  • Jeśli klasa nie ma konstruktora __init__, Python sprawdzi jej klasę nadrzędną, aby zobaczyć, czy może ją znaleźć.

  • Gdy tylko znajdzie taki, Python wywołuje go i przestaje szukać

  • Możemy użyć funkcji super () do wywołania metod z klasy nadrzędnej.

  • Możemy chcieć zainicjować w rodzica, jak również w naszej własnej klasie.

Dziedziczenie wielokrotne i drzewo odnośników

Jak sama nazwa wskazuje, dziedziczenie wielokrotne w Pythonie ma miejsce, gdy klasa dziedziczy z wielu klas.

Na przykład dziecko dziedziczy cechy osobowości po obojgu rodzicach (matce i ojcu).

Składnia wielokrotnego dziedziczenia w Pythonie

Aby klasa dziedziczyła po wielu klasach nadrzędnych, podczas definiowania wpisujemy nazwy tych klas w nawiasach do klasy pochodnej. Oddzielamy te nazwy przecinkami.

Poniżej znajduje się przykład tego -

>>> class Mother:
   pass

>>> class Father:
   pass

>>> class Child(Mother, Father):
   pass

>>> issubclass(Child, Mother) and issubclass(Child, Father)
True

Dziedziczenie wielokrotne odnosi się do zdolności do dziedziczenia z dwóch lub więcej niż dwóch klas. Złożoność pojawia się, gdy dziecko dziedziczy po rodzicu, a rodzice po klasie dziadków. Python wspina się na dziedziczące drzewo, szukając atrybutów, które mają być odczytane z obiektu. Sprawdzi w instancji, w klasie, następnie w klasie nadrzędnej i na końcu w klasie dziadków. Teraz pojawia się pytanie, w jakiej kolejności będą przeszukiwane klasy - najpierw oddech czy najpierw głębia. Domyślnie Python korzysta z opcji depth-first.

Dlatego na poniższym diagramie Python wyszukuje najpierw metodę dothis () w klasie A. Zatem kolejność rozwiązywania metod w poniższym przykładzie będzie

Mro- D→B→A→C

Spójrz na poniższy diagram wielokrotnego dziedziczenia -

Przeanalizujmy przykład, aby zrozumieć funkcję „mro” języka Python.

Wynik

Przykład 3

Weźmy inny przykład wielokrotnego dziedziczenia „w kształcie rombu”.

Powyższy diagram zostanie uznany za niejednoznaczny. Z naszego poprzedniego przykładu, rozumienie „kolejności rozdzielczości metody” .ie mro będzie D → B → A → C → A, ale tak nie jest. Pobierając drugie A z C, Python zignoruje poprzednie A., więc w tym przypadku Mro będzie D → B → C → A.

Stwórzmy przykład na podstawie powyższego schematu -

Wynik

Prosta zasada zrozumienia powyższego wyniku jest następująca: jeśli ta sama klasa pojawi się w kolejności rozwiązywania metod, wcześniejsze wystąpienia tej klasy zostaną usunięte z kolejności rozwiązywania metod.

Podsumowując -

  • Każda klasa może dziedziczyć z wielu klas

  • Python zwykle używa kolejności „najpierw głębia” podczas wyszukiwania klas dziedziczących.

  • Ale kiedy dwie klasy dziedziczą z tej samej klasy, Python eliminuje pierwsze wystąpienia tej klasy z mro.

Dekoratory, metody statyczne i klasowe

Funkcje (lub metody) są tworzone przez instrukcję def.

Chociaż metody działają dokładnie w taki sam sposób jak funkcja, z wyjątkiem jednego punktu, w którym metoda pierwszego argumentu jest obiektem instancji.

Możemy klasyfikować metody na podstawie ich zachowania, np

  • Simple method- zdefiniowane poza klasą. Ta funkcja może uzyskać dostęp do atrybutów klasy, podając argument instancji:

def outside_func(():
  • Instance method -

def func(self,)
  • Class method - jeśli potrzebujemy użyć atrybutów klas

@classmethod
def cfunc(cls,)
  • Static method - nie mam żadnych informacji o zajęciach

@staticmethod
def sfoo()

Do tej pory widzieliśmy metodę instancji, teraz jest czas, aby uzyskać wgląd w pozostałe dwie metody,

Metoda klasowa

Dekorator @classmethod jest dekoratorem funkcji wbudowanej, który jako pierwszy argument przekazuje klasę, do której został wywołany, lub klasę instancji, do której została wywołana. Wynik tej oceny przesłania definicję funkcji.

składnia

class C(object):
   @classmethod
   def fun(cls, arg1, arg2, ...):
      ....
fun: function that needs to be converted into a class method
returns: a class method for function

Mają dostęp do tego argumentu cls, nie może on modyfikować stanu instancji obiektu. To wymagałoby dostępu do siebie.

  • Jest związany z klasą, a nie przedmiotem tej klasy.

  • Metody klasy mogą nadal modyfikować stan klasy, który ma zastosowanie do wszystkich wystąpień klasy.

Metoda statyczna

Metoda statyczna nie pobiera ani parametru self ani cls (klasy), ale może akceptować dowolną liczbę innych parametrów.

syntax

class C(object):
   @staticmethod
   def fun(arg1, arg2, ...):
   ...
returns: a static method for function funself.
  • Metoda statyczna nie może ani modyfikować stanu obiektu, ani stanu klasy.
  • Mają ograniczenia w zakresie danych, do których mają dostęp.

Kiedy używać czego

  • Generalnie używamy metody klas do tworzenia metod fabrycznych. Metody fabryczne zwracają obiekt klasy (podobny do konstruktora) dla różnych przypadków użycia.

  • Generalnie używamy metod statycznych do tworzenia funkcji narzędziowych.