You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

动态创建Pydantic模型时update_forward_refs()报NameError问题

问题描述

通过create_model()动态创建Pydantic模型时,部分场景下调用update_forward_refs()无法解析嵌套在泛型类型内部的前向引用。

可正常运行的静态定义示例

from typing import List, Union
from pydantic import BaseModel
class Foo(BaseModel):
    foo: List["Bar"]

Bar = Union[Foo, int]
Foo.update_forward_refs()

抛出NameError的动态创建示例

逻辑上和上述静态定义等价,但运行时会抛出异常:

from typing import List, Union
from pydantic import create_model, BaseModel
Foo = create_model("Foo", foo=(List["Bar"], ...))
Bar = Union[Foo, int]
Foo.update_forward_refs()

报错信息

Traceback (most recent call last):
  File "test_forward_ref.py", line 11, in <module>
    Foo.update_forward_refs()
  File "pydantic\main.py", line 832, in pydantic.main.BaseModel.update_forward_refs
  File "pydantic\typing.py", line 382, in pydantic.typing.update_field_forward_refs
    or class checks.
  File "pydantic\typing.py", line 62, in pydantic.typing.evaluate_forwardref
    'MutableSet',
  File "C:\Users\Ian\.conda\envs\tso\lib\typing.py", line 518, in _evaluate
    eval(self.__forward_code__, globalns, localns),
  File "<string>", line 1, in <module>
NameError: name 'Bar' is not defined

触发条件

  • 复现环境:Python 3.8、Pydantic 1.8.2
  • 字段注解的泛型参数(如List["Bar"]中的"Bar")使用字符串形式前向引用时触发
  • 若字段注解直接为顶层字符串前向引用(如foo: "Bar")则无异常

问题原因

静态使用class关键字定义模型时,Pydantic会自动将模型绑定到定义所在模块的全局命名空间,update_forward_refs()默认会从该命名空间查找前向引用对应的名称。
通过create_model()动态创建模型时,默认不会自动绑定当前调用位置所在的模块命名空间。Pydantic对字段顶层的字符串前向引用做了特殊的命名空间兜底处理,但嵌套在泛型(如List/Dict)内部的前向引用不会触发该兜底逻辑,解析时找不到后定义的Bar变量,最终抛出NameError。


修复方案

两种方案均可解决该问题,可根据场景选择:

方案1:调用update_forward_refs时显式传入命名空间

解析前向引用时,手动把需要的名称传入方法,强制从指定命名空间查找:

from typing import List, Union
from pydantic import create_model
Foo = create_model("Foo", foo=(List["Bar"], ...))
Bar = Union[Foo, int]

# 方式1:精准传入需要解析的名称
Foo.update_forward_refs(Bar=Bar)

# 方式2:直接传入当前作用域所有变量(适合前向引用较多的场景)
# Foo.update_forward_refs(**locals(), **globals())

提示:如果在函数内部动态创建模型,推荐使用传入locals()和globals()的方式,避免局部作用域变量无法被识别。

方案2:创建模型时指定所属模块

动态创建模型时通过__module__参数指定当前模块,让模型和静态class定义拥有一致的命名空间绑定行为,后续同模块的其他前向引用不需要额外传参:

from typing import List, Union
from pydantic import create_model
import sys

# 绑定当前模块
current_module_name = sys.modules[__name__].__name__
Foo = create_model("Foo", foo=(List["Bar"], ...), __module__=current_module_name)
Bar = Union[Foo, int]
Foo.update_forward_refs()

内容的提问来源于stack exchange,提问作者Ian Goldby

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 09:36:17