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 本章小结

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

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

12.6.1 元类 vs 类装饰器 vs init_subclass

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

12.6.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.7 练习题

基础题

  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.8 延伸阅读

12.8.1 描述符与元类

12.8.2 设计模式

12.8.3 高级OOP实践

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

12.8.4 框架源码研读

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

下一章:第13章 装饰器

青少年创意编程 - 高中Python组 - 江苏省宿城中等专业学校