top | item 29677238

Attrs – the Python library everyone needs (2016)

151 points| aqeelat | 4 years ago |glyph.twistedmatrix.com

111 comments

order

divbzero|4 years ago

The @dataclass decorator [1] as proposed in PEP 557 [2] has also been available since Python 3.7 was released in 2018.

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.html

[2]: https://www.python.org/dev/peps/pep-0557/

hamstergene|4 years ago

As soon as I opened the article I pressed Cmd+F and searched for "dataclasses".

This 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.

skrtskrt|4 years ago

IMO Pydantic is way more ergonomic, has great defaults, and easier to bend to your will when you want to use it a little differently.

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

ledauphin|4 years ago

I'd say the opposite. Specifically, Pydantic tries to do everything, and as a result (partly b/c they favor base classes over higher order functions), it isn't as composable as attrs is.

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.

kissgyorgy|4 years ago

Apples and oranges. pydantic is a serialization and input validation library, attrs is a class-generator library. Completely different features, completely different use cases.

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.

ahurmazda|4 years ago

Do you have concern with speed or memory footprint of pydantic compared to the rest (attrs, dataclasses etc)? Pydantic seems insistent on parsing/validating the types at runtime (which makes good sense for something like FastAPI).

kaashif|4 years ago

Pydantic is dataclasses, except types are validated at runtime? It's nice and looks just like a normal dataclass looking at https://pydantic-docs.helpmanual.io/

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.

joshuamorton|4 years ago

Pydantic is useful if you're dealing with parsing unstructured (or sort of weakly untrusted) data. If you just want "things that feel like structs", dataclassess or attrs are going to be just as easy and more performant (and due to using decorators and not metaclasses, more capable of playing nicely with other things).

tddispointless|4 years ago

I used attrs in a large python project, and it was more pain than it was worth. Off the top of my head, knowing the difference between factory vs default in the initializer was a bug that bit us, inheritance was painful because we were forced to redefine the attrs from the parent class in each child class (maybe they fixed this now). Validators were broken somehow. I even made a GitHub issue for this which was never addressed. Attrs are good for simple stuff. I feel plain old classes are more reliable once things get complex. And for simple things, dataclasses do just fine too.

nauticacom|4 years ago

I've never understood the appeal of these "define struct-like-object" libraries (in any language; I've never understood using the standard library's "Struct" in Ruby). My preferred solution for addressing complexity is also to decompose the codebase into small, understandable, single-purpose objects, but so few of them end up being simple value objects like Point3D. Total ordering and value equality make sense for value objects but not much else, so it really doesn't improve understandability or maintenance that much. And concerns like validation I would never want to put in a library for object construction. In web forms where there are a limited subset of rules I always want to treat the same way, sure, but objects have much more complicated relationships with their dependencies that I don't see much value in validating them with libraries.

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.

joshuamorton|4 years ago

Ignore all of the validation aspects. In python, you have tuples, (x, y, z), then you have namedtuples and then attrs/dataclasses/pydantic-style shorthand classes.

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.

atorodius|4 years ago

well one appeal is that you dont have to write constructors, that‘s already enough of a win for me. then you get sane eq, and sane str, and already you remove 90% boilerplate

NeutralForest|4 years ago

For anyone interested in differences between attrs and pydantic, how to use dataclasses, etc. I can't recommend the mCoding channel enough, this video : https://www.youtube.com/watch?v=vCLetdhswMg goes into the different libraries and there are other videos going more in depth on how to use them.

gjvc|4 years ago

see also "dataclasses" since python 3.7

falafelite|4 years ago

Came here to say this, dataclasses have been super helpful for a big part of the pain point highlighted by the author. More often than not, that is enough for me.

nsonha|4 years ago

Not a python guy, so confused as to why a thing called namedtuple behaves like dataclasses, what are their different usecases?

nmca|4 years ago

counterpoint: stdlib is where things go to die

ahurmazda|4 years ago

dataclasses also have the slots kwargs since 3.10. Should help with faster access and memory

greymalik|4 years ago

attrs is a superset of dataclasses.

bjourne|4 years ago

Costs: Depending on a relatively unknown library. Using arcane class decorators and unusual syntactic constructs: @attr.s and x = attr.ib() (a pun?).

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().

dragonwriter|4 years ago

> Depending on a relatively unknown library

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.

ledauphin|4 years ago

there are newer (since 2020) syntactic constructs that might be more to your liking. take a look at the docs again.

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.

joshuamorton|4 years ago

> However, you do need to understand a lot of Python internals to grok x = attr.ib().

No more than with namedtuples (in fact, both use essentially the same magic: code generation and `eval`).

atorodius|4 years ago

This is only remotely relevant but I recently learned that the related `dataclasses` is implemented by constructing _string representations_ of functions and calling `exec` on them.

https://github.com/python/cpython/blob/3.10/Lib/dataclasses....

Kind of blew my mind

Spivak|4 years ago

You should look at the older implementation. The whole class itself was made by interpolating a huge string and the exec-ing it but was changed with the ability to dynamically generate classes with type(). Nothing other than motivation and a good proposal is standing in the way of a def() that operates similarly.

bjourne|4 years ago

Ew. How can there not be some better way to do it?

posix86|4 years ago

log4j v2 incoming...

julienfr112|4 years ago

I'm writing python code that is not OOP at all, mostly functional (list and dict comprehension, pure function) and use only list, dict, set and combination of them. Am I alone ?

Lamad123|4 years ago

This forcefully casual style of writing wouldn't have an introduction but it wouldn't get to the point in the first couple paragraphs either.

willseth|4 years ago

Someone should tell this person about dataclasses

nhumrich|4 years ago

I mean, the blog post is older than dataclasses

4ec0755f5522|4 years ago

I started a project with dataclasses and quickly ran into their limitations (which are by design) and migrated to attrs. It's quite a bit better.

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.

harpiaharpyja|4 years ago

I think the author is overselling this library. A lot of the problems they mention can be avoided with a consistent application of discipline.

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.

entelechy0|4 years ago

Agreed. There was some drama with twisted years ago involving python 2-3 interoperability iirc. The title reads like clickbait and having to hook me on an article this way only serves to repulse me physically.

flohofwoe|4 years ago

...or one could just use Python without classes, just functions. TBH I never quite understood why Python has the class keyword, it's a much better language without.

rthomas6|4 years ago

I thought like you for several years. Then one day, I needed a custom type. You can use dicts (or lists, or namedtuples, I guess?), but it just ends up being cleaner and more idiomatic to define a class for the type, because you can define common methods for 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.

ledauphin|4 years ago

attrs really isnt about OO-style classes - it's specifically meant to provide struct-like declarative data containers, and these can help bridge the gap between the toolset that Python provides and functional (data-first) programming styles.

Starlevel001|4 years ago

This is an obvious troll post but python is a pure OO language, everything is a an object with an associated class. Functions are just instances of FunctionType.

amirkdv|4 years ago

There's good discussion in this thread about when and why to use attrs, pydantic, or stdlib dataclasses; I have my own thoughts too.

But all I can really feel is gratitude for all of us not having to do namedtuple/slot contortions anymore. Good riddance.

danbmil99|4 years ago

Why is Attrs incorrectly capitalized in the headline? Is that an automatic feature of the software?

unbanned|4 years ago

Erm. Pydantic. Or dataclasses.

EdSchouten|4 years ago

my_point3d = (1.0, 2.5, 7.2)

Voila!

Fatnino|4 years ago

This is explicitly called out in the article.

What does your code do when I try my_point3d.x ?

frenchie4111|4 years ago

Please look at Pydantic if you are interested in attrs or dataclasses. For a bunch of reasons it’s better (happy to discuss if anyone wants)