Why I prefer attrs over dataclasses

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
Welcome back to mCoding! Hope you're having a great day. attrs is a library to help you write Python classes in a simpler, less error-prone, and less boilerplate way. It offers similar functionality  to the built-in dataclasses library. And in fact, dataclasses were originally based on attrs. Very commonly, when I write a class, I need to define an __init__, __eq__, a __repr__, maybe a __hash__, __lt__ etc., And most often, all those functions are complete boilerplate. Who can even remember all of them? And if I do, yeah, maybe procrastinate on actually filling them in. __init__ means self.something= something, for each argument, __eq__ means check self.something= other.something, and so on. If I want to add or remove an attribute, I now have to add or remove it in all of these places, which is both a huge waste of my time as well as a very likely place where I make a typo and break everything. Enter attrs and other dataclass like solutions. Instead of writing all that, they write it for you. Delete all that flaky, error-prone code. Here's how you write it with attrs. Let's watch that again because  it was so satisfying. Nice! You can also customize the generated code using parameters to define or by using these field objects on individual attributes. This is implemented by posting wrong code to Stack Overflow and waiting for someone else to write it for you. Just kidding, it's this `@define` decorator. It could also be done with a metaclass. But attrs and dataclasses both take the decorator approach instead. At this point, you may feel your knee jerking upwards, telling you that this is too much forbidden magic. But fear not. After the decorator runs, the result is the plain old class that you would have written yourself. We can even inspect, say, the __init__ method to see the source code it generated: self.something = something, and so on. Exactly the boilerplate we wanted to avoid. No magic is happening when you use the class. The plain old boring repetitive code is still there. And the resulting class is just as  efficient as if you had handwritten it. It's just that you didn't have to handwrite it. While the logic of this decorator is complex, ultimately all it's doing is using your annotations and whatever parameters to write  the source code of each function in the class. And then it executes that code to create the  function objects and stick them onto the class. But pretty much everything I've said applies just as well to dataclasses as it does to attrs. So why do I prefer attrs? To be clear, dataclasses are still great. And once they came out when they were first released, I started using attrs less and less just due to their convenience of being built into Python. But in the past year, the tables have really started to turn. And I'm choosing attrs over data classes more and more. The first reason is slots, which is kind of a superficial thing because both attrs and dataclasses support slots. But attrs make slotted classes the default, and good defaults are surprisingly important. I have a whole video on slots if you want to check out the details. But the short version is that normal Python classes are basically syntactic sugar around a dictionary. And just like a dictionary, they can  have arbitrary new keys/attributes added to them. In contrast, a slotted class has a fixed set of instance attributes, which makes it slightly more efficient, but also, this prevents a very common bug from making a typo. If I accidentally try to set `user.nmae= JAMES` instead of getting an error on a normal class, I would just silently create the `nmae` attribute  on my instance, leaving the old name unchanged. There's no error. I just now have incorrect data in my application. Can't wait to ship it to a customer, only for them to tell me every name in a database is null. But on a slotted class, as soon as I try to set the `nmae` attribute, I get an error telling me I've done goofed again, telling me `nmae` isn't a valid attribute. While it's certainly possible that you want the ability to dynamically create new attributes on your class at runtime, it's far more common for classes to know all their attributes ahead of time. So it makes sense for slots to be the default. But like I said, attrs and dataclasses  both support slots, so what's the problem?   This brings me to my second reason: Versioning. Dataclasses are part of the Python standard library, As Python develops, more and more features like slots are added to dataclasses. But if I want to use those features, I need to upgrade my version of Python, which may involve convincing a client to upgrade and/or drop support for an older version of Python.   That might not be acceptable. And in a world where many companies are still using Python 3.8, that means no slots if I'm using dataclasses since they aren't supported until Python 3.10. In contrast, if attrs introduces  a new feature that I want to use,   all I need to do is bump the version of attrs on a per-project basis, which is super easy to do. And reasons three, four, five, and so on are all of the features that attrs supports that dataclasses just doesn't. In particular, attrs supports validators, converters, and factories that take a `self` parameter. And it allows you to set specific `on_setattr` hooks per attribute. So you could, for example, make only  one field frozen but allow the others to change. Another superficial one: attrs recognizes  the importance of immutability and gives you a top-level `@frozen` alternative to define that just sets the frozen parameter to true.   These are all little things. But when you use a tool like this every single day, these little things really start to add up, which is why I find myself using attrs more and more in 2023. Let me know what you think! Do you use attrs? What about dataclasses, or do you prefer handwriting all your classes? Thanks for watching! Thank you to my patrons and donors. Slap that like button an odd number of times. And I'll see you in the next one. Do you have a Python or C++ project at your company that needs a bit of love? Well, my company does consulting. So check out mcoding.io to see if I might be able to help.
Info
Channel: mCoding
Views: 62,359
Rating: undefined out of 5
Keywords:
Id: 1S2h11XronA
Channel Id: undefined
Length: 6min 21sec (381 seconds)
Published: Mon Jul 03 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.