pydantic BaseModel с переменной экземпляра

Я использую pydantic с fastapi. И я делаю Модель вот так.

# model.py

from multiprocessing import RLock
from pydantic import BaseModel

class ModelA(BaseModel):
    file_1: str = 'test'

    def __init__(self, **data: Any):
        super().__init__(**data)
        self._lock = RLock()
        self._instance_variable: int = 1

    @property
    def lock(self):
        return self._lock

    @property
    def instance_variable(self) -> int:
        with self.lock:
            return self._instance_variable

    @instance_variable.setter
    def instance_variable(self, v: int) -> int:
        assert isinstance(v, int)
        with self.lock:
            self._instance_variable = v

И я провожу такой тест

# test_model_a.py

def test_model_a():
    instance = ModelA()
    assert instance.json() == '{"field_1": "test"}'

После этого я запускаю тест, но экземпляр не может создать с этой ошибкой.

E ValueError: "ModelA" object has no field "_lock"

Итак, как я могу пройти этот тест ...? Помогите, пожалуйста...


person MR.LEE    schedule 25.02.2021    source источник
comment
Вы не должны делать свое утверждение с помощью сравнения строк; действительно, отступ JSON может отличаться (например, пробелы). Я бы рекомендовал вместо этого использовать _1 _._ 2_ и сравнить 2 объекта.   -  person Raphael Medaer    schedule 25.02.2021
comment
ваш ответ не имеет значения для моего вопроса. у меня вопрос: доза "PROTECTY.setter" не работает   -  person MR.LEE    schedule 25.02.2021


Ответы (1)


Вам необходимо использовать поле PrivateAttr. И вместо установщика используйте обходной путь с __setattr__

from multiprocessing import RLock, synchronize
from typing import Any

from pydantic import BaseModel, PrivateAttr


class ModelA(BaseModel):
    file_1: str = 'test'
    _lock: synchronize.RLock = PrivateAttr()
    _instance_variable: int = PrivateAttr()

    def __init__(self, **data: Any):
        super().__init__(**data)
        self._lock = RLock()
        self._instance_variable: int = 1

    @property
    def lock(self):
        return self._lock

    @property
    def instance_variable(self) -> int:
        with self.lock:
            return self._instance_variable

    def __setattr__(self, key, val):
        if key == "instance_variable":
            assert isinstance(val, int)
            with self.lock:
                self._instance_variable = val
            return
        super().__setattr__(key, val)

Вы также можете избежать использования пользовательского __init__


class ModelA(BaseModel):
    file_1: str = 'test'
    _lock: synchronize.RLock = PrivateAttr(default_factory=RLock)
    _instance_variable: int = PrivateAttr(1)
# ...
person alex_noname    schedule 25.02.2021
comment
ммм ... это такая хорошая идея. но я хочу property.setter ... разве это невозможно ...? - person MR.LEE; 25.02.2021
comment
Видимо это так github.com/samuelcolvin/pydantic/issues/1577 - person alex_noname; 25.02.2021