from abc import ABC, abstractmethod
class Interface(ABC):
@abstractmethod
def do_something(self):
pass
class Implementation(Interface):
def do_something(self):
print("Implementation doing something")
def use_interface(obj: Interface):
obj.do_something()
obj = Implementation()
use_interface(obj)
В Python, благодаря его динамической типизации, концепция интерфейсов реализуется через полиморфизм, а не через строгую реализацию интерфейсов, как в языках вроде Java или C#. Основная идея заключается в том, что объекты разных классов могут использоваться взаимозаменяемо, если они предоставляют одинаковый набор методов (имеют "утиную типизацию" - "если он ходит как утка и крякает как утка, то это утка").
Основные способы создания интерфейсов с использованием полиморфизма в Python:
AttributeError
во время выполнения. Это гибкий, но менее строгий подход.
class Bird:
def fly(self):
print("Птица летит")
class Airplane:
def fly(self):
print("Самолёт летит")
def make_it_fly(flyable):
try:
flyable.fly() # Предполагаем, что объект имеет метод fly()
except AttributeError:
print("Объект не умеет летать")
bird = Bird()
airplane = Airplane()
make_it_fly(bird) # Вывод: Птица летит
make_it_fly(airplane) # Вывод: Самолёт летит
class Rock:
pass #У объекта нет метода fly
rock = Rock()
make_it_fly(rock) #Вывод: Объект не умеет летать
abc
: Модуль abc
предоставляет способ определить абстрактные классы и абстрактные методы. Абстрактные классы не могут быть инстанцированы, и их подклассы должны реализовать все абстрактные методы, иначе Python выбросит исключение TypeError
при попытке создать экземпляр подкласса. Это более строгий способ, чем duck typing.
from abc import ABC, abstractmethod
class Flyable(ABC):
@abstractmethod
def fly(self):
pass
class Bird(Flyable):
def fly(self):
print("Птица летит")
class Airplane(Flyable):
def fly(self):
print("Самолёт летит")
# Создание экземпляра Flyable напрямую невозможно:
# flyable = Flyable() # TypeError: Can't instantiate abstract class Flyable with abstract methods fly
bird = Bird()
bird.fly() #Вывод: Птица летит
#Класс, не реализующий абстрактный метод, вызовет ошибку при инстанциации:
class BadBird(Flyable):
pass
#bad_bird = BadBird() # TypeError: Can't instantiate abstract class BadBird with abstract methods fly
isinstance()
или issubclass()
для проверки типов, но это менее распространено и часто считается не-питоническим. В основном используется для обработки особых случаев. Это обычно избегают, так как это нарушает принципы полиморфизма и duck typing.Пример обобщенного кода:
def process_data(data_source):
data = data_source.load_data() # Предполагаем, что data_source имеет метод load_data()
processed_data = some_processing(data)
data_source.save_data(processed_data) # Предполагаем, что data_source имеет метод save_data()
class FileDataSource:
def load_data(self):
print("Загрузка данных из файла")
return "Данные из файла"
def save_data(self, data):
print(f"Сохранение данных в файл: {data}")
class DatabaseDataSource:
def load_data(self):
print("Загрузка данных из базы данных")
return "Данные из базы данных"
def save_data(self, data):
print(f"Сохранение данных в базу данных: {data}")
def some_processing(data):
print("Обработка данных")
return f"Обработанные данные: {data}"
file_data_source = FileDataSource()
database_data_source = DatabaseDataSource()
process_data(file_data_source) # Работает с FileDataSource
process_data(database_data_source) # Работает с DatabaseDataSource
Заключение:
Выбор между duck typing и ABC зависит от конкретной ситуации. Duck typing обеспечивает большую гибкость, но ABC обеспечивает более строгий контроль и лучшую читаемость кода, особенно в больших проектах.