在对继承自的方法进行注解求值时,使用Python 3.12+的类型参数语法会出现“name 'T' is not defined”

后端开发 2026-07-09

我正在将代码库升级到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 Waygoodpython/cpython#114053 中所解释:

我猜问题很可能在于 get_type_hints 在全局作用域中查找,而没有在由 PEP-695 引入的新注解作用域中查找。定义TypeVars的旧方式不会遇到这个问题,因为TypeVars曾经是在全局作用域中定义;现在它们不再是这样。

它已在版本 3.12.5 中修复。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章