[Python 基础课程]抽象类
在 Python 中,抽象类(Abstract Class) 是一种特殊的类,它不能被直接实例化(也就是你不能直接创建它的对象),它的主要目的是作为其他类的基类(父类),来定义一组必须由子类实现的方法。
你可以把抽象类想象成一份“合同”或者“行为规范”。它规定了所有继承它的子类必须具备哪些功能(抽象方法),但它自己并不知道这些功能具体该如何实现。具体的实现细节,则留给子类去完成。
为什么需要抽象类
考虑这样一个场景:你正在开发一个图形绘制程序,里面有各种各样的图形:圆形、矩形、三角形等。它们都有一些共同的行为,比如计算面积和计算周长。
如果直接这样写:
# class Circle:
# def calculate_area(): ...
# def calculate_perimeter(): ...# class Rectangle:
# def calculate_area(): ...
# def calculate_perimeter(): ...# class Triangle:
# def calculate_area(): ...
# def calculate_perimeter(): ...
这看起来没问题,但如果你在代码中写了一个处理通用图形的函数:
# def print_shape_info(shape):
# # 如果 shape 没有 calculate_area 方法,这里就会报错!
# print(f"面积: {shape.calculate_area()}")
# print(f"周长: {shape.calculate_perimeter()}")
你如何确保所有传入 print_shape_info
函数的 shape
对象都一定有 calculate_area()
和 calculate_perimeter()
这两个方法呢?在没有抽象类的情况下,这只能依靠程序员的约定和自律。一旦有哪个新图形类忘记实现这些方法,就会在运行时出错。
抽象类的作用就是:
- 定义统一接口: 强制子类实现某些特定的方法,确保所有派生类都符合预期的行为规范。
- 提供基础骨架: 可以在抽象类中实现一些通用方法,供子类共享,避免代码重复。
- 防止不完整实例化: 阻止创建没有实现所有必要功能的父类对象。
定义抽象类
Python 通过内置的 abc
模块(Abstract Base Classes)来支持抽象类。
核心要素:
ABC
:抽象基类模块中的一个类,你的抽象类需要继承它。@abstractmethod
: 一个装饰器,用于标记抽象类中的方法,表示这些方法必须由子类实现。
from abc import ABC, abstractmethodclass AbstractClassName(ABC): # 你的抽象类需要继承 ABC@abstractmethoddef abstract_method_one(self, param):"""这是一个抽象方法,子类必须实现它。它只有声明,没有具体的实现。"""pass # 或者 raise NotImplementedError("子类必须实现此方法")@abstractmethoddef abstract_method_two(self):"""另一个抽象方法。"""passdef concrete_method(self):"""这是一个具体方法,抽象类可以有自己的实现。子类可以选择重写或直接继承。"""print("这是一个具体方法,可以在抽象类中实现。")
我们来用抽象类解决前面提到的图形绘制问题:
from abc import ABC, abstractmethod# 定义一个抽象基类 Shape
class Shape(ABC):@abstractmethoddef calculate_area(self):"""计算图形的面积"""pass@abstractmethoddef calculate_perimeter(self):"""计算图形的周长"""passdef get_description(self):"""这是一个具体方法,子类可以直接继承"""return "这是一个通用图形。"# 实现具体的圆形类
class Circle(Shape):def __init__(self, radius):self.radius = radiusdef calculate_area(self):return 3.14159 * self.radius * self.radiusdef calculate_perimeter(self):return 2 * 3.14159 * self.radius# 实现具体的矩形类
class Rectangle(Shape):def __init__(self, length, width):self.length = lengthself.width = widthdef calculate_area(self):return self.length * self.widthdef calculate_perimeter(self):return 2 * (self.length + self.width)# 尝试创建一个没有实现所有抽象方法的类会报错
# class IncompleteShape(Shape):
# def calculate_area(self):
# return 0 # 只实现了一个# --- 演示与测试 ---# 无法直接实例化抽象类
# my_abstract_shape = Shape() # 这行会抛出 TypeErrorcircle = Circle(5)
rectangle = Rectangle(4, 6)print(f"圆形描述: {circle.get_description()}")
print(f"圆形面积: {circle.calculate_area()}")
print(f"圆形周长: {circle.calculate_perimeter()}")print("-" * 20)print(f"矩形描述: {rectangle.get_description()}")
print(f"矩形面积: {rectangle.calculate_area()}")
print(f"矩形周长: {rectangle.calculate_perimeter()}")print("-" * 20)# 前面提到的通用函数现在可以安全使用了
def print_shape_info(shape: Shape): # 可以用类型提示增强可读性print(f"--- {type(shape).__name__} 信息 ---")print(f"描述: {shape.get_description()}")print(f"面积: {shape.calculate_area()}")print(f"周长: {shape.calculate_perimeter()}")print("-" * 20)print_shape_info(circle)
print_shape_info(rectangle)# 如果你尝试创建 IncompleteShape 的实例,会报错:
# incomplete = IncompleteShape() # TypeError: Can't instantiate abstract class IncompleteShape with abstract method calculate_perimeter
代码解释:
class Shape(ABC):
:Shape
类继承自ABC
,表明它是一个抽象基类。@abstractmethod
:装饰器calculate_area
和calculate_perimeter
方法,强制任何继承Shape
的子类都必须实现这两个方法。Circle
和Rectangle
类都继承了Shape
,并且强制性地实现了calculate_area
和calculate_perimeter
。get_description()
是一个具体方法,它在抽象类中提供了实现,子类可以直接继承使用,也可以根据需要重写。- 当你尝试创建
Shape()
的实例时,Python 会抛出TypeError
,因为它是抽象类,不能直接实例化。 - 当你尝试创建
IncompleteShape()
的实例时,即使它继承了Shape
,但由于没有实现所有抽象方法,也会抛出TypeError
抽象类和接口的关联
在 Java 或 C# 等语言中,有明确的 interface
关键字来定义接口。Python 没有这个关键字,但抽象类在很多情况下可以被看作是 Python 实现“接口”的一种方式。
- 抽象类作为接口: 当一个抽象类中所有的方法都是抽象方法(即没有具体实现)时,它就非常类似于其他语言中的接口。它只定义了一套行为规范
- 抽象类与具体方法: 抽象类还可以包含具体的(已实现)方法,这是与纯接口的不同之处。这允许你在接口中定义一些通用的功能,供所有子类共享