接口设计
本课属于 Coddy Python 学习旅程的“面向对象编程”单元。第 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:实现 Song 类video.py:实现 Video 类mediaplayer.py:实现 MediaPlayer 类
每个文件都包含详细的 TODO 注释,为你的实现提供指引。按照这些注释,使用正确的继承关系并实现接口,构建一个完整的媒体播放器系统。
亲手试试
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!")这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Python 在线编译器