Проектирование интерфейсов
Часть раздела Object Oriented Programming путешествия по Python на Coddy — урок 26 из 64.
Интерфейс определяет контракт, которому должны следовать классы. В Python мы создаем интерфейсы, используя абстрактные базовые классы, в которых все методы являются абстрактными.
Импортируйте модуль abc:
from abc import ABC, abstractmethodСоздайте интерфейс, содержащий только абстрактные методы:
class Drawable(ABC):
@abstractmethod
def draw(self):
pass
@abstractmethod
def resize(self, width, height):
passВсе методы в интерфейсе должны быть абстрактными — они определяют, что должны делать реализующие классы, а не то, как это делать.
Реализуйте интерфейс в конкретном классе:
class Circle(Drawable):
def __init__(self, radius):
self.radius = radius
def draw(self):
return "Drawing a circle"
def resize(self, width, height):
self.radius = min(width, height) / 2
return f"Resized circle to radius {self.radius}"Создайте еще один класс, который реализует тот же интерфейс:
class Rectangle(Drawable):
def __init__(self, width, height):
self.width = width
self.height = height
def draw(self):
return "Drawing a rectangle"
def resize(self, width, height):
self.width = width
self.height = height
return f"Resized rectangle to {width}x{height}"Используйте интерфейс полиморфно:
shapes = [Circle(5), Rectangle(3, 4)]
for shape in shapes:
print(shape.draw())
print(shape.resize(10, 8))Вывод:
Drawing a circle
Resized circle to radius 4.0
Drawing a rectangle
Resized rectangle to 10x8Вы также можете использовать интерфейсы в качестве подсказок типов:
def render_shape(drawable: Drawable):
return drawable.draw()
circle = Circle(3)
print(render_shape(circle))Вывод:
Drawing a circleКлючевой момент: Интерфейсы определяют, что классы должны делать, а не то, как они это делают. Используйте абстрактные базовые классы, содержащие только абстрактные методы, для создания четких контрактов, которым должны следовать реализующие их классы. Это обеспечивает согласованное поведение в различных реализациях.
Задание
СреднеВ этом испытании вы реализуете систему медиаплеера с использованием интерфейсов.
Вам необходимо отредактировать следующие файлы:
playable.py— Реализуйте абстрактные базовые классы (интерфейсы)song.py— Реализуйте класс Songvideo.py— Реализуйте класс Videomediaplayer.py— Реализуйте класс MediaPlayer
Каждый файл содержит подробные комментарии TODO, которые помогут вам в реализации. Следуйте этим комментариям, чтобы создать полноценную систему медиаплеера с правильным наследованием и реализацией интерфейсов.
Шпаргалка
Создавайте интерфейсы, используя абстрактные базовые классы с помощью модуля abc:
from abc import ABC, abstractmethodОпределите интерфейс, содержащий только абстрактные методы:
class Drawable(ABC):
@abstractmethod
def draw(self):
pass
@abstractmethod
def resize(self, width, height):
passРеализуйте интерфейс в конкретных классах:
class Circle(Drawable):
def __init__(self, radius):
self.radius = radius
def draw(self):
return "Drawing a circle"
def resize(self, width, height):
self.radius = min(width, height) / 2
return f"Resized circle to radius {self.radius}"Используйте интерфейсы полиморфно:
shapes = [Circle(5), Rectangle(3, 4)]
for shape in shapes:
print(shape.draw())
print(shape.resize(10, 8))Используйте интерфейсы в качестве подсказок типов:
def render_shape(drawable: Drawable):
return drawable.draw()Интерфейсы определяют, что должны делать классы, а не то, как они это делают. Все методы в интерфейсе должны быть абстрактными, чтобы создавать четкие контракты для реализующих классов.
Попробуйте сами
from song import Song
from video import Video
from mediaplayer import MediaPlayer
from playable import Playable, MediaInfo
# Обработчик комплексных тестовых случаев
test_case = input()
if test_case == "default_test":
# Тестовый случай по умолчанию из исходной задачи
song = Song("Bohemian Rhapsody", "Queen", 355)
video = Video("Python Tutorial", "1080p", 1800)
player = MediaPlayer()
# Тест с песней
player.set_media(song)
print(player.current_media.get_info())
print(player.play())
print(player.pause())
print(player.stop())
print() # Пустая строка для читаемости
# Тест с видео
player.set_media(video)
print(player.current_media.get_info())
print(player.play())
print(player.pause())
print(player.stop())
elif test_case == "empty_player":
# Тест медиаплеера без установленного медиафайла
player = MediaPlayer()
print(player.play()) # Должно вывести "No media set"
print(player.pause()) # Должно вывести "No media set"
print(player.stop()) # Должно вывести "No media set"
elif test_case == "time_formatting":
# Тест форматирования времени в методе get_info()
song1 = Song("Short Song", "Artist A", 65) # 1:05
song2 = Song("Long Song", "Artist B", 3661) # 61:01
video1 = Video("Hour Video", "720p", 3600) # 60:00
print(song1.get_info())
print(song2.get_info())
print(video1.get_info())
elif test_case == "interface_compliance":
# Тест того, что Song и Video правильно реализуют интерфейсы
song = Song("Test Song", "Test Artist", 180)
video = Video("Test Video", "480p", 240)
# Проверка реализации интерфейсов
print(f"Song implements Playable: {isinstance(song, Playable)}")
print(f"Song implements MediaInfo: {isinstance(song, MediaInfo)}")
print(f"Video implements Playable: {isinstance(video, Playable)}")
print(f"Video implements MediaInfo: {isinstance(video, MediaInfo)}")
# Тест всех методов интерфейса
print(song.play())
print(song.pause())
print(song.stop())
print(song.get_title())
print(song.get_duration())
print(song.get_info())
print(video.play())
print(video.pause())
print(video.stop())
print(video.get_title())
print(video.get_duration())
print(video.get_info())
elif test_case == "polymorphism":
# Тест полиморфного поведения со списком различных типов медиа
media_list = [
Song("Song 1", "Artist 1", 180),
Video("Video 1", "720p", 300),
Song("Song 2", "Artist 2", 240),
Video("Video 2", "1080p", 420)
]
player = MediaPlayer()
for media in media_list:
player.set_media(media)
print(f"Media: {media.get_title()}")
print(f"Info: {media.get_info()}")
print(f"Play: {player.play()}")
print()
elif test_case == "edge_cases":
# Тест граничных случаев
empty_song = Song("", "", 0)
edge_video = Video("A" * 100, "", -10) # Очень длинное название, пустое разрешение, отрицательная длительность
print(f"Empty song info: {empty_song.get_info()}")
print(f"Edge video info: {edge_video.get_info()}")
player = MediaPlayer()
player.set_media(empty_song)
print(player.play())
player.set_media(edge_video)
print(player.play())
elif test_case == "stress_test":
# Реализация стресс-теста с большим количеством медиа-объектов
songs = [Song(f"Song {i}", f"Artist {i}", i * 30) for i in range(1, 101)]
videos = [Video(f"Video {i}", f"{i*10}p", i * 60) for i in range(1, 101)]
player = MediaPlayer()
# Тест со всеми песнями
for i, song in enumerate(songs):
player.set_media(song)
if i % 10 == 0: # Выводить только каждую 10-ю, чтобы избежать избыточного вывода
print(f"Playing song {i+1}: {player.play()}")
# Тест со всеми видео
for i, video in enumerate(videos):
player.set_media(video)
if i % 10 == 0: # Выводить только каждую 10-ю, чтобы избежать избыточного вывода
print(f"Playing video {i+1}: {player.play()}")
print("Stress test completed successfully!")В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыПараметр selfМетодыАтрибутыМетод-конструктор (__init__)Повторение — Простой калькулятор4Наследование
Базовое наследованиеФункция super()Переопределение методовМножественное наследованиеПорядок разрешения методовИтоги — Иерархия сотрудников7Специальные методы
Введение в магические методыПерегрузка операторовМагические методы контейнеровПовторение — Пользовательский список10Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy5Полиморфизм
Снова о переопределении методовУтиная типизацияАбстрактные классы и методыПроектирование интерфейсовИтоги — Калькулятор фигур8Продвинутые концепции ООП
Композиция против наследованияМиксиныСтатические методы и методы классаДекораторы классовМенеджеры контекста11Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн Компоновщик3Свойства классов
Переменные экземпляра и классаДекораторы свойствПриватные атрибутыИтоги — Менеджер банковского счета6Инкапсуляция
Public, Protected, Private члены классаМодификаторы доступаСокрытие данныхДекораторы property: продвинутый уровеньИтоги: Система учета студентов12Проект: Управление библиотекой
Обзор проектаКлассы Book и User