Pydantic 从__eq__中排除字段以避免递归错误

Pydantic exclude field from __eq__ to avoid recursion error

提问人:mousetail 提问时间:8/14/2022 更新时间:8/14/2022 访问量:1664

问:

我有一个这样的pydantic模型:

class SomeModel(pydantic.BaseModel):
    name: str
    content: str
    previous_model: typing.Optional["SomeModel"] = None

我的代码看起来像这样,这大大简化了,在我的实际代码中,有很多循环依赖关系偶尔偶然发生,而不是有目的地创建:

models = [
   SomeModel("bob", "2"),
   SomeModel("bob", "2"),
]
models[0].previous_model = models[1]
models[1].previous_model = models[0]

models.remove(models[0])

这会引发以下错误:

File "c:\Users\username\project-name\src\main.py", line 108, in run_all
    models.remove(models[0])
  File "pydantic\main.py", line 902, in pydantic.main.BaseModel.__eq__
  File "pydantic\main.py", line 445, in pydantic.main.BaseModel.dict
  File "pydantic\main.py", line 861, in _iter
  File "pydantic\main.py", line 736, in pydantic.main.BaseModel._get_value
  File "pydantic\main.py", line 445, in pydantic.main.BaseModel.dict
  File "pydantic\main.py", line 861, in _iter
  File "pydantic\main.py", line 736, in pydantic.main.BaseModel._get_value
  File "pydantic\main.py", line 445, in pydantic.main.BaseModel.dict
  File "pydantic\main.py", line 861, in _iter
  File "pydantic\main.py", line 736, in pydantic.main.BaseModel._get_value
  File "pydantic\main.py", line 445, in pydantic.main.BaseModel.dict
  File "pydantic\main.py", line 861, in _iter
  File "pydantic\main.py", line 736, in pydantic.main.BaseModel._get_value
  File "pydantic\main.py", line 445, in pydantic.main.BaseModel.dict
  File "pydantic\main.py", line 861, in _iter

... Snip several hunderd more lines

  File "pydantic\main.py", line 734, in pydantic.main.BaseModel._get_value
  File "pydantic\main.py", line 304, in pydantic.main.ModelMetaclass.__instancecheck__
RecursionError: maximum recursion depth exceeded

我真的根本不需要将该字段包含在相等中。有没有办法排除它,这样我的堆栈就不会溢出?在这种情况下,此字段与目的相等无关。previous_model

python-3.x stack-overflow 相等性 pydantic

评论


答:

3赞 Tom Wojcik 8/14/2022 #1

当 pydantic 生成时,它会遍历它的参数。这是有道理的,这是关键卖点之一。但它在你的场景中效果不佳,你必须省略它才能使其工作。您可以跳入或返回更简单的内容。给你一个非常简化的例子,它正在工作__repr__previous_node__repr__previous_node__repr_args____repr__


import typing

import pydantic


class SomeModel(pydantic.BaseModel):
    name: str
    content: str
    previous_model: typing.Optional["SomeModel"] = None

    def __repr__(self):
        return self.name


SomeModel.update_forward_refs()


models = [
   SomeModel(name="bob", content="2"),
   SomeModel(name="bob", content="2"),
]
models[0].previous_model = models[1]
models[1].previous_model = models[0]

models.remove(models[0])

print(models)

不太简单的版本,更接近 pydantic 的行为方式,但也适用于您的情况


import typing

import pydantic


class SomeModel(pydantic.BaseModel):
    name: str
    content: str
    previous_model: typing.Optional["SomeModel"] = None

    def __repr_args__(self, *args, **kwargs):
        args = self.dict(exclude={'previous_model',})
        return list(args.items())


SomeModel.update_forward_refs()


models = [
   SomeModel(name="bob", content="2"),
   SomeModel(name="bob", content="2"),
]
models[0].previous_model = models[1]
models[1].previous_model = models[0]

models.remove(models[0])