Python 教程 / 面向对象 / 面向对象高级编程

面向对象高级编程

在掌握了面向对象基础(封装、继承、多态)之后,让我们深入学习Python提供的高级特性,包括属性装饰器、slots限制、静态方法、类方法、抽象类、元类和枚举等。

@property 装饰器

@property 允许我们将方法"伪装"成属性来访问,既保证了数据安全性,又提供了友好的API。

┌─────────────────────────────────────────────────────────────────┐
│                      @property 工作原理                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   【传统方式】                                                    │
│   ┌─────────────────────────────────────────────────────────┐  │
│   │  class Person:                                            │  │
│   │      def __init__(self, name):                          │  │
│   │          self.name = name  # 直接暴露,无验证           │  │
│   │                                                         │  │
│   │  p = Person("A")                                        │  │
│   │  p.name = ""  # 空的名字也可以?!                      │  │
│   └─────────────────────────────────────────────────────────┘  │
│                                                                 │
│   【@property 方式】                                            │
│   ┌─────────────────────────────────────────────────────────┐  │
│   │  class Person:                                            │  │
│   │      def __init__(self, name):                          │  │
│   │          self.name = name  # 实际上是调用 setter       │  │
│   │                                                         │  │
│   │      @property                                            │  │
│   │      def name(self):          # getter                  │  │
│   │          return self._name                              │  │
│   │                                                         │  │
│   │      @name.setter                # setter               │  │
│   │      def name(self, value):                             │  │
│   │          if len(value) < 2:                             │  │
│   │              raise ValueError("名字至少2个字符")         │  │
│   │          self._name = value                              │  │
│   │                                                         │  │
│   │  p = Person("Alice")  # OK                             │  │
│   │  p.name = ""       # ValueError!                        │  │
│   └─────────────────────────────────────────────────────────┘  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
                         

1. 基本用法

Python
class Person:
    def __init__(self, name, age):
        self._name = name      # 私有变量,约定以 _ 开头
        self._age = age

    # getter - 读取属性时调用
    @property
    def name(self):
        return self._name

    # setter - 写入属性时调用
    @name.setter
    def name(self, value):
        if len(value) < 2:
            raise ValueError("名字至少2个字符")
        self._name = value

    # deleter - 删除属性时调用
    @name.deleter
    def name(self):
        print(f"删除 {self._name} 的名字属性")
        del self._name

p = Person("张三", 25)

# 像访问普通属性一样访问
print(p.name)       # 张三 (触发 getter)

# 赋值时验证
p.name = "李四"      # 触发 setter
print(p.name)       # 李四

p.name = "A"        # ValueError: 名字至少2个字符 

2. 只读属性

只定义 @property 而不定义 setter,可以创建只读属性:

Python
class Circle:
    def __init__(self, radius):
        self._radius = radius

    @property
    def radius(self):
        return self._radius

    # 只读属性:只定义 getter,不定义 setter
    @property
    def area(self):
        return round(math.pi * self._radius ** 2, 2)

    @property
    def perimeter(self):
        return round(2 * math.pi * self._radius, 2)

import math
c = Circle(5)

print(c.radius)     # 5
print(c.area)      # 78.54
print(c.perimeter) # 31.42

c.radius = 10     # AttributeError: property 'radius' has no setter 

__slots__ 限制属性

__slots__ 可以限制一个类能拥有的属性,防止随意添加动态属性,并节省内存。

┌─────────────────────────────────────────────────────────────────┐
│                       __slots__ 的作用                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   【不使用 __slots__】                                          │
│   ┌─────────────────────────────────────────────────────────┐  │
│   │  class Student:                                          │  │
│   │      pass                                               │  │
│   │                                                         │  │
│   │  s = Student()                                          │  │
│   │  s.name = "张三"      # 可以任意添加属性                  │  │
│   │  s.score = 100       # 可以任意添加属性                   │  │
│   │  s.anything = xxx     # 什么都可以添加...                 │  │
│   └─────────────────────────────────────────────────────────┘  │
│                                                                 │
│   【使用 __slots__】                                            │
│   ┌─────────────────────────────────────────────────────────┐  │
│   │  class Student:                                          │  │
│   │      __slots__ = ("name", "score", "grade")            │  │
│   │                                                         │  │
│   │  s = Student()                                          │  │
│   │  s.name = "张三"      # OK                              │  │
│   │  s.score = 100       # OK                               │  │
│   │  s.grade = "A"       # OK                               │  │
│   │  s.other = xxx      # AttributeError!                    │  │
│   └─────────────────────────────────────────────────────────┘  │
│                                                                 │
│   【内存对比】                                                    │
│   普通 dict: 每个实例有独立的 __dict__,占用较多内存             │
│   __slots__: 不创建 __dict__,直接用固定大小的数组存储属性        │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
                         
Python
# 普通类 - 可以任意添加属性
class NormalStudent:
    def __init__(self, name):
        self.name = name

ns = NormalStudent("张三")
ns.score = 100      # 完全可以添加新属性
ns.anything = "任意"  # 什么都可以添加

# 使用 __slots__ - 限制属性
class Student:
    # 只能在 __slots__ 列表中的属性名
    __slots__ = ("name", "score", "grade", "_age")

    def __init__(self, name, score):
        self.name = name
        self.score = score

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, value):
        if value < 0 or value > 150:
            raise ValueError("年龄必须在 0-150 之间")
        self._age = value

s = Student("李四", 95)

# 这些都是允许的
s.name = "王五"      # OK
s.score = 98          # OK
s.grade = "A"          # OK
s.age = 20            # OK - _age 在 slots 中

# 这些会报错
# s.other = "xxx"  # AttributeError: 'Student' object has no attribute 'other'
# s.birthday = "2000-01-01"  # AttributeError

# 注意: __slots__ 不会影响子类,除非子类也定义了 __slots__
# 子类如果不定义 __slots__,仍然可以有 __dict__ 

静态方法与类方法

Python 类中有三种方法:实例方法、静态方法和类方法。它们各有不同的用途。

┌─────────────────────────────────────────────────────────────────┐
│                   三种方法对比                                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   ┌───────────────────────────────────────────────────────────┐ │
│   │  实例方法 (Instance Method)                               │ │
│   │  - 第一个参数是 self                                       │ │
│   │  - 可以访问和修改实例属性                                  │ │
│   │  - 只能通过实例调用                                        │ │
│   │  - 示例: def method(self, arg):                           │ │
│   └───────────────────────────────────────────────────────────┘ │
│                                                                 │
│   ┌───────────────────────────────────────────────────────────┐ │
│   │  静态方法 (Static Method)                                 │ │
│   │  - 不需要 self 或 cls 参数                                │ │
│   │  - 与类相关但不需要访问类/实例属性                         │ │
│   │  - 可以通过类或实例调用                                    │ │
│   │  - 示例: @staticmethod def method(arg):                   │ │
│   └───────────────────────────────────────────────────────────┘ │
│                                                                 │
│   ┌───────────────────────────────────────────────────────────┐ │
│   │  类方法 (Class Method)                                    │ │
│   │  - 第一个参数是 cls (类本身)                               │ │
│   │  - 可以访问和修改类属性                                   │ │
│   │  - 可以通过类或实例调用                                    │ │
│   │  - 示例: @classmethod def method(cls, arg):                │ │
│   └───────────────────────────────────────────────────────────┘ │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
                         

1. 静态方法 @staticmethod

静态方法不需要访问类或实例的属性,纯粹是一个属于类的"函数":

Python
class MathUtil:
    # 静态方法:与类和实例都无关的纯函数
    @staticmethod
    def add(a, b):
        return a + b

    @staticmethod
    def is_positive(n):
        return n > 0

    @staticmethod
    def max_of_three(a, b, c):
        return max(a, b, c)

# 调用静态方法 - 可以通过类直接调用
print(MathUtil.add(3, 5))           # 8
print(MathUtil.is_positive(10))       # True
print(MathUtil.max_of_three(1, 5, 3))  # 5

# 也可以通过实例调用
m = MathUtil()
print(m.add(1, 2))              # 3 

2. 类方法 @classmethod

类方法可以访问类属性,常用于创建类的实例:

Python
class Person:
    total_count = 0

    def __init__(self, name, age):
        self.name = name
        self.age = age
        Person.total_count += 1

    @classmethod
    def from_birth_year(cls, name, birth_year):
        "通过出生年份创建 Person 实例"
        import datetime
        age = datetime.datetime.now().year - birth_year
        return cls(name, age)   # 调用 __init__

    @classmethod
    def create_anonymous(cls):
        "创建一个匿名人物"
        return cls("匿名", 0)

    @classmethod
    def get_total_count(cls):
        "获取总人数"
        return cls.total_count

# 普通的创建方式
p1 = Person("张三", 25)

# 使用类方法 - 通过出生年份创建
p2 = Person.from_birth_year("李四", 1995)
print(p2.name, p2.age)      # 李四 30 (假设今年2025)

# 使用类方法 - 创建匿名人物
p3 = Person.create_anonymous()
print(p3.name)              # 匿名

# 使用类方法 - 获取统计信息
print(Person.get_total_count())  # 3 

3. 三种方法对比

特性 实例方法 @staticmethod @classmethod
第一个参数 self (实例) cls (类)
访问实例属性 可以 不可以 不可以
访问类属性 可以 不可以 可以
通过类调用 不可以 可以 可以
通过实例调用 可以 可以 可以

抽象类

抽象类是一种不能直接实例化的类,它定义了一组子类必须实现的方法。使用 ABC (Abstract Base Class) 和 @abstractmethod 来创建。

┌─────────────────────────────────────────────────────────────────┐
│                       抽象类示意图                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   from abc import ABC, abstractmethod                          │
│                                                                 │
│   class Shape(ABC):          ← 抽象基类                         │
│       @abstractmethod         ← 抽象方法                         │
│       def area(self): ...                                    │
│                                                                 │
│       @abstractmethod                                         │
│       def perimeter(self): ...                                │
│                                                                 │
│   class Circle(Shape):        ← 具体类                         │
│       def area(self): return πr²  ← 必须实现                   │
│       def perimeter(self): return 2πr  ← 必须实现               │
│                                                                 │
│   shape = Shape()  # TypeError! 抽象类不能实例化               │
│   circle = Circle()  # OK!                                     │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
                         
Python
from abc import ABC, abstractmethod

# 抽象基类
class Shape(ABC):
    @abstractmethod
    def area(self):
        """计算面积 - 子类必须实现"""
        pass

    @abstractmethod
    def perimeter(self):
        """计算周长 - 子类必须实现"""
        pass

    # 抽象类也可以有具体方法
    def info(self):
        print(f"面积: {self.area()}, 周长: {self.perimeter()}")

# 具体子类
class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

    def perimeter(self):
        return 2 * (self.width + self.height)

# 抽象类不能实例化
# shape = Shape()  # TypeError: Can't instantiate abstract class Shape

# 具体类可以实例化
rect = Rectangle(4, 5)
print(f"面积: {rect.area()}")        # 20
print(f"周长: {rect.perimeter()}")   # 18
rect.info()                            # 面积: 20, 周长: 18

# 强制实现检查
class IncompleteShape(Shape):
    def area(self):
        return 0

    # 忘记实现 perimeter

# 尝试实例化会报错
# incomplete = IncompleteShape()  # TypeError 

元类 (Metaclass)

元类是"类的类"。普通类定义了如何创建实例,元类定义了如何创建类。

┌─────────────────────────────────────────────────────────────────┐
│                       元类示意图                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   【普通类】                                                     │
│   class MyClass:         ← 类                                   │
│       x = 1                                                  │
│                                                                 │
│   obj = MyClass()         ← 实例                                │
│                                                                 │
│   【元类】                                                       │
│   class Meta(type):         ← 元类 (type 的子类)               │
│       pass                                                    │
│                                                                 │
│   class MyClass(metaclass=Meta):    ← 使用元类创建类           │
│       x = 1                                                  │
│                                                                 │
│   Python 内部: Meta("MyClass", (), {"x": 1})                  │
│                                                                 │
│   常见用途:                                                     │
│   • 自动注册类                                                   │
│   • 验证类属性                                                   │
│   • ORM 模型映射                                                │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
                         
Python
# 元类示例:自动注册所有子类
class Registry(type):
    # 类属性:存储所有注册的类
    _registry = {}

    def __new__(cls, name, bases, attrs):
        new_class = super().__new__(cls, name, bases, attrs)

        # 将类名注册到 registry
        cls._registry[name] = new_class
        print(f"注册类: {name}")
        return new_class

# 使用元类创建类
class Animal(metaclass=Registry):
    def speak(self):
        raise NotImplementedError

class Dog(Animal):
    def speak(self):
        return "汪汪"

class Cat(Animal):
    def speak(self):
        return "喵喵"

# 查看注册的类
print(f"已注册的类: {list(Registry._registry.keys())}")

# 根据名称获取类
DogClass = Registry._registry["Dog"]
dog = DogClass()
print(dog.speak())   # 汪汪 
什么时候使用元类?
大多数情况下,你不需要使用元类。但如果你需要:
1. 修改类的创建过程
2. 自动注册或验证类
3. 实现 ORM 框架
那么元类就是强大的工具。

枚举类

枚举是一组符号名称的集合,用于表示固定的可能值。使用 enum 模块创建。

Python
from enum import Enum, IntEnum, Flag, auto

# 基本枚举
class Status(Enum):
    DRAFT = 0
    PUBLISHED = 1
    ARCHIVED = 2

# 使用枚举
print(Status.DRAFT)              # Status.DRAFT
print(Status.DRAFT.value)        # 0
print(Status.DRAFT.name)         # DRAFT

# 整数枚举 (可以直接比较)
class Priority(IntEnum):
    LOW = 1
    MEDIUM = 2
    HIGH = 3

print(Priority.HIGH >= Priority.MEDIUM)  # True

# 自动编号枚举
class Color(Enum):
    RED = auto()
    GREEN = auto()
    BLUE = auto()

print(list(Color))   # [<Color.RED>, <Color.GREEN>, <Color.BLUE>]

# 遍历枚举
for c in Color:
    print(c.name, c.value) 

知识测验

问题1:以下代码的输出是什么?

class A:
    _x = 10

    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, v):
        self._x = v * 2

a = A()
a.x = 5
print(a.x) 

问题2:关于 __slots__,哪个说法是正确的?

问题3:静态方法和类方法的主要区别是什么?

课后练习

✏️ 练习1:实现带验证的属性

创建一个 BankAccount 类,使用 @property 实现:

  • balance 属性:只读,存款和取款通过方法操作
  • 存款时验证金额为正数
  • 取款时验证余额充足
  • 使用 __slots__ 限制只允许有 name 和 _balance 属性
✏️ 练习2:抽象类设计

设计一个支付系统:

  • 抽象基类 Payment,有抽象方法 pay(amount) 和 refund()
  • 子类 Alipay:实现支付宝支付
  • 子类 WechatPay:实现微信支付
  • 子类 UnionPay:实现银联支付
  • 创建工厂类方法根据类型返回对应支付实例
✏️ 练习3:枚举应用

为订单系统创建枚举:

  • OrderStatus 枚举:PENDING, PAID, SHIPPED, DELIVERED, CANCELLED
  • PaymentMethod 枚举:WECHAT, ALIPAY, CARD, CASH
  • 创建一个 Order 类,使用这两个枚举
  • 实现订单状态流转验证(不能从 PENDING 直接到 DELIVERED)

章节总结

┌─────────────────────────────────────────────────────────────────┐
│                        本章要点                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  1. @property                                                   │
│     • 将方法伪装成属性                                           │
│     • @property.getter - 读取                                    │
│     • @name.setter - 写入                                        │
│     • @name.deleter - 删除                                       │
│     • 只定义 getter 可创建只读属性                                │
│                                                                 │
│  2. __slots__                                                   │
│     • 限制类能拥有的属性                                          │
│     • 可以节省内存                                                │
│     • 不影响子类(除非子类也定义 slots)                          │
│                                                                 │
│  3. 静态方法 vs 类方法                                           │
│     • @staticmethod - 无需 self/cls,纯粹函数                    │
│     • @classmethod - 第一个参数是 cls,可访问类属性              │
│                                                                 │
│  4. 抽象类                                                       │
│     • from abc import ABC, abstractmethod                       │
│     • 不能直接实例化                                              │
│     • 子类必须实现所有抽象方法                                    │
│                                                                 │
│  5. 元类                                                         │
│     • 类的类                                                      │
│     • 用于控制类的创建过程                                        │
│     • 用途:自动注册、验证、ORM等                                 │
│                                                                 │
│  6. 枚举                                                         │
│     • 固定的可能值集合                                            │
│     • IntEnum 支持数值比较                                       │
│     • auto() 自动分配编号                                        │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
                             
🐍 Python 在线代码编辑器
📝 运行结果