第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属性系统的基石。
property、classmethod、staticmethod、__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 = salary12.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) # True12.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) # True12.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.012.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] = value12.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 = value12.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高级面向对象特性:
- 描述符:属性系统的基石,
__get__/__set__/__delete__协议,数据描述符vs非数据描述符 - 元类:
type是类的类,__init_subclass__是更简单的替代方案 - 类装饰器:动态增强类的简洁方式
- 设计模式:单例、工厂、观察者、策略、装饰器、适配器、代理、命令模式
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._instance12.7 练习题
基础题
实现描述符
Positive,确保属性值为正数。使用
__init_subclass__实现插件自动注册系统。实现类装饰器,自动为类添加
to_dict()方法。
进阶题
使用描述符实现简单的ORM字段映射(IntegerField、StringField)。
实现事件总线(EventBus),支持事件订阅、发布和取消。
使用策略模式实现不同的折扣策略(满减、百分比、固定金额)。
项目实践
- 迷你ORM框架:编写一个程序,要求:
- 使用描述符实现字段类型(IntegerField、StringField、FloatField)
- 使用元类或
__init_subclass__自动收集字段定义 - 支持Model基类,提供save()、delete()、查询方法
- 自动生成CREATE TABLE语句
- 支持主键和自动递增
- 实现简单的查询构建器
思考题
数据描述符和非数据描述符的区别是什么?为什么property是数据描述符?
元类、
__init_subclass__和类装饰器各有什么适用场景?Python中实现单例模式有哪些方式?各有什么优缺点?
12.8 延伸阅读
12.8.1 描述符与元类
- Python描述符指南 (https://docs.python.org/3/howto/descriptor.html) — 官方描述符教程
- PEP 487 — Simpler customization of class creation (https://peps.python.org/pep-0487/) —
__init_subclass__和__set_name__ - 《Fluent Python》第23章 — 属性描述符深度解析
12.8.2 设计模式
- 《设计模式:可复用面向对象软件的基础》 (GoF) — 设计模式经典著作
- 《Head First设计模式》 — 设计模式入门经典
- Python Patterns (https://github.com/faif/python-patterns) — Python设计模式集合
- Refactoring Guru (https://refactoring.guru/design-patterns) — 设计模式可视化教程
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章 装饰器