在对继承自的方法进行注解求值时,使用Python 3.12+的类型参数语法会出现“name 'T' is not defined”
我正在将代码库升级到Python 3.12,并将我们的泛型类迁移以使用新的 PEP 695 语法,例如:
class Box[T]:
...
而不是:
T = TypeVar('T')
class Box(Generic[T]):
...
大多数情况都能工作,但当我尝试在子类的方法上动态检查类型提示,且该方法被父类中的自定义装饰器包装时,遇到了一个 NameError:
from functools import wraps
import typing
# A standard route/event tracking decorator that preserves annotations
def track_event(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
# We explicitly sync annotations for an external API generator tool
wrapper.__annotations__ = func.__annotations__
return wrapper
# Parent generic class using Python 3.12+ syntax
class BaseService[T]:
@track_event
def process(self, data: T) -> str:
return f"Processed: {str(data)}"
# Subclass specifying the concrete type
class StringService(BaseService[str]):
pass
# Driving code that triggers the error
if __name__ == "__main__":
service = StringService()
print(service.process("hello")) # This works perfectly fine at runtime!
# This blows up:
hints = typing.get_type_hints(StringService.process)
print(hints)
在运行上面的代码时,运行时逻辑执行得很完美,但 typing.get_type_hints() 会抛出这段看起来很神秘的堆栈跟踪:
Traceback (most recent call last):
File "main.py", line 29, in <module>
hints = typing.get_type_hints(StringService.process)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/typing.py", line 1944, in get_type_hints
value = _eval_type(value, globalns, localns)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/typing.py", line 390, in _eval_type
return arg._evaluate(globalns, localns, recursive_guard)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/typing.py", line 837, in _evaluate
eval(self.__forward_code__, globalns, localns),
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<string>", line 1, in <module>
NameError: name 'T' is not defined
我尝试了以下方法:
- 如果我移除了
@track_event装饰器,typing.get_type_hints就能正确解析。 - 如果切换回旧的Python 3.11
T = TypeVar('T')语法,即使有装饰器,代码也能工作。 - 我使用
wrapper.__annotations__ = func.__annotations__来复制注解,因为我们的文档/API生成工具依赖于直接检查包装函数的属性。
为什么Python在这里找不到 T?显然在运行时它知道 T 是什么。是Python 3.12实现PEP 695的一个bug,还是我根本没有理解新泛型作用域的工作原理?
解决方案
这是一个已知的bug;正如 Alex Waygood 在 python/cpython#114053 中所解释:
我猜问题很可能在于
get_type_hints在全局作用域中查找,而没有在由 PEP-695 引入的新注解作用域中查找。定义TypeVars的旧方式不会遇到这个问题,因为TypeVars曾经是在全局作用域中定义;现在它们不再是这样。
它已在版本 3.12.5 中修复。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。