Python教程33:多态
“一个接口,多种实现。”
多态是面向对象编程三大特性(封装、继承、多态)的最后一个。今天我们学习Python的多态特性,理解如何让代码更灵活、更易扩展。
1. 什么是多态
多态(Polymorphism):
- Poly(多个)+ morph(形态)
- 同一个接口,不同的实现
- 不同对象对同一消息的不同响应
为什么需要多态:
- 灵活性:通过父类引用调用子类方法
- 可扩展:添加新类无需修改现有代码
- 统一接口:不同对象用相同方式操作
- 解耦合:调用者不关心对象类型
简单示例
1class Animal:
2 """动物基类"""
3 def speak(self):
4 pass
5
6class Dog(Animal):
7 def speak(self):
8 return "Woof!"
9
10class Cat(Animal):
11 def speak(self):
12 return "Meow!"
13
14class Cow(Animal):
15 def speak(self):
16 return "Moo!"
17
18# 多态:相同的方法调用,不同的行为
19def animal_sound(animal):
20 """
21 统一的接口
22 - 参数是Animal类型
23 - 不关心具体是什么动物
24 - 调用speak()得到不同的结果
25 """
26 print(animal.speak())
27
28# 使用
29animals = [Dog(), Cat(), Cow()]
30for animal in animals:
31 animal_sound(animal)
32# 输出:
33# Woof!
34# Meow!
35# Moo!
多态的好处:
animal_sound()不需要知道传入的是什么动物- 添加新动物类(如
Pig)无需修改animal_sound() - 代码更灵活、更易扩展
2. Python的动态类型多态
Python是动态类型语言,多态更自然:
1#Python:不需要显式继承
2class Dog:
3 def speak(self):
4 return "Woof!"
5
6class Cat:
7 def speak(self):
8 return "Meow!"
9
10class Robot:
11 def speak(self):
12 return "Beep boop!"
13
14def make_speak(obj):
15 """
16 只要对象有speak()方法就行
17 - 不要求继承关系
18 - 不要求特定类型
19 - 这就是"鸭子类型"
20 """
21 print(obj.speak())
22
23# 所有有speak()方法的对象都可以传入
24make_speak(Dog())
25make_speak(Cat())
26make_speak(Robot()) # Robot不是Animal,但也能用
Python vs 静态类型语言:
Java需要显式继承和类型声明:
1// void makeSpeak(Animal animal) {
2// animal.speak();
3// }
1# Python:只要有对应方法即可
2def make_speak(obj):
3 obj.speak()
3. 鸭子类型(Duck Typing)
鸭子类型:
“如果它走起来像鸭子,叫起来像鸭子,那它就是鸭子。”
- 关注对象的行为,而不是类型
- 只要有需要的方法/属性,就可以使用
- Python的核心特性
1class Duck:
2 def swim(self):
3 print("Duck is swimming")
4
5 def fly(self):
6 print("Duck is flying")
7
8class Penguin:
9 def swim(self):
10 print("Penguin is swimming")
11
12 # 企鹅不会飞,没有fly()方法
13
14class Airplane:
15 def fly(self):
16 print("Airplane is flying")
17
18def let_it_swim(obj):
19 """只要有swim()方法"""
20 obj.swim()
21
22def let_it_fly(obj):
23 """只要有fly()方法"""
24 obj.fly()
25
26# 使用
27duck = Duck()
28penguin = Penguin()
29plane = Airplane()
30
31let_it_swim(duck) # OK
32let_it_swim(penguin) # OK
33# let_it_swim(plane) # AttributeError: Airplane没有swim()
34
35let_it_fly(duck) # OK
36let_it_fly(plane) # OK(飞机也能飞!)
37# let_it_fly(penguin) # AttributeError
鸭子类型的优势:
- 更灵活
- 不强制继承
- 易于测试(Mock对象)
鸭子类型的风险:
- 运行时才发现错误
- 需要明确文档说明接口
4. 魔术方法实现多态
Python的魔术方法让多态更强大:
1class Vector:
2 """向量类"""
3 def __init__(self, x, y):
4 self.x = x
5 self.y = y
6
7 def __add__(self, other):
8 """
9 重载+运算符
10 - 实现多态:不同类型有不同的+行为
11 """
12 return Vector(self.x + other.x, self.y + other.y)
13
14 def __str__(self):
15 return f"Vector({self.x}, {self.y})"
16
17v1 = Vector(1, 2)
18v2 = Vector(3, 4)
19v3 = v1 + v2 # 调用__add__
20print(v3) # Vector(4, 6)
21
22# Python内置类型也有多态
23print(1 + 2) # 整数加法:3
24print("a" + "b") # 字符串连接:"ab"
25print([1] + [2]) # 列表连接:[1, 2]
5. 抽象基类(ABC)
使用ABC定义接口规范:
1from abc import ABC, abstractmethod
2
3class Payment(ABC):
4 """
5 支付抽象基类
6 - 定义支付接口规范
7 - 子类必须实现所有抽象方法
8 """
9
10 @abstractmethod
11 def pay(self, amount):
12 """支付方法(子类必须实现)"""
13 pass
14
15 @abstractmethod
16 def refund(self, amount):
17 """退款方法(子类必须实现)"""
18 pass
19
20class CreditCardPayment(Payment):
21 """信用卡支付"""
22 def pay(self, amount):
23 return f"Paid ${amount} with credit card"
24
25 def refund(self, amount):
26 return f"Refunded ${amount} to credit card"
27
28class PayPal(Payment):
29 """PayPal支付"""
30 def pay(self, amount):
31 return f"Paid ${amount} via PayPal"
32
33 def refund(self, amount):
34 return f"Refunded ${amount} via PayPal"
35
36def process_payment(payment: Payment, amount):
37 """
38 处理支付
39 - 接受Payment类型
40 - 不关心具体支付方式
41 - 多态:不同支付方式有不同实现
42 """
43 print(payment.pay(amount))
44
45# 使用
46credit_card = CreditCardPayment()
47paypal = PayPal()
48
49process_payment(credit_card, 100)
50process_payment(paypal, 50)
51
52# payment = Payment() # TypeError: 不能实例化抽象类
6. 实战示例:图形绘制系统
1from abc import ABC, abstractmethod
2import math
3
4class Shape(ABC):
5 """图形抽象基类"""
6
7 @abstractmethod
8 def area(self):
9 """计算面积"""
10 pass
11
12 @abstractmethod
13 def perimeter(self):
14 """计算周长"""
15 pass
16
17 def describe(self):
18 """描述图形"""
19 return f"{self.__class__.__name__}: Area={self.area():.2f}, Perimeter={self.perimeter():.2f}"
20
21class Circle(Shape):
22 """圆形"""
23 def __init__(self, radius):
24 self.radius = radius
25
26 def area(self):
27 return math.pi * self.radius ** 2
28
29 def perimeter(self):
30 return 2 * math.pi * self.radius
31
32class Rectangle(Shape):
33 """矩形"""
34 def __init__(self, width, height):
35 self.width = width
36 self.height = height
37
38 def area(self):
39 return self.width * self.height
40
41 def perimeter(self):
42 return 2 * (self.width + self.height)
43
44class Triangle(Shape):
45 """三角形"""
46 def __init__(self, a, b, c):
47 self.a = a
48 self.b = b
49 self.c = c
50
51 def area(self):
52 # 海伦公式
53 s = self.perimeter() / 2
54 return math.sqrt(s * (s - self.a) * (s - self.b) * (s - self.c))
55
56 def perimeter(self):
57 return self.a + self.b + self.c
58
59def print_shape_info(shape: Shape):
60 """
61 打印图形信息
62 - 统一接口
63 - 多态:不同图形有不同的计算方式
64 """
65 print(shape.describe())
66
67# 使用
68shapes = [
69 Circle(5),
70 Rectangle(4, 6),
71 Triangle(3, 4, 5)
72]
73
74for shape in shapes:
75 print_shape_info(shape)
76# 输出:
77# Circle: Area=78.54, Perimeter=31.42
78# Rectangle: Area=24.00, Perimeter=20.00
79# Triangle: Area=6.00, Perimeter=12.00
7. 多态的设计模式
策略模式
1from abc import ABC, abstractmethod
2
3class SortStrategy(ABC):
4 """排序策略抽象类"""
5 @abstractmethod
6 def sort(self, data):
7 pass
8
9class BubbleSort(SortStrategy):
10 """冒泡排序"""
11 def sort(self, data):
12 print("Using bubble sort")
13 return sorted(data) # 简化示例
14
15class QuickSort(SortStrategy):
16 """快速排序"""
17 def sort(self, data):
18 print("Using quick sort")
19 return sorted(data) # 简化示例
20
21class Sorter:
22 """排序器"""
23 def __init__(self, strategy: SortStrategy):
24 self.strategy = strategy
25
26 def sort(self, data):
27 """使用策略排序"""
28 return self.strategy.sort(data)
29
30# 使用
31data = [3, 1, 4, 1, 5, 9, 2, 6]
32
33sorter = Sorter(BubbleSort())
34print(sorter.sort(data))
35
36# 切换策略
37sorter.strategy = QuickSort()
38print(sorter.sort(data))
8. 类型检查与多态
虽然Python支持鸭子类型,但有时需要类型检查:
1def process(obj):
2 """处理对象"""
3 # 方式1:鸭子类型(推荐)
4 try:
5 result = obj.process()
6 return result
7 except AttributeError:
8 return "Object doesn't support processing"
9
10 # 方式2:类型检查(不推荐)
11 if isinstance(obj, Processor):
12 return obj.process()
13
14 # 方式3:hasattr检查
15 if hasattr(obj, 'process'):
16 return obj.process()
最佳实践:
- 优先使用鸭子类型
- 必要时使用抽象基类
- 避免过度类型检查
9. 与Golang对比
Golang使用接口实现多态:
1// Go:显式接口
2type Speaker interface {
3 Speak() string
4}
5
6type Dog struct{}
7func (d Dog) Speak() string {
8 return "Woof!"
9}
10
11type Cat struct{}
12func (c Cat) Speak() string {
13 return "Meow!"
14}
15
16// 函数接受接口类型
17func MakeSpeak(s Speaker) {
18 fmt.Println(s.Speak())
19}
20
21// 使用
22MakeSpeak(Dog{})
23MakeSpeak(Cat{})
对比:
- Python:鸭子类型,隐式接口
- Go:显式接口,编译时检查
- Python:更灵活,运行时错误
- Go:更安全,编译时错误
10. 小结
今天我们学习了多态:
- 定义:同一接口,多种实现
- Python多态:动态类型,鸭子类型
- 鸭子类型:关注行为而非类型
- 抽象基类:定义接口规范
- 实战应用:图形系统、策略模式
- 最佳实践:优先鸭子类型,必要时ABC
多态让代码更灵活、更易扩展,是面向对象编程的核心特性。
练习题:
- 实现一个文件处理系统,支持txt、csv、json等多种格式(多态)
- 用策略模式实现计算器的不同运算策略
- 设计一个通知系统,支持邮件、短信、推送等多种方式
本文代码示例:
关注公众号:极客老墨
更多 AI 应用开发、工程实践和效率工具分享,欢迎扫码关注。
