It is very rare that you learn something that completely changes how you program. Reading this post about the attrs package in Python was a revelation to me.
Coming from C++, I am not too big a fan on returning everything as lists and tuples. In many cases, you want to have structure and attributes and the class in Python is a good fit for this. However, creating a proper class with attributes that has all the necessary basic methods is a pain.
This is where attrs comes in. Add its decorator to the class and designate the attributes of the class using its methods and it will generate all the necessary dunder methods for you. You can also get some nice type checking and default values for the attributes too.
First, let us get the biggest confusion about this package out of the way! It is called attrs when you install it cause there is already another existing package called attr (the singular). But when you import and use it, then it is called attr. I know it is irritating, but this is the way it is.
To install it:
$ python3 -m pip install attrs
attr.s. I read it is as the plural attrs. And to declare the class attributes, use
attr.ibmethod. I read it as attribute.
@attr.s class Creature: eyes = attr.ib() legs = attr.ib()
c = Creature(2, 4)
c = Creature(legs=6, eyes=1000)
c = Creature() TypeError: __init__() missing 2 required positional arguments: 'eyes' and 'legs'
@attr.s class Creature: eyes = attr.ib(default=2) legs = attr.ib(default=6) c = Creature()
Note that if there are some rules you run up against if you provide default values for some attributes and not to others.
__repr__dunder method is automatically generated for your class. So, you can print any object:
c = Creature(3, 6) print(c) Creature(eyes=3, legs=6)
This is for me the killer feature! This is far more informational than just looking at a bunch of list or dict values.
c = Creature(2, 4) c.eyes = 10 print(c.legs)
c1 = Creature(2, 4) c2 = Creature(3, 9) c1 == c2
instance_ofvalidators provided by the package:
@attr.s class Creature: eyes = attr.ib(validator=attr.validators.instance_of(int)) legs = attr.ib() c = Creature(3.14, 6) TypeError: ("'eyes' must be <class 'int'> (got 3.14 that is a <class 'float'>)."
@attr.s(slots=True) class Creature: eyes = attr.ib() legs = attr.ib()
import inspect print(inspect.getsource(Creature.__init__)) print(inspect.getsource(Creature.__eq__)) print(inspect.getsource(Creature.__gt__))
print(attr.fields(Creature)) (Attribute(name='eyes', default=NOTHING, validator=<instance_of validator for type <class 'int'>>, repr=True, cmp=True, hash=True, init=True, convert=None), Attribute(name='legs', default=NOTHING, validator=None, repr=True, cmp=True, hash=True, init=True, convert=None))
There is a lot more stuff in this awesome must-use package that can be read here
Update: dataclasses were introduced in Python 3.7 and they seem to have a lot of features of attrs.
Tried with: attrs 16.1.0, Python 3.5.2 and Ubuntu 16.04