Using @dataclass the example from OP would look like:
from dataclasses import dataclass
@dataclass
class Point3D:
x: float
y: float
z: float
[1]: https://docs.python.org/3/library/dataclasses.htmlThis article was correct and addressed a very real need in Python programming—for year 2016. By now it is obsolete and today's standard library module `dataclasses` does all of that and more.
Lots of love to Attrs, which is a great library and is a component of a lot of great software. It was my go-to library for years before Pydantic matured, but I think a lot of people have rightly started to move on to Pydantic, particularly with the popularity of FastAPI
I've done some truly amazing things with attrs because of that composability. If I'd wanted the same things with Pydantic, it would have had to be a feature request.
Yes, you can do validation in attrs, but it's not meant to be used the same way as pydantic. For serialization, you need cattrs, which is a completely different package.
If you’re worried about the performance hit of extra crap happening at runtime… dear lord use another programming language.
Dataclasses is just… meh. Pydantic and Attrs just have so many great features, I would never use dataclasses unless someone had a gun to my head to only use the standard library. I don’t know of a single Python project that uses dataclasses where Pydantic or Attrs would do (I’m sure they exist, but I’ve never run across it).
Dataclasses honestly seems very reactionary by the Python devs, since Attrs was getting so popular and used everywhere that it got a little embarrassing for Python that something so obviously needed in the language just wasn’t there. Those that weren’t using Attrs runtime validators often did something similar to Attrs by abusing NamedTuple with type hints. There were tons of “why isnt Attrs in the stdlib” comments, which is an annoying type of comment to make, but it happens. So they added dataclasses, but having all the many features that Attrs has isn’t a very standard-library-like approach, so we got… dataclasses. Like “look, it’s what you wanted, right!?”. Well no not really, thanks we’ll just keep using Attrs and then Pydantic
For any larger program, pervasive type annotations and "compile" time checking with mypy is a really good idea though, which somewhat lessens the need for runtime checking.
I don’t expect any type-related thing to be remotely safe in Python without applying at least mypy and pylint, potentially pyright as well, plus, as always with an interpreted language, unit tests for typing issues that would be caught by a compiler in another language
Overall, I really don't see the appeal. It makes the already simple cases simpler (was that Point3D implementation really that bad?) and does nothing for the more complicated cases which make up the majority of object relationships.
These are useful even if only due to the "I can take the three related pieces of information I have and stick them next to each other". That is, if I have some object I'm modelling and it has more than a single attribute (a user with a name and age, or an event with a timestamp and message and optional error code), I have a nice way to model them.
Then, the important thing is that these are still classes, so you can start with
@dataclass
class User:
name: str
age: int
and have that evolve over time to @dataclass
class User:
name: str
age: int
...
permissions: PermissionSet
@property
def location():
# send off an rpc, or query the database for some complex thing.
and since it's still just a class, it'll still work. It absolutely makes modelling the more complex cases easier too. class Person:
def __init__(self, name, age):
self.name = name
self.age = age
is any worse than this: @dataclass
class Person:
name: str
age: int
I'm not writing an eq method or a repr method in most cases, so it just doesn't add much for the cost.Their differences are highlighted in the dataclasses PEP: https://www.python.org/dev/peps/pep-0557/#why-not-just-use-n...
Comparatively named tuples are an older language feature which essentially allow you to define named accessors for tuple elements. IIRC, these days you can also define type annotations for them.
Their use case essentially overlap. Personally I much prefer data classes.
Benefits: Saving at best 10-15 lines of boilerplate per data class. Much less if namedtuple works for you.
If you want to save lines in __init__ you can write "for k, v in locals().items(): setattr(self, k, v)". But you shouldn't.
Edit: Forgot to add to the most important cost: Magic. You don't need to know a lot of Python to understand how the standard self.x = x initialization works. However, you do need to understand a lot of Python internals to grok x = attr.ib().
attrs is not “relatively unknown” as Python libraries go.
> Using arcane class decorators and unusual syntactic constructs: @attr.s and x = attr.ib() (a pun?).
There have been conventional, SFW aliases for the punny ones for...a long time.
Incidentally, I'd recommend against Named Tuples for non-trivial software. Because they can be indexed by integer and unpacked like tuples, additions of new fields are backwards-incompatible with existing code.
No more than with namedtuples (in fact, both use essentially the same magic: code generation and `eval`).
https://github.com/python/cpython/blob/3.10/Lib/dataclasses....
Kind of blew my mind
Attrs – The python library everyone needs (2016) - https://news.ycombinator.com/item?id=17160262 - May 2018 (2 comments)
Using attrs for everything in Python - https://news.ycombinator.com/item?id=12359522 - Aug 2016 (101 comments)
The One Python Library Everyone Needs - https://news.ycombinator.com/item?id=12285342 - Aug 2016 (1 comment)
[0] https://www.python.org/dev/peps/pep-0557/#why-not-just-use-a...
If you take 10 seconds to read attrs website they do go over the differences and maybe discussing those would be more valuable than some cheap snark.
You can decompose classes that become too big for their own good. You can design your software, layer abstractions intelligently etc. so that having to do such refactoring isn't a big issue.
Python is a language that demands an above average level of discipline compared to many other programming languages I have used, but only because it IMO leans strongly towards empowering the developer instead of restricting them.
The article mentions quaternions. If you make a quaternion type (class), you can define addition, multiplication, comparison, etc. for it (methods). If you represent a quaternion any other way, you can't say a * b. Or maybe you can, but I don't know how.
But all I can really feel is gratitude for all of us not having to do namedtuple/slot contortions anymore. Good riddance.
Voila!