Наследование и полиморфизм в Python

View more Tutorials:

1- Введение

Наследствие и полиморфизм - очень важное понятие в Python. Вы должны понять эту тему.
Перед изучением " Наследование в Python", удостоверьтесь, что у вас уже есть представление о " К лассе и объекте", если нет, давайте изучим:
Статья основана на:
  • Python 3.x

2- Наследование в Python

Python позволяет вам создать расширенный класс из одного или многих других классов. Этот класс называется производный класс (derived class) или просто подкласс .

Подкласс унаследует атрибуты, методы, и другие члены из родительского класса. Он так же может переопределять (override) методы родительского класса. Если подкласс не определяет свой конструктор, он унаследует конструктор родительского класса по умолчанию.
В отличие от Java, CSharp и некоторых других языков, Python допускает множественное наследование. Класс может быть расширен из одного или нескольких родительских классов.
Нам нужно несколько классов для примера.
  • Animal (Животное): Класс имитирует животное.
  • Duck (Утка): Класс имитирует утку, это подкласс Животного.
  • Cat (Кот): Класс имитирует кота, это подкласс Животного.
  • Mouse (Мышь): Класс имитирует мышь, это подкласс Животного.
В Python, конструктор (constructor) используется для создания объекта и прикрепляет значение атрибутам (attribute).
Конструктор подклассов всегда вызывается конструктором родительского класса, чтобы инициализировать значение для атрибутов родительского класса, затем он прикрепляет значение этим атрибутам.
Например:
animal.py
class Animal :  

    # Constructor
    def __init__(self, name):
        
        # Класс Animal имеет 1 атрибут (attribute): 'name'.
        self.name= name 
    
    # Метод (method):
    def showInfo(self):
        print ("I'm " + self.name)
        
    # Метод (method):
    def move(self):
        print ("moving ...")
Cat является подклассом, унаследованным от класса Animal, он тоже имеет атрибуты (attribute) этого класса.
cat.py
from animal import Animal       
        
# Клас Cat расширен (extends) из класса Animal.
class Cat (Animal): 
    
    def __init__(self, name, age, height):
        # Вызывается constructor родительского класса (Animal)
        # чтобы прикрепить значение к атрибуту 'name' родительского класса.
        super().__init__(name)
        
        self.age = age 
        self.height = height
    
    # Переопределить (override) метод с одинаковым названием родительского класса.
    def showInfo(self):
        
        print ("I'm " + self.name)
        print (" age " + str(self.age))
        print (" height " + str(self.height))
catTest.py
from cat import Cat


tom = Cat("Tom", 3, 20)

print ("Call move() method")

tom.move()

print ("\n")
print ("Call showInfo() method")

tom.showInfo()
Результат запуска модуля  catTest:
Что случится, когда вы создаёте объект конструктором (constructor)? Как он вызовет коструктор родительского класса? Смотрите иллюстрацию ниже:
С иллюстрации выше вы видите, что конструктор (constructor) родителькского класса вызывается в конструкторе подкласса, она присвоит значения атрибутам (attribute) родительского класса, затем и атрибутам подкласса.

3- Переопределить метод

По умолчанию, подкласс унаследует методы из родительского класса, но подкласс может переопределять (override) методы родительского класса.
mouse.py
from animal import Animal       
        
# Класс Mouse расширен (extends) из класса Animal.
class Mouse (Animal): 
    
    def __init__(self, name, age, height):
        # Вызывается Constructor родительского класса (Animal)
        # чтобы прикрепить значение к атрибуту 'name' родительского класса.
        super().__init__(name)
        
        self.age = age 
        self.height = height
    
    # Переопределить (override) метод с одинаковым названием родительского класса.
    def showInfo(self):
        # Вызвать метод родительского класса.
        super().showInfo()
        print (" age " + str(self.age))
        print (" height " + str(self.height))
    
    # Переопределить (override) метод с одинаковым названием родительского класса.
    def move(self):
        print ("Mouse moving ...")
Test
mouseTest.py
from mouse import Mouse


jerry = Mouse("Jerry", 3, 5)

print ("Call move() method")

jerry.move()

print ("\n")
print ("Call showInfo() method")

jerry.showInfo()

4- Абстрактный метод

Понятие абстракного метода (abstract method) или абстракного класса (abstract class) имеется в таких языках как Java, C#. Но не полностью определяется в Python. Все же у нас есть способ определить его.
Класс, называется абстрактным (abstract), определяет абстрактные методы и подкласс должен переопределять (override) эти методы, если хочет их использовать. Абстрактные методы всегда вызывают исключение NotImplementedError.
abstractExample.py
# Абстрактный класс (Abstract class).
class AbstractDocument :
    
    def __init__(self, name):
        
        self.name = name
        
    # Метод невозможно использовать, так как всегда выбрасывает ошибку.
    def show(self):
        raise NotImplementedError("Subclass must implement abstract method")    
    
    

class PDF(AbstractDocument):
    
    # Переопределить метод родительского класса
    def show(self):
        print ("Show PDF document:", self.name)
        
        
class Word(AbstractDocument):     
    
    def show(self):
        print ("Show Word document:", self.name)

# ----------------------------------------------------------
documents = [ PDF("Python tutorial"),
              Word("Java IO Tutorial"),
              PDF("Python Date & Time Tutorial") ]     


for doc in documents :
    
    doc.show()
В приведенном выше примере демонстрируется полиморфизм (Polymorphism) в Python. Объект Document (документ) может быть представлен в различных формах ( PDF, Word, Excel, ...).
Другой пример иллюстрирует полиморфизм: когда я говорю об азиатском человеке, это довольно абстрактно, он может быть японцем, вьетнамцем или индийцем. Однако, у них у всеъ есть особенности азиатских людей.

5- Множественное наследование

Python позволяет множественное наследование, что означает, что вы можете создать расширенный класс из двух или более других классов. Родительские классы могут иметь одинаковые атрибуты (attribute) или методы .... Подкласс будет приоритетно наследовать атрибуты, методы, ... первого класса в списке наследования.
multipleInheritanceExample.py
class Horse:
    
    maxHeight = 200; # centimeter
    
    def __init__(self, name, horsehair):
        self.name = name
        self.horsehair = horsehair
    
    def run(self):
        print ("Horse run")   
     
    def showName(self):
        print ("Name: (House's method): ", self.name)   
        
    def showInfo(self):
        print ("Horse Info")    
 

class Donkey:
    
    def __init__(self, name, weight):        
        self.name = name
        self.weight = weight    
        
        
    def run(self):
        print ("Donkey run")     
        
    def showName(self):        
        print ("Name: (Donkey's method): ", self.name)   

    def showInfo(self):
        print ("Donkey Info")               
  
# Класс Mule унаследован от Horse и Donkey.
class Mule(Horse, Donkey):
    
    def __init__(self, name, hair, weight): 
        Horse.__init__(self, name, hair)  
        Donkey.__init__(self, name, weight)
        
    
    def run(self):   
        print ("Mule run")      


    def showInfo(self):
        print ("-- Call Mule.showInfo: --")
        Horse.showInfo(self)
        Donkey.showInfo(self)

# ---- Test ------------------------------------
# Переменная 'maxHeight', унаследована от класса Horse.
print ("Max height ", Mule.maxHeight)

mule = Mule("Mule", 20, 1000)

mule.run()

mule.showName() 

mule.showInfo()

Метод mro()

Метод mro() позволяет просматривать список  родительских классов определенного класса. Давайте посмотрим на следующий пример:
mroExample.py
class X: pass
class Y: pass
class Z: pass

class A(X,Y): pass
class B(Y,Z): pass

class M(B,A,Z): pass

# Output:
# [<class '__main__.M'>, <class '__main__.B'>,
# <class '__main__.A'>, <class '__main__.X'>,
# <class '__main__.Y'>, <class '__main__.Z'>,
# <class 'object'>]

print(M.mro())
Примечание: В Python команда  pass (pass statement) подобна команде  null (или пустой), она ничего не делает, если у класса или метода нет содержимого, вам по-прежнему нужна хотя бы одна команда, давайте использовать pass.

6- Функция issubclass и isinstance

Python имеет две полезные функции:
  • isinstance
  • issubclass
     

isinstance

Функция isinstance помогает вам проверить, является ли "что-то" объектом определенного класса или нет.

issubclass

Функция issubclass проверяет, является ли этот класс потомком другого класса или нет.
isinstancesubclass.py
class A: pass
class B(A): pass


# True
print ("isinstance('abc', object): ",isinstance('abc', object)) 

# True
print ("isinstance(123, object): ",isinstance(123, object))


b = B()
a = A()

# True
print ("isinstance(b, A): ", isinstance(b, A) )
print ("isinstance(b, B): ", isinstance(b, B) )

# False
print ("isinstance(a, B): ", isinstance(a, B) )


# B is subclass of A? ==> True
print ("issubclass(B, A): ", issubclass(B, A) )

# A is subclass of B? ==> False
print ("issubclass(A, B): ", issubclass(A, B) )

7- Полиморфизм с функцией

Здесь я создаю два класса, English и  French. У обоих классов есть метод greeting(). Оба создают разные приветствия. Создайте два соответствующих объекта из двух классов выше и вызовите действия этих двух объектов в одной функции (функция intro).
people.py
class English:
   
    def greeting(self):       
        print ("Hello")
       
       
class French:
   
    def greeting(self):
        print ("Bonjour")
 
 
def intro(language):               
   
    language.greeting()
   
   
flora  = English()
aalase = French()   


intro(flora)
intro(aalase)
Запуск примера:

View more Tutorials: