CodeGym /Kursy /Python SELF PL /Polimorfizm

Polimorfizm

Python SELF PL
Poziom 16 , Lekcja 2
Dostępny

8.1 Polimorfizm

Polimorfizm to jedna z głównych koncepcji programowania obiektowego (OOP), która pozwala obiektom różnych klas używać tego samego interfejsu.

W Pythonie polimorfizm osiąga się przez dynamiczne typowanie i dziedziczenie. Ważnym aspektem polimorfizmu jest przeciążanie metod i zastępowanie metod klasy bazowej metodami klasy potomnej.

Główne koncepcje polimorfizmu:

Jednolity interfejs dla różnych obiektów:

Polimorfizm pozwala używać jednolitego interfejsu dla obiektów różnych klas, które mogą mieć różne implementacje metod.

Dynamiczne typowanie:

W Pythonie typ zmiennej określa się w trakcie wykonywania, co pozwala funkcjom pracować z obiektami dowolnej klasy, o ile wspierają wymagany interfejs.

Dziedziczenie:

Pozwala tworzyć hierarchie klas, gdzie podklasy dziedziczą właściwości i metody klasy bazowej, ale mogą je nadpisywać dla swojej specyficznej funkcjonalności.

Przykłady polimorfizmu:

Prosty przykład polimorfizmu: wszystkie trzy klasy mają metodę o nazwie move(). To oznacza, że możemy napisać kod, który będzie jednocześnie działał z tymi obiektami.


class Car:
    def move(self):
        pass
        
class Human:
    def move(self):
        pass
        
class Bird:
    def move(self):
        print("Kaa!")
        
car = Car()
human = Human()
bird = Bird()
        
for it in [car, human, bird]:
    it.move()

W tym przypadku „wspólnym interfejsem” jest nazwa metody lub jej sygnatura, jeśli metoda ma parametry.

8.2 Nadpisywanie metod

Polimorfizm jest często używany razem z dziedziczeniem do tworzenia hierarchii klas, gdzie klasa bazowa definiuje wspólny interfejs, a podklasy realizują specyficzne szczegóły.


class Employee:
    def get_salary(self):
        return 1000
        
class FullTimeEmployee(Employee):
    def get_salary(self):
        return 5000
        
class PartTimeEmployee(Employee):
    def get_salary(self):
        return 3000
        
class Intern(Employee):
    pass
        
def print_salary(employee):
    print(employee.get_salary())
        
employees = [FullTimeEmployee(), PartTimeEmployee(), Intern()]
        
for employee in employees:
    print_salary(employee)

Ten przykład jest podobny do poprzedniego, ale ma ważną różnicę — nasze klasy nie muszą mieć metody get_salary, bo zawsze jest ona w klasie bazowej. Teraz „wspólnym interfejsem” wszystkich klas jest nie tylko metoda get_salary(), ale właśnie klasa Employee ze wszystkimi jej metodami i atrybutami.

8.3 Wywołanie metod klasy potomnej

Spójrz uważnie na poniższy kod:

Wywołujemy metodę print_salary(), która występuje tylko w klasie bazowej Employee, a ona z klasy bazowej wywołuje inną metodę klasy bazowej — get_salary(). Jakie więc pensje zostaną wypisane na ekran?


class Employee:
    def print_salary(self):
        salary = self.get_salary()
        print(salary)
        
    def get_salary(self):
        return 1000
        
class FullTimeEmployee(Employee):
    def get_salary(self):
        return 5000
        
class PartTimeEmployee(Employee):
    def get_salary(self):
        return 3000
        
class Intern(Employee):
    pass
        
employees = [FullTimeEmployee(), PartTimeEmployee(), Intern()]
        
for employee in employees:
    employee.print_salary()

Ważne! Przeczytaj, co jest napisane poniżej — to ważne.

Jeśli wywołamy metodę print_salary(), na przykład w klasie FullTimeEmployee, to wywoła się metoda klasy bazowej, ponieważ taka metoda nie jest zadeklarowana w samej klasie.

Ale ta metoda print_salary() będzie wywoływać metodę get_salary() obiektu self, który ma typ FullTimeEmployee, a nie Employee. Dlatego wywoła się właśnie metoda get_salary() z klasy FullTimeEmployee!

Co więcej, klasa FullTimeEmployee może wywołać metodę get_salary() klasy bazowej dla swoich potrzeb. Na przykład, chcemy obliczać pensję w procentach od stawki bazowej:


class FullTimeEmployee(Employee):
    def get_salary(self):
        base_salary = super().get_salary()
        return base_salary * 5  
      
  
# 500%
class PartTimeEmployee(Employee): def get_salary(self): base_salary = super().get_salary() return base_salary * 3 # 300%

8.4 Przeciążanie metod

Przeciążanie metod (method overloading) w Pythonie — to zdolność do tworzenia kilku metod o takiej samej nazwie, ale różnych parametrach. Jednak w czystej postaci przeciążanie metod nie jest wspierane w Pythonie, jak to jest w innych językach (na przykład C++ czy Java).

W Pythonie można imitować przeciążanie metod przez użycie argumentów domyślnych, *args i **kwargs.

Przykłady przeciążania metod

Możemy zrealizować funkcję, która będzie mieć różne zachowanie w zależności od liczby przekazanych argumentów. Można to zrobić na różne sposoby, na przykład tak:


class Example:
    def display(self, a=None, b=None):
        if a is not None and b is not None: print(a, b)
        elif a is not None: print(a)
        else: print("Brak argumentów")
        

obj = Example()
obj.display(1, 2)  # Output: 1 2
obj.display(1)  # Output: 1
obj.display()  # Output: Brak argumentów

Można również uwzględnić typ przekazanych danych — po prostu zrobić sprawdzenie typu:


class Example:
    def mod(self, a, b):
        if type(a) == int and type(b) == int: print(a % b)
        elif type(a) == float or type(b) == float: print(round(a / b))
        else: print("Instrukcja pomocy: a i b powinny być typu int lub float")
        

obj = Example()
obj.mod(5, 2)  # Output: 1
obj.mod(5.0, 2)  # Output: 2
obj.mod("5", 2)  # Output: Instrukcja pomocy: a i b powinny być typu int lub float

Jeśli przekazane zostaną niewłaściwe typy danych, można wyświetlić instrukcję lub nawet link do dokumentacji w internecie — to również bardzo popularne rozwiązanie.

Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION