在 Python 中,抽象类(Abstract Base Class, ABC)是一种不能被直接实例化的类,它主要用于定义一套子类必须遵守的接口规范。抽象类通过强制子类实现特定的方法,来保证代码的一致性和可维护性。

比如我们需要定义一个图形类,要求所有的子类(三角形、圆形、矩形等等)都必须实现计算面积和周长的方法。我们可以使用抽象类来实现这一点。

使用抽象类有以下几个好处:

  1. 强制子类实现特定方法:确保所有子类都遵循相同的接口规范,提高代码的一致性。
  2. 提高代码的可维护性:通过定义抽象类,可以更容易地管理和维护代码。
  3. 支持多态性:抽象类允许我们使用多态性来处理不同类型的对象,只要它们实现了相同的接口。

创建抽象类

要创建一个抽象类,需要导入 abc 模块,并使用 ABC 作为基类,同时使用 @abstractmethod 装饰器来标记抽象方法。下面是一个示例:

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self) -> float:
        pass

    @abstractmethod
    def perimeter(self) -> float:
        pass

在上面的代码中,Shape 类是一个抽象类,定义了两个抽象方法 areaperimeter。任何继承自 Shape 的子类都必须实现这两个方法,否则会抛出错误。

下面是如何实现抽象类的示例:

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius
        
    def area(self) -> float:
        return 3.14 * self.radius ** 2
        
    def perimeter(self) -> float:
        return 2 * 3.14 * self.radius
    
class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height
        
    def area(self) -> float:
        return self.width * self.height
        
        
# 创建 Circle 和 Rectangle 对象
circle = Circle(5)
print("Circle Area:", circle.area())            # 输出: Circle Area: 78.5
print("Circle Perimeter:", circle.perimeter())  # 输出: Circle Perimeter: 31.400000000000002


# 尝试实例化 Shape 类会引发错误
try:
    shape = Shape()
except TypeError as e:
    print("Error:", e)  # 输出: Error: Can't instantiate abstract class Shape without an implementation for abstract methods 'area', 'perimeter'
    
# Rectangle 类未实现 perimeter 方法,尝试实例化 Rectangle 类会引发错误
try:
    rectangle = Rectangle(4, 6)
except TypeError as e:
    print("Error:", e)  # 输出: Error: Can't instantiate abstract class Rectangle with an implementation for abstract method 'perimeter'

上面的代码展示了如何创建一个抽象类 Shape,以及如何实现它的子类 CircleRectangle。注意,Rectangle 类没有实现 perimeter 方法,因此尝试实例化它会引发错误。

创建抽象实例属性

抽象类不仅可以包含抽象方法,还可以包含抽象实例属性。使用 @property 装饰器结合 @abstractmethod 来定义抽象属性。

下面示例展示如何创建一个只读的抽象实例属性:

from abc import ABC, abstractmethod
class Person(ABC):
    @property
    @abstractmethod
    def age(self) -> int:
        pass
        
    @property
    @abstractmethod
    def name(self) -> str:
        pass
    
    @property
    @abstractmethod
    def gender(self) -> str:
        pass

class Male(Person):

    def __init__(self, age: int, name: str):
        self._age = age
        self._name = name
        
    @property
    def age(self) -> int:
        return self._age
    
    @property
    def name(self) -> str:
        return self._name
        
    @property
    def gender(self) -> str:
        return "Male"
        

class Female(Person):

    def __init__(self, age: int, name: str):
        self._age = age
        self._name = name
        
    @property
    def age(self) -> int:
        return self._age
    
    @property
    def name(self) -> str:
        return self._name
        

# 获取实例属性
car = Male(age=30, name="John")
print(car.age)  # 输出: 30
print(car.name)  # 输出: John
print(car.gender)  # 输出: Male


# Female 类未实现 gender 属性,尝试实例化 Female 类会引发错误
try:
    female = Female(age=25, name="Jane")
except TypeError as e:
    print("Error:", e)  # 输出: Error: Can't instantiate abstract class Female without an implementation for abstract method 'gender'

# 尝试修改只读的抽象类属性会引发 AttributeError
try:
    car.age = 5  
except AttributeError as e:
    print("Error:", e) # 输出: Error: property 'age' of 'Male' object has no setter

上面的代码展示了如何创建一个只读的抽象实例属性 agenamegender。任何继承自 Person 的子类都必须实现这些属性,否则会抛出错误。

下面示例展示如何创建一个可读写的抽象实例属性:

from abc import ABC, abstractmethod


class Person(ABC):
    @property
    @abstractmethod
    def age(self) -> int:
        pass

    # 实例属性的 setter 方法,用于设置属性值
    @age.setter
    @abstractmethod
    def age(self) -> int:
        pass

    @property
    @abstractmethod
    def name(self) -> str:
        pass

    @property
    @abstractmethod
    def gender(self) -> str:
        pass


class Male(Person):
    def __init__(self, age: int, name: str):
        self._age = age
        self._name = name


    @property
    def age(self) -> int:
        return self._age

    @age.setter
    def age(self, value: int):
        self._age = value

    @property
    def name(self) -> str:
        return self._name

    @property
    def gender(self) -> str:
        return "Male"


# 获取实例属性
car = Male(age=30, name="John")
print(car.age)  # 输出: 30
print(car.name)  # 输出: John
print(car.gender)  # 输出: Male

# 设置实例属性
car.age = 5
print(car.age)  # 输出: 5

实例属性设置了 setter 方法后,就可以对属性进行修改了。


THEEND



© 转载需要保留原始链接,未经明确许可,禁止商业使用。CC BY-NC-ND 4.0