Introducing Python 3.7’s Dataclasses

Python 3.7's dataclasses reduce repetition in your class definitions.

Newcomers to Python often are surprised by how little code is required to accomplish quite a bit. Between powerful built-in data structures that can do much of what you need, comprehensions to take care of many tasks involving iterables, and the lack of getter and setter methods in class definitions, it's no wonder that Python programs tend to be shorter than those in static, compiled languages.

However, this amazement often ends when people start to define classes in Python. True, the class definitions generally will be pretty short. But the __init__ method, which adds attributes to a new object, tends to be rather verbose and repetitive—for example:

class Book(object):
    def __init__(self, title, author, price):
        self.title = title = author
        self.price = price

Let's ignore the need for the use of self, which is an outgrowth of the LEGB (local, enclosing, global, builtins) scoping rules in Python and which isn't going away. Let's also note that there is a world of difference between the parameters title, author and price and the attributes self.title, and self.price.

What newcomers often wonder—and in the classes I teach, they often wonder about this out loud—is why you need to make these assignments at all. After all, can't __init__ figure out that the three non-self parameters are meant to be assigned to self as attributes? If Python's so smart, why doesn't it do this for you?

I've given several answers to this question through the years. One is that Python tries to make everything explicit, so you can see what's happening. Having automatic, behind-the-scenes assignment to attributes would violate that principal.

At a certain point, I actually came up with a half-baked solution to this problem, although I did specifically say that it was un-Pythonic and thus not a good candidate for a more serious implementation. In a blog post, "Making Python's __init__ method magical", I proposed that you could assign parameters to attributes automatically, using a combination of inheritance and introspection. This was was a thought experiment, not a real proposal. And yet, despite my misgivings and the skeletal implementation, there was something attractive about not having to write the same boilerplate __init__ method, with the same assignment of arguments to attributes.