一、类相关方法概述
1. 类操作的特殊方法
| | |
|---|
__init_subclass__ | | |
__subclasshook__ | | issubclass() |
__class_getitem__ | | cls[T] |
2. 元类 vs 类方法
# 元类方法(影响类创建)class Meta(type): def __new__(cls, name, bases, namespace): ...# 类相关方法(影响子类行为)class Base: def __init_subclass__(cls, **kwargs): ... @classmethod def __subclasshook__(cls, subclass): ...
二、__init_subclass__ 详解
1. 基本用法
class Parent: """父类,定义子类初始化钩子""" def __init_subclass__(cls, **kwargs): """ 当创建子类时自动调用 cls: 新创建的子类 kwargs: 类定义中的关键字参数 """ print(f"创建子类: {cls.__name__}") print(f" 参数: {kwargs}") # 可以添加类属性或修改类 cls.created_by = __name__ # 调用父类的 __init_subclass__ super().__init_subclass__(**kwargs)class Child1(Parent): """简单的子类""" passclass Child2(Parent, extra="data", version=1): """带参数的子类""" passprint(f"Child1.created_by: {Child1.created_by}")print(f"Child2.created_by: {Child2.created_by}")
2. 注册模式
class PluginRegistry: """插件注册器""" plugins = {} # 存储所有插件 categories = {} # 按类别存储 def __init_subclass__(cls, **kwargs): """子类自动注册""" super().__init_subclass__(**kwargs) # 获取插件元数据 plugin_name = getattr(cls, 'plugin_name', cls.__name__) plugin_category = getattr(cls, 'category', 'default') # 注册到全局字典 PluginRegistry.plugins[plugin_name] = cls # 按类别注册 if plugin_category not in PluginRegistry.categories: PluginRegistry.categories[plugin_category] = [] PluginRegistry.categories[plugin_category].append(cls) print(f"注册插件: {plugin_name} (类别: {plugin_category})")class BasePlugin(PluginRegistry): """插件基类""" plugin_name = None category = 'default' def process(self, data): """处理数据,子类实现""" raise NotImplementedErrorclass TextPlugin(BasePlugin): """文本处理插件""" plugin_name = 'text_processor' category = 'text' def process(self, data): return f"处理文本: {data}"class ImagePlugin(BasePlugin): """图像处理插件""" plugin_name = 'image_processor' category = 'image' def process(self, data): return f"处理图像: {data}"class AdvancedTextPlugin(TextPlugin): """高级文本插件""" plugin_name = 'advanced_text' category = 'text'print(f"\n所有插件: {list(PluginRegistry.plugins.keys())}")print(f"文本插件: {[p.__name__ for p in PluginRegistry.categories.get('text', [])]}")print(f"图像插件: {[p.__name__ for p in PluginRegistry.categories.get('image', [])]}")# 使用插件text_plugin = PluginRegistry.plugins['text_processor']()print(text_plugin.process("Hello"))
3. 验证和约束
class ValidatedClass: """带验证的类""" def __init_subclass__(cls, **kwargs): """子类创建时验证""" super().__init_subclass__(**kwargs) # 验证必需属性 required_attrs = ['name', 'version'] for attr in required_attrs: if not hasattr(cls, attr): raise TypeError(f"{cls.__name__} 必须定义 {attr} 属性") # 验证方法 required_methods = ['process', 'validate'] for method in required_methods: if not hasattr(cls, method) or not callable(getattr(cls, method)): raise TypeError(f"{cls.__name__} 必须实现 {method} 方法") # 版本检查 if cls.version < 1.0: raise ValueError(f"{cls.__name__} 版本必须 >= 1.0") print(f"验证通过: {cls.__name__}")class ValidPlugin(ValidatedClass): """有效的插件""" name = "test_plugin" version = 1.5 def process(self, data): return data def validate(self, data): return Truetry: class InvalidPlugin1(ValidatedClass): """缺少属性""" name = "bad_plugin" # 缺少 versionexcept TypeError as e: print(f"错误: {e}")try: class InvalidPlugin2(ValidatedClass): """缺少方法""" name = "bad_plugin" version = 2.0 # 缺少 process 方法except TypeError as e: print(f"错误: {e}")
4. 混入类和多重继承
class LoggingMixin: """日志混入类""" def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) cls.log_enabled = True print(f"{cls.__name__} 启用了日志")class TimingMixin: """计时混入类""" def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) cls.timing_enabled = True print(f"{cls.__name__} 启用了计时")class ValidationMixin: """验证混入类""" def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) cls.validation_enabled = True print(f"{cls.__name__} 启用了验证")class Service(LoggingMixin, TimingMixin, ValidationMixin): """服务类,使用多个混入""" def __init__(self, name): self.name = name def process(self): if hasattr(self, 'log_enabled') and self.log_enabled: print(f"[日志] 处理 {self.name}") if hasattr(self, 'timing_enabled') and self.timing_enabled: import time start = time.time() # 处理逻辑 time.sleep(0.1) elapsed = time.time() - start print(f"[计时] 耗时: {elapsed:.3f}s") if hasattr(self, 'validation_enabled') and self.validation_enabled: print(f"[验证] 验证数据")# 使用service = Service("test")service.process()
三、__subclasshook__ 详解
1. 基本用法
from abc import ABCclass MyInterface: """自定义接口""" @classmethod def __subclasshook__(cls, subclass): """ 检查 subclass 是否是 cls 的子类 返回 True/False/NotImplemented """ print(f"检查 {subclass.__name__} 是否是 {cls.__name__} 的子类") # 检查必要的方法 required_methods = ['process', 'validate'] for method in required_methods: if not any(hasattr(c, method) for c in subclass.__mro__): return False return Trueclass GoodClass: """实现了所需方法的类""" def process(self): pass def validate(self): passclass BadClass: """缺少必要方法的类""" def process(self): pass # 缺少 validateprint(f"GoodClass 是子类? {issubclass(GoodClass, MyInterface)}")print(f"BadClass 是子类? {issubclass(BadClass, MyInterface)}")
2. 实现鸭子类型
class Duck: """鸭子类""" @classmethod def __subclasshook__(cls, subclass): """任何有 quack 和 swim 方法的类都被认为是鸭子""" required = ['quack', 'swim'] for method in required: if not any(hasattr(c, method) for c in subclass.__mro__): return False return Trueclass Mallard: """野鸭 - 有 quack 和 swim""" def quack(self): return "Quack!" def swim(self): return "Swimming"class Dog: """狗 - 只有 swim,没有 quack""" def swim(self): return "Dog swimming"class Robot: """机器人 - 有 quack 和 swim""" def quack(self): return "Beep quack" def swim(self): return "Robot swimming"print("=== 鸭子类型检查 ===")print(f"Mallard is Duck? {issubclass(Mallard, Duck)}")print(f"Dog is Duck? {issubclass(Dog, Duck)}")print(f"Robot is Duck? {issubclass(Robot, Duck)}")# 实例检查mallard = Mallard()print(f"mallard 是 Duck 实例? {isinstance(mallard, Duck)}")
3. 抽象基类结合使用
from abc import ABC, abstractmethodimport collections.abcclass IterableInterface(ABC): """可迭代接口""" @classmethod def __subclasshook__(cls, subclass): if cls is IterableInterface: # 检查是否有 __iter__ 方法 if any("__iter__" in c.__dict__ for c in subclass.__mro__): return True return NotImplementedclass SequenceInterface(ABC): """序列接口""" @classmethod def __subclasshook__(cls, subclass): if cls is SequenceInterface: # 检查必要的序列方法 methods = ['__len__', '__getitem__'] for method in methods: if not any(method in c.__dict__ for c in subclass.__mro__): return False return True return NotImplementedclass MyList: """自定义列表""" def __init__(self, items): self._items = items def __len__(self): return len(self._items) def __getitem__(self, index): return self._items[index] def __iter__(self): return iter(self._items)class MyContainer: """自定义容器""" def __init__(self, items): self._items = items def __iter__(self): return iter(self._items)print("=== 接口检查 ===")print(f"MyList is Sequence? {issubclass(MyList, SequenceInterface)}")print(f"MyContainer is Sequence? {issubclass(MyContainer, SequenceInterface)}")print(f"MyContainer is Iterable? {issubclass(MyContainer, IterableInterface)}")# 与 collections.abc 对比print(f"\n=== 与 collections.abc 对比 ===")print(f"MyList is Sequence? {issubclass(MyList, collections.abc.Sequence)}")print(f"MyContainer is Iterable? {issubclass(MyContainer, collections.abc.Iterable)}")
四、__class_getitem__ 详解
1. 基本用法(Python 3.7+)
from typing import TypeVar, GenericT = TypeVar('T')class Box: """泛型盒子""" def __init__(self, value): self.value = value def __class_getitem__(cls, item): """ 支持 Box[T] 语法 item: 类型参数 """ print(f"创建参数化类型: {cls.__name__}[{item}]") # 创建新的参数化类 class ParametrizedBox(cls): def __init__(self, value): if not isinstance(value, item): raise TypeError(f"值必须是 {item} 类型") super().__init__(value) ParametrizedBox.__name__ = f"{cls.__name__}[{item.__name__}]" return ParametrizedBox# 使用IntBox = Box[int]StrBox = Box[str]int_box = IntBox(42)str_box = StrBox("hello")print(f"int_box 值: {int_box.value}")print(f"str_box 值: {str_box.value}")try: invalid_box = IntBox("string") # 类型错误except TypeError as e: print(f"错误: {e}")
2. 泛型容器实现
from typing import TypeVar, Generic, Listimport inspectT = TypeVar('T')K = TypeVar('K')V = TypeVar('V')class GenericList: """泛型列表""" _types = {} # 缓存参数化类型 def __init__(self): self._items = [] def __class_getitem__(cls, item_type): """创建参数化版本""" if item_type in cls._types: return cls._types[item_type] # 创建新的列表类 class TypedList(cls): def __init__(self): super().__init__() self.item_type = item_type def append(self, item): if not isinstance(item, self.item_type): raise TypeError(f"只能添加 {self.item_type.__name__} 类型") self._items.append(item) def __getitem__(self, index): return self._items[index] def __repr__(self): return f"{self.__class__.__name__}({self._items})" TypedList.__name__ = f"{cls.__name__}[{item_type.__name__}]" cls._types[item_type] = TypedList return TypedListclass GenericDict: """泛型字典""" def __init__(self): self._items = {} def __class_getitem__(cls, key_value): key_type, value_type = key_value class TypedDict(cls): def __init__(self): super().__init__() self.key_type = key_type self.value_type = value_type def __setitem__(self, key, value): if not isinstance(key, self.key_type): raise TypeError(f"键必须是 {self.key_type.__name__} 类型") if not isinstance(value, self.value_type): raise TypeError(f"值必须是 {self.value_type.__name__} 类型") self._items[key] = value def __getitem__(self, key): return self._items[key] def __repr__(self): return f"{self.__class__.__name__}({self._items})" TypedDict.__name__ = f"{cls.__name__}[{key_type.__name__}, {value_type.__name__}]" return TypedDict# 使用泛型列表IntList = GenericList[int]StrList = GenericList[str]int_list = IntList()int_list.append(1)int_list.append(2)int_list.append(3)print(f"IntList: {int_list}")try: int_list.append("string") # 类型错误except TypeError as e: print(f"错误: {e}")# 使用泛型字典StrIntDict = GenericDict[str, int]dict1 = StrIntDict()dict1["age"] = 25dict1["score"] = 90print(f"StrIntDict: {dict1}")try: dict1[123] = 100 # 键类型错误except TypeError as e: print(f"错误: {e}")
3. 类型检查和运行时信息
from typing import Any, get_args, get_originclass TypedContainer: """支持运行时类型信息的容器""" def __init__(self): self._origin = None self._args = None def __class_getitem__(cls, params): """创建参数化类并保存类型信息""" if not isinstance(params, tuple): params = (params,) # 创建新的参数化类 class Parametrized(cls): def __init__(self): super().__init__() self._origin = cls self._args = params @classmethod def get_origin(cls): return cls._origin @classmethod def get_args(cls): return cls._args Parametrized.__name__ = f"{cls.__name__}{params}" return Parametrized# 使用Vec = TypedContainer[int, str]vec = Vec()print(f"原始类型: {vec._origin.__name__}")print(f"类型参数: {vec._args}")# 使用 typing 模块的函数from typing import get_origin, get_argsclass MyList(TypedContainer[int, str]): passprint(f"get_origin(MyList): {get_origin(MyList)}")print(f"get_args(MyList): {get_args(MyList)}")
五、总结
1. 方法速查表
| | | |
|---|
__init_subclass__ | | | None |
__subclasshook__ | issubclass() | | bool |
__class_getitem__ | cls[T] | | |
2. 应用场景
| |
|---|
__init_subclass__ | |
__subclasshook__ | |
__class_getitem__ | |
3. 设计原则
__init_subclass__:用于子类自动注册和验证,避免手动注册
__subclasshook__:实现鸭子类型,让非继承关系的类也能通过检查
__class_getitem__:提供泛型支持,增强类型安全性
4. 常见陷阱
# 陷阱1:忘记调用 super().__init_subclass__class BadChild: def __init_subclass__(cls): # 没有调用 super(),可能破坏继承链 pass# 陷阱2:__subclasshook__ 返回错误类型class BadHook: @classmethod def __subclasshook__(cls, subclass): return "True" # 应该返回 bool 或 NotImplemented# 陷阱3:__class_getitem__ 不返回类class BadGeneric: def __class_getitem__(cls, item): return item # 应该返回类# 陷阱4:在 __init_subclass__ 中修改子类导致循环class Circular: def __init_subclass__(cls): # 创建新的子类可能导致无限循环 class NewSubclass(cls): pass
5. 最佳实践
class BestPractices: """最佳实践示例""" # 1. 总是调用 super().__init_subclass__ def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) # 自定义逻辑 # 2. __subclasshook__ 返回 NotImplemented 表示不处理 @classmethod def __subclasshook__(cls, subclass): if cls is BestPractices: # 只处理当前类 pass return NotImplemented # 3. __class_getitem__ 缓存参数化类 _cache = {} def __class_getitem__(cls, item): if item in cls._cache: return cls._cache[item] class Parametrized(cls): pass cls._cache[item] = Parametrized return Parametrized
这些类相关的特殊方法提供了强大的元编程能力,让我们能够控制类的创建、检查和泛型行为,是构建框架和库的重要工具。