Skip to content

第12章 高级面向对象特性

学习目标

完成本章学习后,读者将能够:

  • 理解描述符协议及其在property、classmethod、staticmethod中的底层作用
  • 掌握元类的基本原理与__init_subclass__钩子的使用
  • 运用类装饰器实现类的动态增强
  • 识别并实现常见设计模式的Python惯用写法

12.1 描述符

12.1.1 描述符协议

python
class Validated:
    """验证描述符:限制属性值范围"""
    def __init__(self, min_val: float | None = None, max_val: float | None = None):
        self.min_val = min_val
        self.max_val = max_val
    
    def __set_name__(self, owner, name: str):
        self.name = name
        self.storage_name = f"_{name}"
    
    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        return getattr(obj, self.storage_name, None)
    
    def __set__(self, obj, value):
        if self.min_val is not None and value < self.min_val:
            raise ValueError(f"{self.name} must be >= {self.min_val}")
        if self.max_val is not None and value > self.max_val:
            raise ValueError(f"{self.name} must be <= {self.max_val}")
        setattr(obj, self.storage_name, value)

class Person:
    age = Validated(0, 150)
    score = Validated(0, 100)
    
    def __init__(self, name: str, age: int, score: int):
        self.name = name
        self.age = age
        self.score = score

p = Person("Alice", 25, 85)
print(p.age)                        # 25
p.age = 30                          # 合法
# p.age = -5                        # ValueError!

学术注记:描述符是Python属性系统的基石。propertyclassmethodstaticmethod__slots__都是描述符的实现。描述符协议定义了__get____set____delete__方法。同时实现__get____set__的是数据描述符(优先级高于实例属性),只实现__get__的是非数据描述符(优先级低于实例属性)。

12.1.2 类型验证描述符

python
class Typed:
    """类型验证描述符"""
    def __init__(self, expected_type):
        self.expected_type = expected_type
    
    def __set_name__(self, owner, name: str):
        self.name = name
        self.storage_name = f"_{name}"
    
    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        return getattr(obj, self.storage_name, None)
    
    def __set__(self, obj, value):
        if not isinstance(value, self.expected_type):
            raise TypeError(f"{self.name} must be {self.expected_type.__name__}")
        setattr(obj, self.storage_name, value)

class Employee:
    name = Typed(str)
    age = Typed(int)
    salary = Typed(float)
    
    def __init__(self, name: str, age: int, salary: float):
        self.name = name
        self.age = age
        self.salary = salary

12.2 元类

12.2.1 type——类的类

python
# type创建类
Person = type("Person", (), {
    "__init__": lambda self, name: setattr(self, "name", name),
    "greet": lambda self: f"Hello, I'm {self.name}"
})

p = Person("Alice")
print(p.greet())                    # "Hello, I'm Alice"
print(type(Person))                 # <class 'type'>
print(type(p))                      # <class 'Person'>

学术注记:Python中类也是对象,type是所有类的元类(metaclass)。class Foo:等价于Foo = type("Foo", (), {})。自定义元类是type的子类,拦截类的创建过程。

12.2.2 自定义元类

python
class SingletonMeta(type):
    """单例元类"""
    _instances = {}
    
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]

class Database(metaclass=SingletonMeta):
    def __init__(self):
        self.connected = False

db1 = Database()
db2 = Database()
print(db1 is db2)                   # True

12.2.3 __init_subclass__钩子

python
class Plugin:
    """插件基类,自动注册子类"""
    registry: dict[str, type] = {}
    
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        Plugin.registry[cls.__name__] = cls

class EmailPlugin(Plugin):
    def send(self, message: str) -> None:
        print(f"Email: {message}")

class SMSPlugin(Plugin):
    def send(self, message: str) -> None:
        print(f"SMS: {message}")

print(Plugin.registry)              # {'EmailPlugin': <class 'EmailPlugin'>, ...}

工程实践:优先使用__init_subclass__而非元类。它更简单、更易理解,能满足大多数类定制需求。元类仅在需要控制类创建过程(如修改__new__)时使用。


12.3 类装饰器

python
def add_repr(cls):
    """自动添加__repr__"""
    def __repr__(self):
        attrs = ", ".join(f"{k}={v!r}" for k, v in self.__dict__.items())
        return f"{cls.__name__}({attrs})"
    cls.__repr__ = __repr__
    return cls

@add_repr
class Point:
    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y

p = Point(3, 4)
print(p)                            # Point(x=3, y=4)

def singleton(cls):
    """单例装饰器"""
    instances = {}
    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return get_instance

@singleton
class Config:
    def __init__(self):
        self.debug = False

c1 = Config()
c2 = Config()
print(c1 is c2)                     # True

12.4 设计模式

12.4.1 单例模式

python
# 方法1:模块级别(Python最惯用方式)
# config.py 中直接定义实例,Python模块天然单例

# 方法2:元类(见12.2.2)

# 方法3:装饰器(见12.3)

12.4.2 工厂模式

python
from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def speak(self) -> str: ...

class Dog(Animal):
    def speak(self) -> str:
        return "Woof!"

class Cat(Animal):
    def speak(self) -> str:
        return "Meow!"

# 工厂方法
class AnimalFactory:
    _registry: dict[str, type[Animal]] = {}
    
    @classmethod
    def register(cls, name: str, animal_cls: type[Animal]):
        cls._registry[name] = animal_cls
    
    @classmethod
    def create(cls, name: str) -> Animal:
        if name not in cls._registry:
            raise ValueError(f"Unknown animal: {name}")
        return cls._registry[name]()

AnimalFactory.register("dog", Dog)
AnimalFactory.register("cat", Cat)

dog = AnimalFactory.create("dog")
print(dog.speak())                  # "Woof!"

12.4.3 观察者模式

python
from collections import defaultdict
from typing import Callable

class EventEmitter:
    def __init__(self):
        self._listeners: dict[str, list[Callable]] = defaultdict(list)
    
    def on(self, event: str, callback: Callable) -> None:
        self._listeners[event].append(callback)
    
    def off(self, event: str, callback: Callable) -> None:
        self._listeners[event].remove(callback)
    
    def emit(self, event: str, *args, **kwargs) -> None:
        for callback in self._listeners[event]:
            callback(*args, **kwargs)

emitter = EventEmitter()
emitter.on("login", lambda name: print(f"Welcome, {name}!"))
emitter.on("login", lambda name: print(f"Logging: {name} logged in"))
emitter.emit("login", "Alice")

12.4.4 策略模式

python
from typing import Protocol

class SortStrategy(Protocol):
    def sort(self, data: list) -> list: ...

class BubbleSort:
    def sort(self, data: list) -> list:
        arr = data.copy()
        n = len(arr)
        for i in range(n):
            for j in range(n - i - 1):
                if arr[j] > arr[j + 1]:
                    arr[j], arr[j + 1] = arr[j + 1], arr[j]
        return arr

class PythonSort:
    def sort(self, data: list) -> list:
        return sorted(data)

class Sorter:
    def __init__(self, strategy: SortStrategy):
        self.strategy = strategy
    
    def sort(self, data: list) -> list:
        return self.strategy.sort(data)

sorter = Sorter(PythonSort())
print(sorter.sort([3, 1, 4, 1, 5, 9]))

12.4.5 装饰器模式(结构型)

python
class Coffee:
    def cost(self) -> float:
        return 5.0
    
    def description(self) -> str:
        return "Coffee"

class MilkDecorator:
    def __init__(self, coffee: Coffee):
        self._coffee = coffee
    
    def cost(self) -> float:
        return self._coffee.cost() + 2.0
    
    def description(self) -> str:
        return f"{self._coffee.description()} + Milk"

coffee = Coffee()
coffee = MilkDecorator(coffee)
print(f"{coffee.description()}: ${coffee.cost()}")  # Coffee + Milk: $7.0

12.4.6 适配器模式

python
class USPlug:
    def connect_110v(self) -> str:
        return "Connected to 110V"

class EUPlug:
    def connect_220v(self) -> str:
        return "Connected to 220V"

class EUToUSAdapter:
    def __init__(self, eu_plug: EUPlug):
        self._eu_plug = eu_plug
    
    def connect_110v(self) -> str:
        return f"Adapter: {self._eu_plug.connect_220v()} (converted)"

us_plug = USPlug()
print(us_plug.connect_110v())

eu_plug = EUPlug()
adapter = EUToUSAdapter(eu_plug)
print(adapter.connect_110v())

12.4.7 代理模式

python
class Database:
    def query(self, sql: str) -> list:
        print(f"Executing: {sql}")
        return [{"id": 1, "name": "Alice"}]

class DatabaseProxy:
    def __init__(self):
        self._real_db: Database | None = None
        self._cache: dict[str, list] = {}
    
    def query(self, sql: str) -> list:
        if sql in self._cache:
            print(f"Cache hit: {sql}")
            return self._cache[sql]
        
        if self._real_db is None:
            self._real_db = Database()
        
        result = self._real_db.query(sql)
        self._cache[sql] = result
        return result

db = DatabaseProxy()
db.query("SELECT * FROM users")
db.query("SELECT * FROM users")

12.4.8 命令模式

python
from typing import Callable
from dataclasses import dataclass, field

@dataclass
class Command:
    execute: Callable[[], None]
    undo: Callable[[], None]
    description: str = ""

class CommandManager:
    def __init__(self):
        self._history: list[Command] = []
        self._position: int = -1
    
    def execute(self, command: Command) -> None:
        self._history = self._history[:self._position + 1]
        command.execute()
        self._history.append(command)
        self._position += 1
    
    def undo(self) -> None:
        if self._position >= 0:
            self._history[self._position].undo()
            self._position -= 1
    
    def redo(self) -> None:
        if self._position < len(self._history) - 1:
            self._position += 1
            self._history[self._position].execute()

text = ""
manager = CommandManager()

def make_insert_command(text_ref: list, position: int, chars: str) -> Command:
    def execute():
        text_ref[0] = text_ref[0][:position] + chars + text_ref[0][position:]
    def undo():
        text_ref[0] = text_ref[0][:position] + text_ref[0][position + len(chars):]
    return Command(execute, undo, f"Insert '{chars}'")

text_ref = [""]
manager.execute(make_insert_command(text_ref, 0, "Hello"))
print(text_ref[0])
manager.undo()
print(text_ref[0])

12.5 前沿技术动态

12.5.1 高性能描述符

python
from __future__ import annotations

class ValidatedAttribute:
    __slots__ = ('name', 'validator')
    
    def __init__(self, validator: callable):
        self.validator = validator
    
    def __set_name__(self, owner, name):
        self.name = name
    
    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        return obj.__dict__[self.name]
    
    def __set__(self, obj, value):
        if not self.validator(value):
            raise ValueError(f"Invalid value for {self.name}")
        obj.__dict__[self.name] = value

12.5.2 元类与类型系统

python
from typing import TypeVar, Generic

T = TypeVar('T')

class GenericMeta(type):
    def __getitem__(cls, params):
        return type(f"{cls.__name__}[{params}]", (cls,), {'__params__': params})

class Container(Generic[T], metaclass=GenericMeta):
    def __init__(self, value: T):
        self.value = value

12.5.3 结构化模式匹配与元编程

python
from dataclasses import dataclass

@dataclass
class Pattern:
    type: str
    value: int

def match_pattern(obj: Pattern) -> str:
    match obj:
        case Pattern(type="A", value=v) if v > 10:
            return f"High A: {v}"
        case Pattern(type="A"):
            return "Low A"
        case Pattern(type="B", value=v):
            return f"B: {v}"
        case _:
            return "Unknown"

12.5.4 代码生成与AST操作

python
import ast
import inspect

def instrument_function(func):
    source = inspect.getsource(func)
    tree = ast.parse(source)
    
    for node in ast.walk(tree):
        if isinstance(node, ast.FunctionDef):
            node.body.insert(0, ast.parse("print('Entering function')"))
    
    code = compile(tree, '<string>', 'exec')
    exec(code, func.__globals__)
    return func.__globals__[func.__name__]

12.6 知识图谱

12.6.1 描述符协议

描述符协议与属性查找

描述符类型:
┌─────────────────────────────────────────────────────────────┐
│ 数据描述符 (Data Descriptor)                                 │
│ - 实现 __get__ 和 __set__ (或 __delete__)                   │
│ - 优先级最高,覆盖实例字典                                   │
│ - 示例: property, 描述符类                                   │
├─────────────────────────────────────────────────────────────┤
│ 非数据描述符 (Non-Data Descriptor)                           │
│ - 只实现 __get__                                            │
│ - 优先级低于实例字典                                         │
│ - 示例: 普通方法, classmethod, staticmethod                  │
└─────────────────────────────────────────────────────────────┘

属性查找优先级:
┌─────────────────────────────────────────────────────────────┐
│ 1. 数据描述符.__get__                                       │
│ 2. obj.__dict__[name]                                       │
│ 3. 非数据描述符.__get__                                     │
│ 4. type(obj).__dict__[name]                                 │
│ 5. 沿MRO向上查找                                            │
│ 6. __getattr__                                              │
│ 7. AttributeError                                           │
└─────────────────────────────────────────────────────────────┘

描述符方法:
┌─────────────────────────────────────────┐
│ __get__(self, obj, objtype=None)        │
│   obj: 实例,类访问时为None             │
│   objtype: 实例的类                     │
│                                         │
│ __set__(self, obj, value)               │
│   obj: 实例                             │
│   value: 要设置的值                     │
│                                         │
│ __delete__(self, obj)                   │
│   obj: 实例                             │
│                                         │
│ __set_name__(self, owner, name)         │
│   Python 3.6+: 获取属性名               │
└─────────────────────────────────────────┘

12.6.2 元类层次结构

Python类型系统

┌─────────────────────────────────────────────────────────────┐
│                        type                                 │
│  - 所有类的类型(元类)                                     │
│  - type是type的实例                                        │
│  - type是object的子类                                      │
└─────────────────────────────────────────────────────────────┘

          ┌───────────────┼───────────────┐
          ▼               ▼               ▼
    ┌──────────┐    ┌──────────┐    ┌──────────┐
    │  int     │    │  str     │    │ 自定义类  │
    │ (type实例)│    │ (type实例)│    │ (type实例)│
    └──────────┘    └──────────┘    └──────────┘
          │               │               │
          ▼               ▼               ▼
       1, 2, 3        "hello"         实例对象

类创建过程:
┌─────────────────────────────────────────────────────────────┐
│ class MyClass(Base, metaclass=Meta):                        │
│     attr = value                                            │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│ 1. Meta.__prepare__(name, bases) → namespace                │
│ 2. 执行类体,填充namespace                                  │
│ 3. Meta.__new__(mcs, name, bases, namespace) → cls          │
│ 4. Meta.__init__(cls, name, bases, namespace)               │
│ 5. __init_subclass__(**kwargs)                              │
│ 6. __set_name__ 调用                                        │
└─────────────────────────────────────────────────────────────┘

12.6.3 设计模式分类

常用设计模式速查

创建型:
┌─────────────────────────────────────────┐
│ 单例模式    确保只有一个实例             │
│ 工厂模式    创建对象的接口               │
│ 建造者模式  分步骤构建复杂对象           │
└─────────────────────────────────────────┘

结构型:
┌─────────────────────────────────────────┐
│ 装饰器模式  动态添加功能                 │
│ 适配器模式  转换接口                     │
│ 代理模式    控制访问                     │
│ 外观模式    简化接口                     │
└─────────────────────────────────────────┘

行为型:
┌─────────────────────────────────────────┐
│ 策略模式    可互换算法                   │
│ 观察者模式  发布-订阅                    │
│ 命令模式    封装请求                     │
│ 模板方法    算法骨架                     │
│ 责任链模式  处理链                       │
└─────────────────────────────────────────┘

12.7 技术选型指南

12.7.1 类定制技术选型

场景推荐方案原因
子类注册init_subclass简单直接
属性验证描述符可复用
添加方法类装饰器灵活
控制类创建元类最强大
简单扩展init_subclass首选

12.7.2 描述符vs property

场景推荐方案原因
简单属性验证@property简洁
可复用验证器描述符类DRY原则
多个相同属性描述符避免重复
计算属性@property直观
类型检查描述符可复用

12.7.3 单例实现选型

方式线程安全推荐度说明
模块级变量★★★★★最简单
new需加锁★★★☆☆需处理线程安全
装饰器需加锁★★★☆☆灵活
元类需加锁★★☆☆☆过度设计
类变量★★★★☆简单有效

12.7.4 设计模式选型

需求推荐模式替代方案
扩展功能装饰器模式子类化
替换算法策略模式条件判断
事件通知观察者模式回调函数
接口转换适配器模式包装类
延迟加载代理模式属性

12.8 常见问题与解决方案

12.8.1 描述符存储问题

python
# 问题:描述符实例变量被共享
class BadDescriptor:
    def __init__(self):
        self.value = None  # 所有实例共享!
    
    def __get__(self, obj, objtype=None):
        return self.value
    
    def __set__(self, obj, value):
        self.value = value

# 解决方案:使用实例字典存储
class GoodDescriptor:
    def __set_name__(self, owner, name):
        self.storage_name = f"_{name}"
    
    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        return getattr(obj, self.storage_name, None)
    
    def __set__(self, obj, value):
        setattr(obj, self.storage_name, value)

12.8.2 元类冲突问题

python
# 问题:多继承时元类冲突
class MetaA(type): pass
class MetaB(type): pass

class A(metaclass=MetaA): pass
class B(metaclass=MetaB): pass
# class C(A, B): pass  # TypeError: metaclass conflict!

# 解决方案:统一元类
class MetaC(MetaA, MetaB): pass
class C(A, B, metaclass=MetaC): pass

12.8.3 单例线程安全

python
import threading

# 问题:非线程安全的单例
class ThreadUnsafeSingleton:
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

# 解决方案:加锁
class ThreadSafeSingleton:
    _instance = None
    _lock = threading.Lock()
    
    def __new__(cls):
        with cls._lock:
            if cls._instance is None:
                cls._instance = super().__new__(cls)
        return cls._instance

# 最佳方案:使用模块级变量(天然线程安全)
# singleton.py
class _Singleton:
    pass
instance = _Singleton()

12.8.4 描述符与property共存

python
# 问题:描述符和property的优先级
class MyClass:
    @property
    def value(self):
        return self._value
    
    # 描述符会被property覆盖

# 解决方案:理解优先级
# 数据描述符 > 实例字典 > 非数据描述符
# property是数据描述符,优先级最高

12.8.5 类装饰器状态丢失

python
# 问题:装饰器覆盖类属性
def add_method(cls):
    def new_method(self):
        return "new"
    cls.new_method = new_method
    return cls

@add_method
class MyClass:
    original = "kept"

# 解决方案:使用functools.update_wrapper或手动保留
import functools

def add_method(cls):
    def new_method(self):
        return "new"
    cls.new_method = new_method
    functools.update_wrapper(cls, cls.__wrapped__ if hasattr(cls, '__wrapped__') else cls)
    return cls

12.9 本章小结

本章系统介绍了Python高级面向对象特性:

  1. 描述符:属性系统的基石,__get__/__set__/__delete__协议,数据描述符vs非数据描述符
  2. 元类type是类的类,__init_subclass__是更简单的替代方案
  3. 类装饰器:动态增强类的简洁方式
  4. 设计模式:单例、工厂、观察者、策略、装饰器、适配器、代理、命令模式

12.9.1 元类 vs 类装饰器 vs init_subclass

技术复杂度适用场景优先级
__init_subclass__子类注册、属性验证首选
类装饰器添加方法、修改属性次选
元类控制类创建过程、修改__new__最后手段

12.9.2 常见陷阱与规避

python
# 陷阱1:描述符存储名称冲突
class BadDescriptor:
    def __init__(self):
        self.value = None  # 所有实例共享!
    
    def __get__(self, obj, objtype=None):
        return self.value
    
    def __set__(self, obj, value):
        self.value = value

# 正确做法:使用实例字典存储
class GoodDescriptor:
    def __set_name__(self, owner, name):
        self.storage_name = f"_{name}"
    
    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        return getattr(obj, self.storage_name, None)
    
    def __set__(self, obj, value):
        setattr(obj, self.storage_name, value)

# 陷阱2:元类冲突
class MetaA(type): pass
class MetaB(type): pass

class A(metaclass=MetaA): pass
class B(metaclass=MetaB): pass
# class C(A, B): pass  # TypeError: metaclass conflict!

# 正确做法:统一元类
class MetaC(MetaA, MetaB): pass
class C(A, B, metaclass=MetaC): pass

# 陷阱3:单例线程安全
import threading

class ThreadUnsafeSingleton:
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

# 正确做法:加锁
class ThreadSafeSingleton:
    _instance = None
    _lock = threading.Lock()
    
    def __new__(cls):
        with cls._lock:
            if cls._instance is None:
                cls._instance = super().__new__(cls)
        return cls._instance

12.10 练习题

基础题

  1. 实现描述符Positive,确保属性值为正数。

  2. 使用__init_subclass__实现插件自动注册系统。

  3. 实现类装饰器,自动为类添加to_dict()方法。

进阶题

  1. 使用描述符实现简单的ORM字段映射(IntegerField、StringField)。

  2. 实现事件总线(EventBus),支持事件订阅、发布和取消。

  3. 使用策略模式实现不同的折扣策略(满减、百分比、固定金额)。

项目实践

  1. 迷你ORM框架:编写一个程序,要求:
    • 使用描述符实现字段类型(IntegerField、StringField、FloatField)
    • 使用元类或__init_subclass__自动收集字段定义
    • 支持Model基类,提供save()、delete()、查询方法
    • 自动生成CREATE TABLE语句
    • 支持主键和自动递增
    • 实现简单的查询构建器

思考题

  1. 数据描述符和非数据描述符的区别是什么?为什么property是数据描述符?

  2. 元类、__init_subclass__和类装饰器各有什么适用场景?

  3. Python中实现单例模式有哪些方式?各有什么优缺点?

12.11 延伸阅读

12.11.1 描述符与元类

12.11.2 设计模式

12.11.3 高级OOP实践

  • **《Python Cookbook》第8-9章》 — 类与元编程技巧
  • **《Effective Python》第3版》 — Python最佳实践
  • PEP 3115 — Metaclasses in Python 3000 (https://peps.python.org/pep-3115/) — 元类设计

12.11.4 框架源码研读

  • Django ORM — 描述符实现字段访问
  • SQLAlchemy — 元类实现声明式映射
  • attrs库 — 类装饰器与描述符的高级应用
  • dataclasses模块 — Python 3.7+数据类实现

下一章:第13章 装饰器

Python技术丛书 - 江苏省宿城中等专业学校