You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
``attr.s()`` now has a *field_transformer* hook that is called for all ``Attribute``s and returns a (modified or updated) list of ``Attribute`` instances.
2
+
``attr.asdict()`` has a *value_serializer* hook that can change the way values are converted.
3
+
Both hooks are meant to help with data (de)serialization workflows.
Copy file name to clipboardExpand all lines: docs/extending.rst
+104Lines changed: 104 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -159,3 +159,107 @@ Here are some tips for effective use of metadata:
159
159
... x = typed(int, default=1, init=False)
160
160
>>> attr.fields(C).x.metadata[MY_TYPE_METADATA]
161
161
<class 'int'>
162
+
163
+
164
+
Automatic field transformation and modification
165
+
------------------------------------------------
166
+
167
+
Attrs allows you to automatically modify or transform the class' fields while the class is being created.
168
+
You do this by passing a *field_transformer* hook to :func:`attr.define()` (and its friends).
169
+
Its main purpose is to automatically add converters to attributes based on their type to aid the development of API clients and other typed data loaders.
- *cls* is your class right *before* it is being converted into an attrs class.
175
+
This means it does not yet have the ``__attrs_attrs__`` attribute.
176
+
177
+
- *fields* is a list of all :class:`attr.Attribute` instances that will later be set to ``__attrs_attrs__``. You can modify these attributes any way you want:
178
+
You can add converters, change types, and even remove attributes completely or create new ones!
179
+
180
+
For example, let's assume that you really don't like floats:
181
+
182
+
.. doctest::
183
+
184
+
>>> defdrop_floats(cls, fields):
185
+
... return [f for f in fields if f.type notin {float, 'float'}]
186
+
...
187
+
>>> @attr.frozen(field_transformer=drop_floats)
188
+
... classData:
189
+
... a: int
190
+
... b: float
191
+
... c: str
192
+
...
193
+
>>> Data(42, "spam")
194
+
Data(a=42, c='spam')
195
+
196
+
A more realistic example would be to automatically convert data that you, e.g., load from JSON:
Attrs allows you to serialize instances of attrs classes to dicts using the :func:`attr.asdict()` function.
230
+
However, the result can not always be serialized since most data types will remain as they are:
231
+
232
+
.. doctest::
233
+
234
+
>>> import json
235
+
>>> import datetime
236
+
>>>
237
+
>>> @attr.frozen
238
+
... classData:
239
+
... dt: datetime.datetime
240
+
...
241
+
>>> data = attr.asdict(Data(datetime.datetime(2020, 5, 4, 13, 37)))
242
+
>>> data
243
+
{'dt': datetime.datetime(2020, 5, 4, 13, 37)}
244
+
>>> json.dumps(data)
245
+
Traceback (most recent call last):
246
+
...
247
+
TypeError: Object of type datetime is not JSON serializable
248
+
249
+
To help you with this, :attr:`attr.asdict()` allows you to pass a *value_serializer* hook. It has the signature :code:`your_hook(inst: type, field: Attribute, value: Any) -> Any`:
0 commit comments