https://github.com/wyfo/field-properties
Properties for dataclass fields
https://github.com/wyfo/field-properties
Last synced: 8 months ago
JSON representation
Properties for dataclass fields
- Host: GitHub
- URL: https://github.com/wyfo/field-properties
- Owner: wyfo
- License: mit
- Created: 2020-11-30T20:44:35.000Z (almost 5 years ago)
- Default Branch: main
- Last Pushed: 2021-04-05T14:08:17.000Z (over 4 years ago)
- Last Synced: 2024-10-07T09:08:12.854Z (12 months ago)
- Language: Python
- Size: 10.7 KB
- Stars: 3
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# field-properties
Properties for dataclass fields## Example
```python
from dataclasses import dataclassfrom field_properties import field_property, unwrap_property
@dataclass(frozen=True)
class Foo:
bar: int = field_property(default=0) # Same parameter than dataclasses.field@field_property(bar) # Equivalent to @field_property(bar).getter
def get_bar(self) -> int:
# unwrap_property(self).bar is equivalent to self._bar
# but it's type-checked and linter-friendly
return unwrap_property(self).bar
# When not declared, getter, setter and deleter are generated like the following:
# @field_property(bar).setter
# def set_bar(self, value: int):
# unwrap_property(self).bar = valueassert repr(Foo()) == repr(Foo(0)) == "Foo(bar=0)"
```## How does it works?
When a dataclass field has a default value, this value is assigned as a class attribute. `field_property` use this mechanism and create a field with a property as default value.
If a default value/factory is registered with `field_property`, the property setter will be called with it in `__init__`.
## Default getter/setter/deleter
`field_property` generates default getter/setter/deleter as simple wrappers around an instance attribute whose name is the field name prefixed with an underscore `_`. `unwrap_property` allows accessing this attribute in a type-checked/linter-friendly way.
By the way, if all the getter/setter/deleter are declared (and thus not generated), the protected attribute will not be created.
## Overriding
Field properties can be overridden, but the dataclass field must be overridden too — this is because a new field must be created, as property is declared as its default value (see [previous section](#how-does-it-works)).
```python
from dataclasses import dataclassfrom field_properties import field_property, unwrap_property
@dataclass
class Foo:
bar: int = field_property(default=0)
@field_property(bar)
def get_bar(self):
return unwrap_property(self).bar + 1class Foo2(Foo):
bar: int = field_property(default=0) # field property must be overridden
# field_property(inherit=True) is a shortcut to override a field
# and reusing all it's arguments
@field_property(bar)
def get_bar(self):
return unwrap_property(self).bar + 2
assert Foo() == 1
assert Foo2() == 2
```In fact, because field is redeclared, it's also possible to override normal fields with a field property
```python
from dataclasses import dataclassfrom field_properties import field_property
@dataclass
class Foo:
bar: int = 0class Foo2(Foo):
bar: int = field_property(default=1)assert Foo2() == 1
```## Raw property
`field_property` comes with a default implementation for its getter/setter/deleter. This can be turned off with `raw=False` parameter. Here is an example of a read_only field:
```python
from dataclasses import dataclass
from field_properties import field_property@dataclass
class Foo:
bar: int = field_property(init=False, raw=True)@field_property(bar)
def get_bar(self) -> int:
return 0assert Foo().bar == 0
assert str(Foo()) == "Foo(bar=0)"
try:
Foo().bar = 1
except AttributeError:
assert True
else:
assert False
```## [PEP 614](https://www.python.org/dev/peps/pep-0614/)
Decorator syntax `@field_property(bar).setter` is only valid in *Python 3.9*. Previous version can use the following hack:
```python
from dataclasses import dataclassfrom field_properties import field_property
@dataclass
class Foo:
bar: int = field_property()def set_bar(self, value: int):
...
field_property(bar).setter(set_bar)
```