优秀的编程知识分享平台

网站首页 > 技术文章 正文

python散装笔记——99: 多态

nanyue 2025-03-03 19:35:01 技术文章 8 ℃

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 解释器中运行,多态性反例代码将引发异常,因为如果它们没有显式地继承自 objecttype(input_obj).name 将返回 instance,而不是类名,从而导致区域从未被赋值。

最近发表
标签列表