Why I prefer attrs over dataclasses
Ғылым және технология
Better Python dataclasses!
Attrs is the original dataclass library. Literally, the standard library "dataclasses" module was based off of attrs. They are very similar in their main functionality, but I personally find attrs to be more and more preferred.
― mCoding with James Murphy (mcoding.io)
Source code: github.com/mCodingLLC/VideosS...
Dataclasses: docs.python.org/3/library/dat...
Attrs: www.attrs.org/en/stable/
SUPPORT ME ⭐
---------------------------------------------------
Sign up on Patreon to get your donor role and early access to videos!
/ mcoding
Feeling generous but don't have a Patreon? Donate via PayPal! (No sign up needed.)
www.paypal.com/donate/?hosted...
Want to donate crypto? Check out the rest of my supported donations on my website!
mcoding.io/donate
Top patrons and donors: Jameson, Laura M, Dragos C, Vahnekie, Neel R, Matt R, Johan A, Casey G, Mark M, Mutual Information, Pi
BE ACTIVE IN MY COMMUNITY 😄
---------------------------------------------------
Discord: / discord
Github: github.com/mCodingLLC/
Reddit: / mcoding
Facebook: / james.mcoding
CHAPTERS
---------------------------------------------------
0:00 Intro
0:20 Dataclass-like tools
2:38 Why attrs over dataclasses?
3:06 Better defaults
4:19 Independent versioning
5:08 Features
Пікірлер: 159
I came here to tell you that you're wrong but you made a really good point when facing customers locked on a specific version of Python. Great video!
@arisweedler4703
11 ай бұрын
Just curious, before being convinced what would your argument have been? I can think of one argument: The fact that it is built in to python (and I understand why this is no longer an argument after the video!)
@fus3n
11 ай бұрын
if anyone tells me to use old shit I am quitting the job I always stick to new stuff, it's like OCD I can't use old stuff unless new one is likely unstable
@dhkatz_
11 ай бұрын
@@fus3n Then you will never get a job anywhere
@t0prar
11 ай бұрын
@@arisweedler4703 yes. It mostly comes down to that. So there's no technical reason. On personal projects on smaller teams I don't see any issue with using any alternative. Hiring someone and expecting them to know about how the intricacies of other libraries work that do more or less the same as preexisting ones increases the amount of onboarding time we need to give to each team member. Hiring a Python developer that doesn't know about slots and dataclasses means they might just not be on that level yet. That in addition we have to manage another dependency versioin to sync across teams and that triggers rebuilding your platform for each release. Because of that it doesn't seem to be worth it from my pov.
@fus3n
11 ай бұрын
@@dhkatz_ then I will go for my own startup where people aren't idiots and use proper tools (meant other companies BTW)
Please! Start making videos on rust. Rust can be really confusing sometimes, but you manage to explain every topic you tackle nice and simple
Nice. I have been using dataclasses since their release and we're working on an internal project (Py3.11 required). I have never actually used attrs myself, but have seen others use it, and you make some good arguments for preferring it.
But as always great, fast concise coverage. You do make wonderful Python videos that are very practical and real and do not focus on the same old stuff covered everywhere else. I hope you produce a book.
My problem with both dataclasses and attrs is that they're not type safe. Similar to your "nmae" typo, if you declare slmething as an int, but put a string there, now you get no errors but incorrect data in your application. Pydantic, on the other hand, solves this perfectly. Pydantic 2.0 was recently released and increases performance significantly. I really like how Pydantic is tightly integrated in frameworks that benefit greatly from type safety, like Litestar.
@Madinko12
11 ай бұрын
Pydantic = cancer
@matthewgamble9934
11 ай бұрын
By default, Pydantic doesn't validate on assignment. It only on construction.
@yash1152
11 ай бұрын
use pylance error reports
@helboy1111
11 ай бұрын
Pedantic will try to coerce it into an int and then throw an error if it’s not possible. One could make the argument this is a nice feature or if one’s less familiar with Pydantic a unexpected bug. Further more this case can be handled in all the above libraries by using validation.
@Mekuso8
11 ай бұрын
@@helboy1111 Having type safety by default is a massive advantage, just like attrs doing slots by default is an advantage. Having to define validators for everything is error prone.
thanks for this video! I've actually never used attrs, always dataclasses... But after your video, I will take a look! I'm especially curious about validators and the fact that certain attributes can be frozen, but not all of them. Also slots as default is great
amazing! The bumping of the package rather than the python version is really the biggest factor for me
The locked on specific python version is a good point, but for personal projects or packages I hope they stick to dataclasses. Just in fear of every repo having a different standard.
Hi, thanks for the video! Which is the fastest in terms of creating instances?
Had no idea attr existed, but your arguments seems valid so I might use this in the future. As often, good video :)
honestly i cant believe it took me THIS long to find theres a dataclass library that lets you convert values on the constructor, i really needed that...
Thanks for the video! What do you think attrs vs pydantic?
I just checked and I've only got Python 3.9.4, so using a newer feature would require updating, something I don't generally like doing, especially for scripting languages because of users. Personally, this is one of the key reasons to prefer a compiled language, because you don't have to worry about what version the user has, just provide all the library dependencies or static build and you'll be fine. And I know, you can compile Python into a binary format, but it's not as optimized as it should be, it's some of the laziest translation I've seen for all of the different methods of conversion. I know, not the point of Python as optimization is nearly never a concern, but honestly, I only learned Python so I could translate it to C, and I mean C, not C++.
These are really great tips, thank you.
personally whenever i need to inherit dataclasses or start using some default values, I often hit the ceiling of what's possible with the built in module. the fact that you can't define attributes without defaults after ones with defaults (even if they come from a superclass) is often the deal breaker.
@PetrSzturc
11 ай бұрын
Exactly me. Start with dataclass and then leave it on first hurdle. No one wants to read through fields, defaults etc when they are familiar only with __init__.
@Kaniee96
11 ай бұрын
I had a similar experience with dataclasses a couple of years ago. But I'm giving it in my latest project another try because I discovered two things: 1. When setting kwargs_only=True you can create child classes with more attributes without defaults. 2. I'm relying more on duck typing now since I discovered Protocols, so I don't lose the powers of typing checking with pyright. What still annoys me is the fact that I can't initialize the fields (where init=True) on an immutable type in the __post_init__ function.
@atrus3823
11 ай бұрын
If you could, what you the constructor look like? It's the same reason you can't define a function def my_func(a = 1, b). I have run into how annoying this is when inheriting, but it's the same problem. The new constructor will have the attributes in order from the superclass as arguments, and then the new attributes from the subclass. I don't know enough about attrs to know how it handles this differently, but I'm not sure how you could get around this without mangling the order, which I could see leading to a whole host of problems.
@sadhlife
11 ай бұрын
@@atrus3823 attrs does change the order, what problems do you think occur from that? when I use attrs it's almost always a case where I will have to pass everything as a keyword argument anyway to avoid confusion. If there were only 2-3 arguments I'd have used NamedTuple or dataclasses
@atrus3823
11 ай бұрын
@@sadhlife If it enforces keyword-only, then I don't see a problem. However, if you set kw_only on dataclasses, it also doesn't have a problem with defining fields with a default before fields without a default. If you didn't ensure keyword-only, then imagine a class A: a: int = 1; b: int. It would have to write the constructor def __init__(b, a = 1): ... Now, you've instantiated this class all over your code (e.g., A(3, 4), i.e., b = 3, a = 4), and someone decides b needs a default value. Now it reverses the constructor args, and every instantiation A(3, 4) now means a = 3 and b = 4.
Handwriting or code generation is strong because it guarantees someone can always read the source and not need to know about the dependencies or imports as much. decorators are effectively macros but still come off magical.
For simpler things I often use NamedTuple instead of dataclass, it's kind of a deal breaker that they don't work with generics though
I currently take a deep dive into dataclasses as a python beginner 🤔just begun to write code. And with a outside view from dataclasses vs attr, I will choose on a project basis which one to choose. - When the project is to complicated and restricted in terms of versioning I would use attrs - When it would be implemented in python and I can effortlessly upgrade, then I use dataclasses. I have anyway CI/CD pipelines in place with multiple versions testing for each commit, so if anything breaks on newer versions I can take a look at it
super useful! Subscribed
Loved the instant replay!
3:50 I encountered this pitfall when defining read/write properties on wrapper classes for the Cairo graphics library. Nowadays I reflexively put ‗‗slots‗‗ on my wrapper classes, just in case.
looks like it needs to compile each time during startup? no caching because of eval ?
I started using attrs recently to automate de-serialization and validation, sometimes with custom validators and converters. I don't like the exceptions generated by bad data though, the messages are not great and a bit cryptic for the client user
@geeshta
11 ай бұрын
What about Pydantic?
I don't use neither, instead I use Pydantic. I can't tell about slots exactly, may be it is using slots, may be not, but it does everything else that you have and it forcing to use validator at least on typing level and may be more if you need more validations, such as number in range, e.g. "ge=0" (greater equal) or lists with "min_items=1" or strings with "regex=r'whatever' ", they have both defaults and defaults factory, can set optional or required parameters (... - Ellipsis is used if set "defaults=..."), and forget to mention they have same Field() attribute, like "x: list[int] = Field(default=..., title="Can give a name here", description="Can describe here", min_items=1)". Title and description can be used for both IDE showing what this parameter do or autodocs like Swagger. Pydantic used by default in FastAPI, but I also use it outside of FastAPI projects too. And 2 last comments I want to tell: 1) For transmitting model back into front or over the internet it have convenient methods like ".dict()" and more to that it have useful parameters like exclude unset parameters, exclude defaults, exclude none or just exclude from a list of strings. This method return JSON in the end and it does not have issues like as if you do with "json.dumps(my_dict)" when dict has unusual types like datetime, etc. 2) There absolutely no issue with amounts of fields like positional, e.g. "x: int" and keywords like "y: int = Field(...)" and you can have like first keyword, 2nd positional, 3rd keyword, etc. and you wont get error about this. So it is allow for sorting fields in a way you need and no need to use hacks to make it work. It was a really big issue for me before I used Pydantic.
4:42 My clients mainly use Debian Stable on their servers. So I go by whatever Python version is included as standard, e.g. 3.9 for Debian 11, 3.11 for Debian 12.
I didn't actually use those, I tried to avoid classes because of how much boilerplate there is and how slow the software designs tend to be with them. This makes it significantly more ergonomic. Thanks
I remember when I first started getting serious about Python, and everyone was saying "In Python, there's always one right way to do something and it's usually pretty obvious." You see it repeated all the time in this community but I don't really think it's true. Little rivalries like dataclasses vs attrs exist and then there's the package management problem. Anyway I stopped using python for personal projects and I've been happier I think.
@k98killer
11 ай бұрын
What is your replacement language?
@Graham_Wideman
11 ай бұрын
Yeah, there are seeming increasing number of ways in which there was no right way to do something to now three possible ways to optionally do something. Resulting in a messy landscape where a codebase might or might use some of these features, and that usage might vary from one class or module to another.
@danielgysi5729
11 ай бұрын
@@k98killer Rust mostly. Admittedly the learning curve is much steeper and Python is definitely better as a first language, but the tooling in rust is a breath of fresh air and personally I like that people who write rust care more about performance, since I enjoy optimizing. This is from the perspective of hobby projects though. At work I write Java and PHP.
@lawrencedoliveiro9104
11 ай бұрын
Personally, if I’m creating a one-off object, often a dict is just fine. For example, I did a Python wrapper for the Asterisk telephony engine. I looked at other wrappers, and their event-listener calls return a separate event subclass for every event type, with attributes specific to that event type. Me, I didn’t bother, I just return a dict with the appropriate fields in each case. Am I lazy? Yes, I’m lazy. I put my effort into things like the option for SSL/TLS connections, and supporting both sync and async versions of calls, which as far as I can tell, nobody else does.
You're great man! If performance isn't an issue, would you still go with attrs over pydantic?
@mCoding
10 ай бұрын
Since pydantic's primary goal is parsing, it has a lot of defaults that are specific to that goal, like eager conversions. Attrs aims to be a more general class building utility library. Although if you look at my dataclass alternatives video, you can see that pydantic performance is not just worse, it's much worse, like 10x worse because of all the validation. In a small script that may not matter, but if you have a whole application, all those 10x perf hits can add up to be an issue even if you don't care much about performance.
Is usually hand wrote but that frozen thing feels really good
The Python versioning is problem that is an issue. On systems with Ubuntu 20.04, it has Python 3.8 which means that scripts will not run with dataclasses with slots=True.
What do you think about pydantic?
I also have been using attrs before switching to dataclasses when they got introduced, but i had no idea attrs had all these neat features, i'll probably switch back
when I first discovered dataclasses three years ago my mind was blown, but I had to implement a few validators took me some time and it worked, but only now you're telling me there's already a tool for it? damn
I've always used dataclasses or Pydantic depending if I need parsing and validation or not. I never knew attrs and slots by default is neat! For validation I always used Pydantic not sure if there's a difference.
@doronvaida
11 ай бұрын
I prefer pydantic because it has all kinds of ultra specific types that can save a lot of validation, so much in fact that i don't use pydantic validator for validation but for mutation when needed
Hi! What about "Msgspec"?
I'd really love to see your take on pydantic dataclasses as an alternative to each!
@loic1665
11 ай бұрын
I think mCoding already a video where he compared tuples, namedtuples, dataclasses etc... and pydantic!
@mCoding
11 ай бұрын
Yep! kzread.info/dash/bejne/qHeAx9admNbdfco.html
@alexandreboisselet8336
10 ай бұрын
What about the old param library?
How about attrs vs pydantic?
i use strict mode pylance so typo is never a problem. i still think dataclass is better designed. it does not have those over powerful tools that might change what a python code should looks like and behaves, which is good.
How is this different than using a TypedDict implementation in a class?
No mention of NamedTuple? 😊
Interesting I only learned about dataclasses through your old video about them, do you still stand by that video?
I wish python had macros or anything to make source generation like this easier... I'm pretty fed up with the pseudo-oop and overuse of reflection that writing a python metaprogramming library often requires.
msgspec is my new favorite library for this usecase
I agree with your presentation, but we chose between dataclasses and pydantic. I would like to hear what you think about dataclass/attrs vs pydantic.
@mCoding
3 ай бұрын
Then check out my video where I compare dataclasses, attrs, and pydantic!
Personally I am moving to pydantics BaseModel whenever possible. Is there a reason to prefer attrs over pydantic?
@mCoding
11 ай бұрын
You may like to see my comparison video between attrs and other dataclass solutions, including pydantic. Pydantic is not recommended for general purpose classes, but it does a great job if you goal is parsing and validating input. kzread.info/dash/bejne/qHeAx9admNbdfco.html
@jeroenvermunt3372
11 ай бұрын
@@mCoding Thanks! I checked it and it makes a lot of sense
I use attrs too !
My number one reason why I choose attrs over dataclasses is inheritance from a baseclass with kw arg fields and adding a positional field in the subclass. Just doesn't work in dataclasses. (Atleast py38, but I'm one of the poor guys stuck with it for the foreseeable future, thanks nvidia)
Im looking forward to attrs tutorial
Should I use pydantic over attrs since the 2.0 update and it's new high performance?
@mCoding
4 ай бұрын
I'd say that Pydantic and attrs are for different purposes, I made a whole video about it here kzread.info/dash/bejne/qHeAx9admNbdfco.html In short, if you are parsing data that you do not trust, e.g. from a web request, then Pydantic is probably what you want. But if your goal is not parsing and you do trust the data, e.g. because it is generated internally by your application, then Pydantic (even V2) will be orders of magnitude slower than attrs because attrs does not perform any data validation.
@Georgehwp
4 ай бұрын
@@mCoding thanks a lot for getting back to me. Really appreciate it.
Never used attrs myself. Using dataclasses all the time, left and right. Python 3.9, using slots auccessfully
What about Pydantic?
1:42 it was but I guess I normally do that
what about pydantic?
IDEs usually help with that part but I get it.
Personally, I write the boilerplate myself. I understand the benefits of dataclasses/attr, but I would rather be sure of my own code. I do agree with many of your points though. Both dataclasses and attr are great tools to reduce boilerplate and write less flaky code. But in my personal experience, I find that I would rather have full control over what I define in any given class. 😅
@orterves
11 ай бұрын
I wonder if there could be an ide shortcut to apply the generated code to the source and remove the annotation, for an annotated class? You get to see the code and could modify the class later if necessary, but don't need to do the tedious bit yourself.
Okay with dataclasses. But why not Pydantic?
The actual thing that makes this code simple, is the abstraction - hiding details in a more high-level solution. The particular solution, like attr or dataclass is not that important.
Do you always want all this functionality? Like if you use it for your class Employee and later find out that Alice < Bob makes no sense but does not raise any error.
@mCoding
7 ай бұрын
No, you wouldn't normally set all the options to True. You would only set the order/compare parameter to True if comparing those elements makes sense. If it doesn't, then set it to False (the default).
@Jeyekomon
7 ай бұрын
@@mCoding Ah, I didn't notice that ordering is disabled by default. Sorry, my bad. Thank you for the explanation!
Just before you said to watch again this beautiful refactor I stopped your video and rewinded myself. ROTFL when you did exactly the same soon after yourself.
I use dataclasses, slots don't seem necessary. If I misspell an attribute it's get caught by mypy or pylint. If I need perfomance I don't use python :)
the whole point of attrs writing the code for you I feel like is a negative, since it does this at runtime which seems like it might have a performance cost. I would rather write more verbose code than have all of it done at runtime
@mCoding
2 ай бұрын
Keep in mind that attrs (and other dataclass-like alternatives) use a decorator on your class object, meaning they execute once *per class definition*, not once per instance. This means that any performance penalty is small and you will incur it at the very beginning of your program when you import your modules. In the wild though, attrs instances tends to be *faster* and *more efficient* at runtime because attrs will happily do all the little optimizations that a human programmer wouldn't bother with because it takes so much typing and is easy to mess up, like defining slots by default and implementing readonly properties by writing a custom setattr that only runs when an attribute is written to rather than using the much slower @property decorator that runs also when a field is read from.
I just switched to Typescript
attrs does not support protected attributes, and it is really annoying. From the docs: “attrs follows the doctrine that there is no such thing as a private argument”. This is a very big no for me. But dataclasses do lack many features that attrs have, so it is not a perfect option either. After spending some time with fastapi I’m thinking about using pydantic instead of attrs and dataclasses for some of my projects. On the paper, it has all the features that I need, so I’m hoping for the best!
0:36 Hey James, "who can even remember all of them?" - a class with 3 atrributes would not be memorable? ;)
@mentisdominus
11 ай бұрын
🙂 James was talking about remembering all the boilerplate functions that would need to be implemented.
@krzysztofjarek6476
11 ай бұрын
@@mentisdominus It was just a joke. I wanted him to respond to this provocation ;)
I use dataclasses but i'll give attrs a shot tonight
I have never in my 4 years of using Python ever needed to use dataclasses or attrs. In my projects, there were simply no need for a data structure with complex boilerplate
why not use pydantic ?
@mCoding
11 ай бұрын
I made a whole video to answer this question :) kzread.info/dash/bejne/qHeAx9admNbdfco.html
@rx97_mc
11 ай бұрын
@@mCoding might be slightly outdated since pydantic v2 is a pretty large rework of the library, esp considering performance
Hei fra Norge!
well well well, i have a new toy to play with now, dont I 👀
pydantic is also good
discord gang 🤙🤙
@amir3515
11 ай бұрын
is slow
Pydantic
the update to `@define` and modern attrs is really attractive and I do agree with many of your points. one thing i've seen though is that `mypyc` (a compiler that is bundled with `mypy`) isn't able to compile attrs classes to a more optimized struct-like thing as it can w/ `dataclasses`.
Pydantic all day ftw....
Looks like someone ported Lombok over to python heh
I use vim btw
@Graham_Wideman
11 ай бұрын
Why are you telling us?
😳 promo sm
What I think: I stopped trying to do OOP in python and use Rust’s far superior type system instead (generics, traits, enums, etc). Believe it or not, it’s actually much simpler to understand than all these python decorators in classes.
@shadowviper95
11 ай бұрын
Out of curiosity, what do you use as your “struct” equivalent if not data classes? I often do the same with rust style syntax, but reach for data classes to maybe a named triple if it’s immutable for my “struct and or impl group”
@PetrSzturc
11 ай бұрын
Could you provide small example py class vs rust? I am interested in Rust but haven't fully tried it yet.
@lawrencedoliveiro9104
11 ай бұрын
Python has metaclasses, though.
pydantic is my to go.
barf
pydantic...
If you don't define your classes by manually writing cpython bytecode and assembling it at runtime with marshal or xasm you aren't a real python programmer...
I don't use attrs or dataclasses. I've never really found either of them particularly useful and really hate when stuff is working under the hood. It just adds too much trivia to a project. Also, I absolutely loathe pythons syntaxes for defining a variable type. It adds so much clutter and doesn't do anything.
@mCoding
11 ай бұрын
Fun fact! Although I showed lots of type annotations in this video, attrs is fully compatible with using zero type annotations.
Seems like rust suits you more than python 😅
“post the wrong code to stack overflow and wait for someone else to write it for you” 😂😂😂
Disagreed. +1 3rd-party dependency == +1 moving part.
"In the world where many companies still use python 3.8" TFW your company still uses python2.7 😢
It's mad to me that you pronounce "attrs" as "adders"