1: 鸭子类型
由于 Python 的动态类型系统,在 Python 中可以使用鸭子类型(duck typing)形式的无继承多态性。这意味着只要类包含相同的方法,Python 解释器就不会区分它们,因为只有在运行时才会对调用进行检查。
class Duck:
def quack(self):
print("Quaaaaaack!")
def feathers(self):
print("The duck has white and gray feathers.")
class Person:
def quack(self):
print("The person imitates a duck.")
def feathers(self):
print("The person takes a feather from the ground and shows it.")
def name(self):
print("John Smith")
def in_the_forest(obj):
obj.quack()
obj.feathers()
donald = Duck()
john = Person()
in_the_forest(donald)
in_the_forest(john)
输出结果:
Quaaaaaack!
The duck has white and gray feathers.
The person imitates a duck.
The person takes a feather from the ground and shows it.
2: 基本多态
多态性是一种对对象执行操作的能力,无论其类型如何。实现方法一般是创建一个基类,然后创建两个或更多子类,这些子类都实现了具有相同签名的方法。任何其他操作这些对象的函数或方法都可以调用相同的方法,而无需先进行类型检查,也不管它是对哪种类型的对象执行操作。在面向对象的术语中,当类 X 扩展类 Y 时,Y 被称为超类或基类,X 被称为子类或派生类。
class Shape:
"""
这是一个父类,旨在被其他类继承。
它为不同形状的面积计算提供了一个通用接口。
"""
def calculate_area(self):
"""
这个方法旨在被子类覆盖。
如果子类没有实现该方法但被调用,将抛出 NotImplementedError 异常。
"""
raise NotImplementedError("子类必须实现此方法。")
class Square(Shape):
"""
这是 Shape 类的子类,表示一个正方形。
"""
side_length = 2 # 在这个例子中,正方形的边长为 2 个单位。
def calculate_area(self):
"""
这个方法覆盖了 Shape 类的 calculate_area() 方法。
当调用 Square 类型对象的 calculate_area() 方法时,将调用此方法而不是父类的方法。
它执行正方形的面积计算并返回结果。
"""
return self.side_length ** 2 # 正确的面积计算公式:边长的平方。
class Triangle(Shape):
"""
这是 Shape 类的子类,表示一个三角形。
"""
base_length = 4 # 三角形的底边长度为 4 个单位。
height = 3 # 三角形的高为 3 个单位。
def calculate_area(self):
"""
这个方法覆盖了 Shape 类的 calculate_area() 方法,并执行三角形的面积计算,
返回结果。
"""
return 0.5 * self.base_length * self.height # 三角形面积公式:底边长度 × 高 ÷ 2。
def get_area(input_obj):
"""
这个函数接受一个输入对象,并调用该对象的 calculate_area() 方法。
对象类型未指定,可以是 Square、Triangle 或其他任何 Shape 的实例。
"""
try:
print(f"该形状的面积为:{input_obj.calculate_area()}")
except NotImplementedError as e:
print(f"错误:{e}")
# 创建每个类的一个实例
shape_obj = Shape()
square_obj = Square()
triangle_obj = Triangle()
# 分别将每个对象传递给 get_area() 函数,查看结果。
get_area(shape_obj) # 这将引发错误,因为 Shape 类的 calculate_area() 方法未实现。
get_area(square_obj) # 这将打印正方形的面积。
get_area(triangle_obj) # 这将打印三角形的面积。
我们应该看到以下输出:
None
4
6.0
没有多态性会发生什么?
如果没有多态性,在对对象执行操作前可能需要进行类型检查,以确定要调用的方法是否正确。下面的计数器示例执行了与前面代码相同的任务,但由于没有使用多态性,get_area() 函数需要做更多的工作。
class Shape:
"""
父类 Shape,定义了一个通用的 calculate_area 方法。
"""
def calculate_area(self):
"""
子类需要覆盖此方法以实现具体的面积计算逻辑。
"""
raise NotImplementedError("子类必须实现此方法。")
class Square(Shape):
"""
正方形类,继承自 Shape。
"""
side_length = 2 # 正方形的边长
def calculate_area(self):
"""
计算正方形的面积。
"""
return self.side_length ** 2
class Triangle(Shape):
"""
三角形类,继承自 Shape。
"""
base_length = 4 # 三角形的底边长度
height = 3 # 三角形的高
def calculate_area(self):
"""
计算三角形的面积。
"""
return 0.5 * self.base_length * self.height
def get_area(input_obj):
"""
通用函数,接受一个 Shape 类型的对象并调用其 calculate_area 方法。
无需显式类型检查,利用多态直接调用对象的 calculate_area 方法。
"""
try:
area = input_obj.calculate_area()
print(f"该形状的面积为:{area}")
except NotImplementedError as e:
print(f"错误:{e}")
# 创建每个类的一个实例
square_obj = Square()
triangle_obj = Triangle()
# 分别将每个对象传递给 get_area() 函数,查看结果。
get_area(square_obj) # 输出正方形的面积
get_area(triangle_obj) # 输出三角形的面积
我们应该看到以下输出:
4
6.0
重要说明
注意计数器示例中使用的类是 new style 类,如果使用 Python 3,则隐式继承于对象类。多态性在 Python 2.x 和 3.x 中都能工作,但如果在 Python 2.x 解释器中运行,多态性反例代码将引发异常,因为如果它们没有显式地继承自 object,type(input_obj).name 将返回 instance,而不是类名,从而导致区域从未被赋值。