The package Python Attrs allows you to create advanced data classes using simple annotations. Of course, python has its own native data class module as well, but the Python attrs package offers a couple of extra features you might like!
Table of Contents
Install attrs
The attrs package is not part of the base library, so you will need to install it with the pip install command or something similar, like Pipenv. You probably also want to create a virtual environment, so it won’t interfere with other projects you have. The package is called attrs, so installing it will look like this:
$ pip install attrs # or with pipenv: $ pipenv install attrs
Attrs vs data classes
The authors of attrs
have, in fact, worked on the PEP that introduced data classes into Python. Python’s native data classes are intentionally kept simpler and easier to understand while attrs offering the full range of features you might want!
Some of the reasons to choose Python attrs
over the built-in data classes are:
- You are using a Python version from before 3.7. Attrs has you covered since it supports all mainstream Python versions, including CPython 2.7 and PyPy.
- You want more features: attrs offers validators and converters
- You want optimal performance and minimal memory usage using attrs slotted classes
A basic Python attrs example
Let’s look at a very basic example first:
import attr @attr.s class Person(object): name = attr.ib(default='John') surname = attr.ib(default='Doe') age = attr.ib(init=False) p = Person() print(p) p = Person('Bill', 'Gates') p.age = 60 print(p) # Output: # Person(name='John', surname='Doe', age=NOTHING) # Person(name='Bill', surname='Gates', age=60)
A couple of observations:
- The syntax is less elegant and more verbose than that of data classes, but you get extra features in return.
- Similar to data classes, you get a nicely formatted representation of your data when you print it.
- The attrs package uses smartly picked names like
attr.ib
, so you only need to import attr. You can, alternatively, import the full names. For example withfrom attr import attrib, attrs
, and use those names instead. The functionality is the same.
Next let’s look at the most important features this package offers over regular data classes: validators and converters.
Python attrs validator example
You can add validators to your attrs data class in two ways:
- Using a decorator
- By providing a callable function
I’ll demonstrate the callable function method here first. Attrs offers several validators out of the box, of which we’ll use the instance_of
validator in the following example:
>>> @attr.s ... class C(object): ... x = attr.ib(validator=attr.validators.instance_of(int)) >>> C(42) C(x=42) >>> C("a string") Traceback (most recent call last): ... TypeError: ("'x' must be <type 'int'> (got 'a string' that is a <type 'str'>).", ...
Since we tried to create an object C with a string value for x, the instance_of validator throws an error because it requires an int type instead of a string.
Let’s now define our own validator:
import attr @attr.s class DividableByTwo(object): x = attr.ib() @x.validator def check(self, attribute, value): if value % 2 != 0: raise ValueError(f'{value} is not dividable by 2') print (DividableByTwo(60)) print (DividableByTwo(11)) # Output will be something like: # DividableByTwo(x=60) # ... # ValueError: 11 is not dividable by 2
Python attrs converter example
A converter takes the value that is set and converts it automatically. You can use this for all kinds of purposes. One example is to automatically convert a value to an int. Again, let’s start with using a callable function, in this case, we simply use Python’s int() function:
import attr @attr.s class C(object): x = attr.ib(converter=int) c = C("1") print(c) # Output: # C(x=1)
Our input (the string “1”) was converted to an integer automatically. Because converters are run before validators, you can validate the final value after conversion. E.g., you could combine the above two examples to first convert any input to int, and then check if the value is dividable by two.
Using slots with Python attrs
Finally, you can tell attrs to use slotted classes. Slotted classes have some advantages over regular classes:
- They have a small memory footprint
- They are faster
In short, with a slotted class you explicitly state which instance attributes you expect your object instances to have. This way, Python can leave out some checks and such, resulting in less memory usage and slight speed increases. You can find more details in the attrs documentation here.
However, slotted classes come with caveats too, especially when you manually create them. Luckily, attrs offers us a simple way to enable the feature:
import attr @attr.s(slots=True) class YourClassName: ...
Keep learning
- Our article on Python data classes
- Our tutorial on Python classes and objects
- For more examples, check out the attrs examples page