组合与继承
本课属于 Coddy Python 学习旅程的“面向对象编程”单元。第 37 课,共 64 课。
面向对象编程提供了两种代码复用的主要方式:继承(“是一个”关系)和组合(“有一个”关系)。
下面是一个通过继承创建“是一个”关系的示例:
class Animal:
def __init__(self, name):
self.name = name
def eat(self):
return f"{self.name} is eating"
class Dog(Animal): # Dog “是一种” Animal
def bark(self):
return "Woof!"
dog = Dog("Buddy")
print(dog.eat()) # 继承的方法
print(dog.bark()) # 自身的方法下面是一个通过组合创建“拥有”关系的示例:
class Engine:
def start(self):
return "Engine started"
def stop(self):
return "Engine stopped"
class Car: # Car "has an" Engine
def __init__(self):
self.engine = Engine() # 组合
def start(self):
return self.engine.start()
car = Car()
print(car.start()) # 使用组合的 engine通过一个更复杂的例子来比较这两种方法:
# 继承方式
class Bird:
def move(self):
return "Flying"
class Duck(Bird):
def quack(self):
return "Quack!"
# 组合方式
class FlyBehavior:
def move(self):
return "Flying"
class SwimBehavior:
def move(self):
return "Swimming"
class VersatileDuck:
def __init__(self):
self.fly_behavior = FlyBehavior()
self.swim_behavior = SwimBehavior()
def fly(self):
return self.fly_behavior.move()
def swim(self):
return self.swim_behavior.move()
def quack(self):
return "Quack!"测试这两种方法:
# 继承
duck1 = Duck()
print(duck1.move()) # 飞行
print(duck1.quack()) # 嘎嘎!
# 组合
duck2 = VersatileDuck()
print(duck2.fly()) # 飞行
print(duck2.swim()) # 游泳
print(duck2.quack()) # 嘎嘎!输出:
Buddy is eating
Woof!
Engine started
Flying
Quack!
Flying
Swimming
Quack!主要区别:
继承:
- 父类与子类之间耦合紧密
- “是一个”关系
- 父类的更改会影响所有子类
- 最适合真正的层级关系
组合:
- 对象之间的松散耦合
- “拥有”关系
- 更加灵活,可以在运行时更改行为
- 更容易测试和修改
要点:当存在真正的“是一个”关系时,使用继承。当你需要灵活性和松耦合时,使用组合。“组合优于继承”这一原则建议在大多数情况下优先使用组合,因为它更灵活,也更易于维护。
挑战
中等在这个挑战中,你将使用继承和组合模式来实现一个媒体库系统。
你需要编辑以下文件,并根据每个文件中的 TODO 注释完成操作:
media.py、book.py、movie.py、musicalbum.py,用于实现继承mediaitem.py、bookcomposition.py、moviecomposition.py、musicalbumcomposition.py,用于实现组合
每种媒体类型都应包含:
- 标题、创作者(作者/导演/艺术家)和年份属性
- 适当的
display_info()方法
亲手试试
from media import Media
from book import Book
from movie import Movie
from musicalbum import MusicAlbum
from mediaitem import MediaItem
from bookcomposition import BookComposition
from moviecomposition import MovieComposition
from musicalbumcomposition import MusicAlbumComposition
# 综合测试用例处理程序
test_case = input()
def test_inheritance_basic():
book = Book("The Hobbit", "J.R.R. Tolkien", 1937)
movie = Movie("The Matrix", "Wachowski Sisters", 1999)
album = MusicAlbum("Abbey Road", "The Beatles", 1969)
assert book.display_info() == "Book: The Hobbit by J.R.R. Tolkien (1937)"
assert movie.display_info() == "Movie: The Matrix directed by Wachowski Sisters (1999)"
assert album.display_info() == "Music Album: Abbey Road by The Beatles (1969)"
def test_composition_basic():
book_comp = BookComposition("Dune", "Frank Herbert", 1965)
movie_comp = MovieComposition("Inception", "Christopher Nolan", 2010)
album_comp = MusicAlbumComposition("Thriller", "Michael Jackson", 1982)
assert book_comp.display_info() == "Book: Dune by Frank Herbert (1965)"
assert movie_comp.display_info() == "Movie: Inception directed by Christopher Nolan (2010)"
assert album_comp.display_info() == "Music Album: Thriller by Michael Jackson (1982)"
def test_inheritance_relationships():
book = Book("Test Book", "Test Author", 2000)
movie = Movie("Test Movie", "Test Director", 2001)
album = MusicAlbum("Test Album", "Test Artist", 2002)
assert isinstance(book, Book)
assert isinstance(book, Media)
assert isinstance(movie, Movie)
assert isinstance(movie, Media)
assert isinstance(album, MusicAlbum)
assert isinstance(album, Media)
def test_attribute_access():
# 测试继承方式
book = Book("Test Book", "Test Author", 2000)
assert book.title == "Test Book"
assert book.creator == "Test Author"
assert book.year == 2000
# 测试组合方式
book_comp = BookComposition("Test Book", "Test Author", 2000)
assert book_comp.media.title == "Test Book"
assert book_comp.media.creator == "Test Author"
assert book_comp.media.year == 2000
def test_polymorphism():
media_list = [
Book("Book1", "Author1", 2001),
Movie("Movie1", "Director1", 2002),
MusicAlbum("Album1", "Artist1", 2003)
]
assert media_list[0].display_info() == "Book: Book1 by Author1 (2001)"
assert media_list[1].display_info() == "Movie: Movie1 directed by Director1 (2002)"
assert media_list[2].display_info() == "Music Album: Album1 by Artist1 (2003)"
def test_edge_cases():
# 空字符串
book = Book("", "", 0)
assert book.display_info() == "Book: by (0)"
# 特殊字符:已修复此处的语法错误
movie = Movie("Test\"Movie", "Test\\nDirector", -1)
assert movie.display_info() == "Movie: Test\"Movie directed by Test\\nDirector (-1)"
# 极端年份
album = MusicAlbum("Test Album", "Test Artist", 9999)
assert album.display_info() == "Music Album: Test Album by Test Artist (9999)"
def test_stress():
# 创建大量媒体对象
books = [Book(f"Book{i}", f"Author{i}", 2000+i) for i in range(100)]
movies = [Movie(f"Movie{i}", f"Director{i}", 2000+i) for i in range(100)]
albums = [MusicAlbum(f"Album{i}", f"Artist{i}", 2000+i) for i in range(100)]
for i, book in enumerate(books):
assert book.display_info() == f"Book: Book{i} by Author{i} ({2000+i})"
for i, movie in enumerate(movies):
assert movie.display_info() == f"Movie: Movie{i} directed by Director{i} ({2000+i})"
for i, album in enumerate(albums):
assert album.display_info() == f"Music Album: Album{i} by Artist{i} ({2000+i})"
# 根据输入运行对应的测试
if test_case == "default_test" or test_case == "":
test_inheritance_basic()
test_composition_basic()
print("All tests passed!")
elif test_case == "inheritance_test":
test_inheritance_basic()
test_inheritance_relationships()
print("Inheritance tests passed!")
elif test_case == "composition_test":
test_composition_basic()
print("Composition tests passed!")
elif test_case == "polymorphism_test":
test_polymorphism()
print("Polymorphism tests passed!")
elif test_case == "attribute_test":
test_attribute_access()
print("Attribute tests passed!")
elif test_case == "edge_cases":
test_edge_cases()
print("Edge case tests passed!")
elif test_case == "stress_test":
test_stress()
print("Stress tests passed!")这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Python 在线编译器