4 принципа ООП
| Принцип | Суть |
|---|---|
| Наследование | Один класс получает поля и методы другого. Позволяет избежать дублирования и расширять логику. |
| Инкапсуляция | Объект скрывает внутреннее устройство и предоставляет только нужный интерфейс. |
| Полиморфизм | Один интерфейс может вести себя по-разному в зависимости от конкретного типа объекта. |
| Абстракция | Выделение общих характеристик и поведения, чтобы задать интерфейс для будущих реализаций. |
Наследование
Базовый (родительский, супер) класс — тот, от которого наследуются. Дочерний (производный, подкласс) класс — тот, который наследует.
class Employee:
def __init__(self, name: str) -> None:
self.name = name
def work(self) -> None:
print(f"{self.name} is working...")
class Programmer(Employee):
pass
class Manager(Employee):
pass
programmer = Programmer("Alice")
manager = Manager("Bob")
programmer.work() # Alice is working...
manager.work() # Bob is working...
Полиморфизм
Полиморфизм (от греч. «много форм») позволяет использовать один и тот же интерфейс для разных типов объектов. В Python основной механизм — переопределение методов (overriding).
class Employee:
def __init__(self, name: str) -> None:
self.name = name
def work(self) -> None:
print(f"{self.name} is working...")
class Programmer(Employee):
def work(self) -> None:
print(f"{self.name} is writing code...")
class Manager(Employee):
def work(self) -> None:
print(f"{self.name} is managing team...")
staff = [Programmer("Alice"), Manager("Bob"), Programmer("Bill")]
for person in staff:
person.work()
Python автоматически выбирает нужную версию метода в зависимости от реального класса объекта.
Переопределение методов
Если дочерний класс заново определяет метод родителя, при вызове через объект дочернего класса используется новая версия. Это и есть overriding.
Функция super()
super() вызывает метод ближайшего родителя с учётом порядка разрешения методов (MRO). Она помогает не дублировать общий код.
super() в __init__
class Employee:
def __init__(self, name: str) -> None:
self.name = name
class Programmer(Employee):
def __init__(self, name: str, language: str) -> None:
super().__init__(name)
self.language = language
p = Programmer("Alice", "Python")
print(p.name) # Alice
print(p.language) # Python
super() в обычном методе
class Employee:
def work(self) -> None:
print("General tasks")
class Programmer(Employee):
def work(self) -> None:
super().work()
print("Writing code")
Programmer().work()
# General tasks
# Writing code
super() в цепочке наследования
class Person:
def __init__(self, name: str) -> None:
self.name = name
print(f"Init Person: {self.name}")
class Employee(Person):
def work(self) -> None:
print(f"{self.name} is working...")
class Manager(Employee):
def __init__(self, name: str, department: str) -> None:
super().__init__(name)
self.department = department
print(f"Init Manager: {self.name} manages {self.department}")
m = Manager("Alice", "Development")
# Init Person: Alice
# Init Manager: Alice manages Development
super() вместо явного вызова Parent.__init__(self): так код не привязан к имени родителя и корректно работает при изменении иерархии.
isinstance()
Проверяет, является ли объект экземпляром указанного класса или его наследника. Работает и со встроенными типами, и с кортежем классов.
class Employee:
pass
class Programmer(Employee):
pass
p = Programmer()
print(isinstance(p, Programmer)) # True
print(isinstance(p, Employee)) # True — наследник
print(isinstance(p, object)) # True — все классы от object
print(isinstance(p, (str, int))) # False
issubclass()
Проверяет, является ли один класс подклассом другого — напрямую или через цепочку наследования.
class Employee:
pass
class Programmer(Employee):
pass
class BackendDeveloper(Programmer):
pass
print(issubclass(Programmer, Employee)) # True
print(issubclass(BackendDeveloper, Employee)) # True (через цепочку)
print(issubclass(Employee, Programmer)) # False
print(issubclass(Employee, object)) # True
super() — Built-in Functions.