第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) p.age = 30
学术注记 :描述符是Python属性系统的基石。property、classmethod、staticmethod、__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 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()) print (type (Person)) print (type (p))
学术注记 :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)
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)
工程实践 :优先使用__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) 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)
12.4 设计模式 12.4.1 单例模式 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, abstractmethodclass 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())
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 defaultdictfrom 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 Protocolclass 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()} " )
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 annotationsclass 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 astimport inspectdef 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 常见陷阱与规避 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 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) class MetaA (type ): pass class MetaB (type ): pass class A (metaclass=MetaA): pass class B (metaclass=MetaB): pass class MetaC (MetaA, MetaB): pass class C (A, B, metaclass=MetaC): pass import threadingclass 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 练习题 基础题 实现描述符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 描述符与元类 12.8.2 设计模式 12.8.3 高级OOP实践 12.8.4 框架源码研读 Django ORM — 描述符实现字段访问SQLAlchemy — 元类实现声明式映射attrs库 — 类装饰器与描述符的高级应用dataclasses模块 — Python 3.7+数据类实现下一章:第13章 装饰器