Property descriptors for Pydantic
Project description
Added a field_property which gives property like functionality to pydantic models.
Note:
Due to the way pydantic is written the field_property will be slow and inefficient. Pydantic heavily uses and modifies the __dict__ attribute while overloading __setattr__. Additionally, Pydantic’s metaclass modifies the class __dict__ before class creation removing all property objects from the class definition. These changes prevent property objects and normal descriptors from working.
To work around this setting the field_property value must run the normal pydantic __setattr__, run the object __setattr__ to call the property setter, and must update the __dict__ with all field_property values. The Pydantic model __dict__ is only updated on __setattr__ and does not dynamically retrieve values.
Simple Example
from pydantic import PrivateAttr
from pydantic_property import PropertyModel, field_property
class Props(PropertyModel):
x: int = field_property('_x', default=0) # getter is created with '_x'
@x.setter
def x(self, value):
self._x = value # Note: matches field_property('_x')
y: int = field_property('_y', default=0) # Need to define '_y' for __private_attribute__
@y.getter
def y(self) -> int:
return getattr(self, '_y', 0)
@y.setter
def y(self, value):
if isinstance(value, float):
self._x = int((value % 1) * 10) # Must update all field_property with __dict__ for _x to be seen
self._y = int(value)
# Does not have a __private_attribute__ so '_z' will fail. we hack this later for testing
@field_property(default=0)
def z(self):
return getattr(self, '_z', 0)
@z.setter
def z(self, value):
self._z = value
# Have to add private attribute to allow _z to work.
# z.private_name = '_z'
# or
_z = PrivateAttr(default=0) # field_property.PrivateAttr
p = Props()
print(p)
p.x = 2
p.y = 1.5
print(p)
assert p.x == 5, '{} != {}'.format(p.x, 5)
assert p.y == 1, '{} != {}'.format(p.y, 1)
msg = p.dict()
assert p.x == msg['x'], '{} != {}'.format(p.x, msg['x'])
assert p.y == msg['y'], '{} != {}'.format(p.y, msg['y'])
assert p.z == msg['z'], '{} != {}'.format(p.z, msg['z'])
Pydantic Notes Example
Pydantic __dict__ Example below does not work, but shows what pydantic is doing in the background.
from pydantic import BaseModel, PrivateAttr
class MyModel(BaseModel):
x: int = 1
# Property doesn't really work. This is to show what pydantic does.
_y: int = PrivateAttr(default=1)
@property
def y(self):
return self._y
@y.setter
def y(self, value):
self._y = value
def set_point(self, x, y):
self.x = x
self._y = y
m = MyModel()
m.x = 2 # This actually sets self.__dict__['x'] = 2
assert m.dict() == {'x': 2}
m.y = 3 # Essentially this would change self.__dict__['y'] = 2
assert m.dict() == {'x': 2, 'y': 3}
assert m.__dict__ == {'x': 2, 'y': 3}
# This sets self.__dict__['x'] = 4 and the instance value m._y to 5, but does not change self.__dict__['y']
m.set_point(4, 5)
m.dict() == {'x': 4, 'y': 3} # y is not updated ._y is 5. self.__dict__['y'] still == 3
# This is why field_property must update __dict__ for all field_property values.
# This makes the field_property inefficient.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file pydantic_property-0.0.1.tar.gz
.
File metadata
- Download URL: pydantic_property-0.0.1.tar.gz
- Upload date:
- Size: 6.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.3.0 pkginfo/1.5.0.1 requests/2.23.0 setuptools/47.1.1 requests-toolbelt/0.9.1 tqdm/4.46.0 CPython/3.8.8
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | a91aa6554c8a96a21e807076c8710aeeccc065298cdb6bc63b1ae413c029498d |
|
MD5 | f46d31f44db1bd5ac5fd868675bfc7ac |
|
BLAKE2b-256 | 615dd36d2f43df3bb47fdd45a612aa71b3dea7c583975fa8df7256c72f066126 |
File details
Details for the file pydantic_property-0.0.1-py3-none-any.whl
.
File metadata
- Download URL: pydantic_property-0.0.1-py3-none-any.whl
- Upload date:
- Size: 10.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.3.0 pkginfo/1.5.0.1 requests/2.23.0 setuptools/47.1.1 requests-toolbelt/0.9.1 tqdm/4.46.0 CPython/3.8.8
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 724d17dcae2c34a2b6c2d9f1d3eea93262234833a53ab878e05ee4e38b00808c |
|
MD5 | fdcff1d7c86f8cb9d74934d124bc1809 |
|
BLAKE2b-256 | 35112051e76d04f39c3d255e6dd68dbe69d27936986edaa49866c4e19d2fb98f |