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
Copy file name to clipboardExpand all lines: docs/init.rst
+59-9
Original file line number
Diff line number
Diff line change
@@ -34,10 +34,10 @@ For similar reasons, we strongly discourage from patterns like::
34
34
35
35
pt = Point(**row.attributes)
36
36
37
-
which couples your classes to the data model.
37
+
which couples your classes to the database data model.
38
38
Try to design your classes in a way that is clean and convenient to use -- not based on your database format.
39
39
The database format can change anytime and you're stuck with a bad class design that is hard to change.
40
-
Embrace classmethods as a filter between reality and what's best for you to work with.
40
+
Embrace functions and classmethods as a filter between reality and what's best for you to work with.
41
41
42
42
If you look for object serialization, there's a bunch of projects listed on our ``attrs`` extensions `Wiki page`_.
43
43
Some of them even support nested schemas.
@@ -315,13 +315,62 @@ A converter will override an explicit type annotation or ``type`` argument.
315
315
{'return': None, 'x': <class 'str'>}
316
316
317
317
318
-
Post-Init Hook
319
-
--------------
318
+
Hooking Yourself Into Initialization
319
+
------------------------------------
320
320
321
321
Generally speaking, the moment you think that you need finer control over how your class is instantiated than what ``attrs`` offers, it's usually best to use a classmethod factory or to apply the `builder pattern <https://en.wikipedia.org/wiki/Builder_pattern>`_.
322
322
323
-
However, sometimes you need to do that one quick thing after your class is initialized.
324
-
And for that ``attrs`` offers the ``__attrs_post_init__`` hook that is automatically detected and run after ``attrs`` is done initializing your instance:
323
+
However, sometimes you need to do that one quick thing before or after your class is initialized.
324
+
And for that ``attrs`` offers three means:
325
+
326
+
- ``__attrs_pre_init__`` is automatically detected and run *before* ``attrs`` starts initializing.
327
+
This is useful if you need to inject a call to ``super().__init__()``.
328
+
- ``__attrs_post_init__`` is automatically detected and run *after* ``attrs`` is done initializing your instance.
329
+
This is useful if you want to derive some attribute from others or perform some kind of validation over the whole instance.
330
+
- ``__attrs_init__`` is written and attached to your class *instead* of ``__init__``, if ``attrs`` is told to not write one (i.e. ``init=False`` or a combination of ``auto_detect=True`` and a custom ``__init__``).
331
+
This is useful if you want full control over the initialization process, but don't want to set the attributes by hand.
332
+
333
+
334
+
Pre Init
335
+
~~~~~~~~
336
+
337
+
The sole reason for the existance of ``__attrs_pre_init__`` is to give users the chance to call ``super().__init__()``, because some subclassing-based APIs require that.
338
+
339
+
.. doctest::
340
+
341
+
>>> @attr.s
342
+
... classC(object):
343
+
... x = attr.ib()
344
+
... def__attrs_pre_init__(self):
345
+
... super().__init__()
346
+
>>> C(42)
347
+
C(x=42)
348
+
349
+
If you need more control, use the custom init approach described next.
350
+
351
+
352
+
Custom Init
353
+
~~~~~~~~~~~
354
+
355
+
If you tell ``attrs`` to not write an ``__init__``, it will write an ``__attrs_init__`` instead, with the same code that it would have used for ``__init__``.
356
+
You have full control over the initialization, but also have to type out the types of your arguments etc.
357
+
Here's an example of a manual default value:
358
+
359
+
.. doctest::
360
+
361
+
>>> from typing import Optional
362
+
>>> @attr.s(auto_detect=True) # or init=False
363
+
... classC(object):
364
+
... x = attr.ib()
365
+
...
366
+
... def__init__(self, x: int=42):
367
+
... self.__attrs_init__(x)
368
+
>>> C()
369
+
C(x=42)
370
+
371
+
372
+
Post Init
373
+
~~~~~~~~~
325
374
326
375
.. doctest::
327
376
@@ -370,13 +419,14 @@ Order of Execution
370
419
371
420
If present, the hooks are executed in the following order:
372
421
373
-
1. For each attribute, in the order it was declared:
422
+
1. ``__attrs_pre_init__`` (if present on *current* class)
423
+
2. For each attribute, in the order it was declared:
374
424
375
425
a. default factory
376
426
b. converter
377
427
378
-
2. *all* validators
379
-
3. ``__attrs_post_init__``
428
+
3. *all* validators
429
+
4. ``__attrs_post_init__`` (if present on *current* class)
380
430
381
431
Notably this means, that you can access all attributes from within your validators, but your converters have to deal with invalid values and have to return a valid value.
0 commit comments