Python is one of the most popular programming languages in use today—and for good reasons: it's open source, it has a wide range of uses (such as web programming, business applications, games, scientific programming, and much more), and it has a vibrant and dedicated community supporting it. This community is the reason we have such a large, diverse range of software packages available in the Python Package Index (PyPI) to extend and improve Python and solve the inevitable glitches that crop up.
In this series, we'll look at seven PyPI libraries that can help you solve common Python problems. Today, we'll examine attrs, a Python package that helps you write concise, correct code quickly.
attrs
If you have been using Python for any length of time, you are probably used to writing code like:
class Book(object):
def __init__(self, isbn, name, author):
self.isbn = isbn
self.name = name
self.author = author
Then you write a __repr__ function; otherwise, it would be hard to log instances of Book:
def __repr__(self):
return f"Book({self.isbn}, {self.name}, {self.author})"
Next, you write a nice docstring documenting the expected types. But you notice you forgot to add the edition and published_year attributes, so you have to modify them in five places.
What if you didn't have to?
@attr.s(auto_attribs=True)
class Book(object):
isbn: str
name: str
author: str
published_year: int
edition: int
Annotating the attributes with types using the new type annotation syntax, attrs detects the annotations and creates a class.
ISBNs have a specific format. What if we want to enforce that format?
@attr.s(auto_attribs=True)
class Book(object):
isbn: str = attr.ib()
@isbn.validator
def pattern_match(self, attribute, value):
m = re.match(r"^(\d{3}-)\d{1,3}-\d{2,3}-\d{1,7}-\d$", value)
if not m:
raise ValueError("incorrect format for isbn", value)
name: str
author: str
published_year: int
edition: int
The attrs library also has great support for immutability-style programming. Changing the first line to @attr.s(auto_attribs=True, frozen=True) means that Book is now immutable: trying to modify an attribute will raise an exception. Instead, we can get a new instance with modification using attr.evolve(old_book, published_year=old_book.published_year+1), for example, if we need to push publication forward by a year.
In the next article in this series, we'll look at singledispatch, a library that allows you to add methods to Python libraries retroactively.
Comments are closed.