88 lines
3.6 KiB
ReStructuredText
88 lines
3.6 KiB
ReStructuredText
========
|
|
Overview
|
|
========
|
|
|
|
In order to fulfill its ambitious goal of bringing back the joy to writing classes, it gives you a class decorator and a way to declaratively define the attributes on that class:
|
|
|
|
.. include:: ../README.rst
|
|
:start-after: -code-begin-
|
|
:end-before: -getting-help-
|
|
|
|
|
|
.. _philosophy:
|
|
|
|
Philosophy
|
|
==========
|
|
|
|
**It's about regular classes.**
|
|
``attrs`` is for creating well-behaved classes with a type, attributes, methods, and everything that comes with a class.
|
|
It can be used for data-only containers like ``namedtuple``\ s or ``types.SimpleNamespace`` but they're just a sub-genre of what ``attrs`` is good for.
|
|
|
|
**The class belongs to the users.**
|
|
You define a class and ``attrs`` adds static methods to that class based on the attributes you declare.
|
|
The end.
|
|
It doesn't add metaclasses.
|
|
It doesn't add classes you've never heard of to your inheritance tree.
|
|
An ``attrs`` class in runtime is indistiguishable from a regular class: because it *is* a regular class with a few boilerplate-y methods attached.
|
|
|
|
**Be light on API impact.**
|
|
As convenient as it seems at first, ``attrs`` will *not* tack on any methods to your classes save the dunder ones.
|
|
Hence all the useful `tools <helpers>` that come with ``attrs`` live in functions that operate on top of instances.
|
|
Since they take an ``attrs`` instance as their first argument, you can attach them to your classes with one line of code.
|
|
|
|
**Performance matters.**
|
|
``attrs`` runtime impact is very close to zero because all the work is done when the class is defined.
|
|
Once you're instantiating it, ``attrs`` is out of the picture completely.
|
|
|
|
**No surprises.**
|
|
``attrs`` creates classes that arguably work the way a Python beginner would reasonably expect them to work.
|
|
It doesn't try to guess what you mean because explicit is better than implicit.
|
|
It doesn't try to be clever because software shouldn't be clever.
|
|
|
|
Check out `how-does-it-work` if you'd like to know how it achieves all of the above.
|
|
|
|
|
|
What ``attrs`` Is Not
|
|
=====================
|
|
|
|
``attrs`` does *not* invent some kind of magic system that pulls classes out of its hat using meta classes, runtime introspection, and shaky interdependencies.
|
|
|
|
All ``attrs`` does is:
|
|
|
|
1. take your declaration,
|
|
2. write dunder methods based on that information,
|
|
3. and attach them to your class.
|
|
|
|
It does *nothing* dynamic at runtime, hence zero runtime overhead.
|
|
It's still *your* class.
|
|
Do with it as you please.
|
|
|
|
|
|
On the ``attr.s`` and ``attr.ib`` Names
|
|
=======================================
|
|
|
|
The ``attr.s`` decorator and the ``attr.ib`` function aren't any obscure abbreviations.
|
|
They are a *concise* and highly *readable* way to write ``attrs`` and ``attrib`` with an *explicit namespace*.
|
|
|
|
At first, some people have a negative gut reaction to that; resembling the reactions to Python's significant whitespace.
|
|
And as with that, once one gets used to it, the readability and explicitness of that API prevails and delights.
|
|
|
|
For those who can't swallow that API at all, ``attrs`` comes with serious business aliases: ``attr.attrs`` and ``attr.attrib``.
|
|
|
|
Therefore, the following class definition is identical to the previous one:
|
|
|
|
.. doctest::
|
|
|
|
>>> from attr import attrs, attrib, Factory
|
|
>>> @attrs
|
|
... class SomeClass(object):
|
|
... a_number = attrib(default=42)
|
|
... list_of_numbers = attrib(default=Factory(list))
|
|
...
|
|
... def hard_math(self, another_number):
|
|
... return self.a_number + sum(self.list_of_numbers) * another_number
|
|
>>> SomeClass(1, [1, 2, 3])
|
|
SomeClass(a_number=1, list_of_numbers=[1, 2, 3])
|
|
|
|
Use whichever variant fits your taste better.
|