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 教程。