第12章 高级面向对象特性

学习目标

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

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

12.1 描述符

12.1.1 描述符协议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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 类型验证描述符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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——类的类

1
2
3
4
5
6
7
8
9
10
# 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 自定义元类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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__钩子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 类装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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 单例模式

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

# 方法2:元类(见12.2.2)

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

12.4.2 工厂模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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 观察者模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 策略模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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 装饰器模式(结构型)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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 适配器模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 代理模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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 命令模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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 高性能描述符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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 元类与类型系统

1
2
3
4
5
6
7
8
9
10
11
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 结构化模式匹配与元编程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 常见陷阱与规避

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# 陷阱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章 装饰器