`pydantic` 中的私有属性

mkl*_*mkl 11 python pydantic

我试图通过以下方式获得以下行为pydantic.BaseModel

class MyClass:
    def __init__(self, value: T) -> None:
        self._value = value

    # Maybe:
    @property
    def value(self) -> T:
        return self._value

    # Maybe:
    @value.setter
    def value(self, value: T) -> None:
        # ...
        self._value = value
Run Code Online (Sandbox Code Playgroud)

如果T也是一个 pydantic 模型,那么使用字典的递归初始化应该可以工作:

# Initialize `x._value` with `T(foo="bar", spam="ham")`:
x = MyClass(value={"foo": "bar", "spam": "ham"})
Run Code Online (Sandbox Code Playgroud)

请注意,它_value是使用 kwargs 初始化的value。验证也必须可用于私有字段。

pydantic 文档(PrivateAttr等)似乎暗示 pydantic 永远不会公开私有属性。我确信这有一些技巧。但是有没有一种惯用的方式来实现 pydantic 中的行为呢?或者我应该只使用自定义类?

小智 4

不确定这个解决方案是否可取,基于: https: //github.com/samuelcolvin/pydantic/issues/1577 https://github.com/samuelcolvin/pydantic/issues/655

import inspect
from typing import Dict

from pydantic import BaseModel, PrivateAttr
from pydantic.main import no_type_check


class PatchedModel(BaseModel):
    @no_type_check
    def __setattr__(self, name, value):
        """
        To be able to use properties with setters
        """
        try:
            super().__setattr__(name, value)
        except ValueError as e:
            setters = inspect.getmembers(
                self.__class__,
                predicate=lambda x: isinstance(x, property) and x.fset is not None
            )
            for setter_name, func in setters:
                if setter_name == name:
                    object.__setattr__(self, name, value)
                    break
            else:
                raise e


class T(BaseModel):
    value1: str
    value2: int


class MyClassPydantic(PatchedModel):
    _value: T = PrivateAttr()

    def __init__(self, value: Dict, **kwargs):
        super().__init__(**kwargs)
        object.__setattr__(self, "_value", T(**value))

    @property
    def value(self) -> T:
        return self._value

    @value.setter
    def value(self, value: T) -> None:
        self._value: T = value

    # To avoid the PatchedModel(BaseModel) use instead
    # def set_value(self, value: T) -> None:
    #    self._value: T = value


if __name__ == "__main__":
    my_pydantic_class = MyClassPydantic({"value1": "test1", "value2": 1})
    print(my_pydantic_class.value)
    my_pydantic_class.value = T(value1="test2", value2=2)
    # my_pydantic_class.set_value(T(value1="test2", value2=2))
    print(my_pydantic_class.value)
Run Code Online (Sandbox Code Playgroud)