Python __class_getitem__ 方法
最后修改于 2025 年 4 月 8 日
本综合指南探讨了 Python 的 __class_getitem__
方法,该方法在 PEP 560 中引入,用于类型提示和泛型类支持。我们将介绍基本用法、类型提示、泛型和实际示例。
基本定义
__class_getitem__
方法允许类支持下标表示法(方括号),用于类型提示目的。 当使用 Class[item]
语法时,会调用它。
主要特征:它是一个类方法(虽然没有用 @classmethod 装饰),接收类作为第一个参数,并返回该类的专用版本。 它支持泛型类型注解,而无需运行时类型检查。
基本 __class_getitem__ 实现
这是一个简单的实现,展示了 __class_getitem__
的工作方式。 这演示了该方法的基本语法和行为。
class GenericBox: def __class_getitem__(cls, item): print(f"Creating GenericBox specialized for {item}") return f"GenericBox[{item.__name__}]" print(GenericBox[int]) # GenericBox[int] print(GenericBox[str]) # GenericBox[str]
此示例展示了 __class_getitem__
如何拦截方括号表示法。当调用 GenericBox[int]
时,它会调用 __class_getitem__
,并将 int
作为 item。
该方法在此处返回一个字符串以进行演示,但在实际使用中,它通常会返回该类的专用版本或 typing._GenericAlias。
使用 __class_getitem__ 进行类型提示
__class_getitem__
主要用于支持 Python 中的类型提示。 以下是如何为自定义容器类型实现它。
from typing import Any, TypeVar, Generic T = TypeVar('T') class Box(Generic[T]): def __init__(self, content: T): self.content = content @classmethod def __class_getitem__(cls, item): return super().__class_getitem__(item) def __repr__(self): return f"Box({self.content!r})" # Type hints work with the custom container def process_box(box: Box[int]) -> int: return box.content * 2 box = Box(42) print(process_box(box))
此示例显示了一个通用的 Box 类,可以使用类型进行参数化。 __class_getitem__
实现委托给父 Generic 类来正确处理类型参数化。
类型检查器会将 Box[int]
理解为包含整数的 box,而在运行时它会返回一个 typing._GenericAlias 实例。
自定义泛型类
您可以通过实现 __class_getitem__
来创建自己的泛型类,而无需从 typing.Generic
继承。
class Pair: def __init__(self, first, second): self.first = first self.second = second def __class_getitem__(cls, items): if not isinstance(items, tuple): items = (items,) if len(items) != 2: raise TypeError("Pair requires exactly two type arguments") first_type, second_type = items return type(f"Pair[{first_type.__name__}, {second_type.__name__}]", (cls,), {'__annotations__': {'first': first_type, 'second': second_type}}) def __repr__(self): return f"Pair({self.first!r}, {self.second!r})" IntStrPair = Pair[int, str] pair = IntStrPair(42, "answer") print(pair) # Pair(42, 'answer')
此自定义 Pair 类实现了自己的泛型类型支持。 __class_getitem__
方法创建一个新的子类,该子类具有基于提供的类型参数的类型注解。
在运行时,Pair[int, str]
创建一个具有适当类型注解的新类,类型检查器可以使用该类进行静态类型检查。
运行时类型检查
虽然 __class_getitem__
主要用于类型提示,但您可以将其与运行时类型检查结合使用,以获得更健壮的代码。
class CheckedList: def __init__(self, items): self.items = list(items) def __class_getitem__(cls, item_type): class CheckedListSubclass(cls): def append(self, item): if not isinstance(item, item_type): raise TypeError(f"Expected {item_type.__name__}, got {type(item).__name__}") super().append(item) return CheckedListSubclass def append(self, item): self.items.append(item) IntList = CheckedList[int] numbers = IntList([1, 2, 3]) numbers.append(4) # numbers.append("four") # Raises TypeError
此示例创建一个类型检查列表,该列表在运行时验证项目是否与指定的类型匹配。 __class_getitem__
方法创建一个具有运行时类型检查的子类。
当调用 CheckedList[int]
时,它返回一个子类,该子类验证所有附加的项目都是整数。 这将静态类型提示与运行时验证相结合。
前向引用和字符串字面量
__class_getitem__
可以使用字符串字面量处理前向引用,这对于引用尚未定义的类型的类型提示非常有用。
class Node: def __class_getitem__(cls, item): if isinstance(item, str): # Handle forward references return f"Node['{item}']" return f"Node[{item.__name__}]" class Tree: pass # Using forward reference print(Node["Tree"]) # Node['Tree'] # Using actual type print(Node[Tree]) # Node[Tree]
此示例演示了 __class_getitem__
如何处理实际类型和字符串字面量。 字符串字面量用于类型提示中的前向引用。
类型检查器将识别此模式并正确处理类型注解中的前向引用,而在运行时它只会返回一个格式化的字符串。
最佳实践
- 类型提示优先: 主要目的是类型提示,而不是运行时行为
- 一致的返回值: 返回 typing._GenericAlias 以获得兼容性
- 前向引用: 支持字符串字面量进行前向引用
- 文档行为: 清楚地记录任何特殊类型处理
- 性能: 如果性能至关重要,则缓存创建的泛型类型
资料来源
作者
列出所有 Python 教程。