' 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

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

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

注意_value是使用kwargsvalue初始化的。验证也必须对私有字段可用。

pydantic文档(PrivateAttr等)似乎暗示pydantic永远不会暴露私有属性。我相信一定有办法解决这个问题。但是在pydantic中有没有一种习惯的方法来实现这种行为呢?或者我应该使用自定义类?

不确定这个解决方案是否可取,基于:https://github.com/samuelcolvin/pydantic/issues/1577https://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)

我最终得到了这样的东西,它就像一个私有字段,但我可以通过公共方法改变它:


import inspect
from typing import Optional
from uuid import UUID
from pydantic import BaseModel, Field

class Entity(BaseModel):
"""Base entity class."""
def __setattr__(self, name, value):
if "self" not in inspect.currentframe().f_back.f_locals:
raise Exception("set attr is protected")
super().__setattr__(name, value)

class PostId(UUID):
"""Post unique id."""

class Post(Entity):
"""Post."""
post_id: PostId = Field(description='unique post id')
title: Optional[str] = Field(None, description='title')
def change_title(self, new_title: str) -> None:
"""Changes title."""
self.title = new_title

我只是在看inspect.currentframe().f_back.f_locals和寻找self键。

由access触发

用这个小测试测试:

from uuid import uuid4
import pytest
import post_pydantic

def test_pydantic():
"""Test pydantic varriant."""
post_id = uuid4()
post = post_pydantic.Post(post_id=post_id)
with pytest.raises(Exception) as e:
post.post_id = uuid4()
assert post.post_id == post_id
assert e.value.args[0] == "set attr is protected"
new_title = "New title"
post.change_title(new_title)
assert post.title == new_title

相关内容

  • 没有找到相关文章

最新更新