| # On The Core API Names |
| |
| You may be surprised seeing *attrs* classes being created using {func}`attrs.define` and with type annotated fields, instead of {func}`attr.s` and {func}`attr.ib()`. |
| |
| Or, you wonder why the web and talks are full of this weird `attr.s` and `attr.ib` -- including people having strong opinions about it and using `attr.attrs` and `attr.attrib` instead. |
| |
| And what even is `attr.dataclass` that's not documented but commonly used!? |
| |
| |
| ## TL;DR |
| |
| We recommend our modern APIs for new code: |
| |
| % {func} syntax does currently not work for `.. function` definitions. |
| |
| - {func}`attrs.define` to define a new class, |
| - [`attrs.mutable()`](attrs.mutable) is an alias for {func}`attrs.define`, |
| - [`attrs.frozen()`](attrs.frozen) is an alias for `define(frozen=True)` |
| - and {func}`attrs.field()` to define an attribute. |
| |
| They have been added in *attrs* 20.1.0, they are expressive, and they have modern defaults like slots and type annotation awareness switched on by default. |
| Sometimes, they're referred to as *next-generation* or *NG* APIs. |
| As of *attrs* 21.3.0 you can also import them from the `attrs` package namespace. |
| |
| The traditional, or *OG*, APIs {func}`attr.s` / {func}`attr.ib`, their serious-business aliases `attr.attrs` / `attr.attrib`, and the never-documented, but popular `attr.dataclass` easter egg will stay **forever**. |
| |
| *attrs* will **never** force you to use type annotations. |
| |
| |
| ## A Short History Lesson |
| |
| At this point, *attrs* is an old project. |
| It had its first release in April 2015 -- back when most Python code was on Python 2.7 and Python 3.4 was the first Python 3 release that showed promise. |
| *attrs* was always Python 3-first, but [type annotations](https://peps.python.org/pep-0484/) came only into Python 3.5 that was released in September 2015 and were largely ignored until years later. |
| |
| At this time, if you didn't want to implement all the {term}`dunder methods`, the most common way to create a class with some attributes on it was to subclass a {obj}`collections.namedtuple`, or one of the many hacks that allowed you to access dictionary keys using attribute lookup. |
| |
| But *attrs* history goes even a bit further back, to the now-forgotten [*characteristic*](https://github.com/hynek/characteristic) that came out in May 2014 and already used a class decorator, but was overall too unergonomic. |
| |
| In the wake of all of that, [Glyph](https://github.com/glyph) and [Hynek](https://github.com/hynek) came together on IRC and brainstormed how to take the good ideas of *characteristic*, but make them easier to use and read. |
| At this point the plan was not to make *attrs* what it is now -- a flexible class-building kit. |
| All we wanted was an ergonomic little library to succinctly define classes with attributes. |
| |
| Under the impression of the unwieldy `characteristic` name, we went to the other side and decided to make the package name part of the API, and keep the API functions very short. |
| This led to the infamous {func}`attr.s` and {func}`attr.ib` which some found confusing and pronounced it as "attr dot s" or used a singular `@s` as the decorator. |
| But it was really just a way to say `attrs` and `attrib`[^attr]. |
| |
| [^attr]: We considered calling the PyPI package just `attr` too, but the name was already taken by an *ostensibly* inactive [package on PyPI](https://pypi.org/project/attr/#history). |
| |
| Some people hated this cutey API from day one, which is why we added aliases for them that we called *serious business*: `@attr.attrs` and `attr.attrib()`. |
| Fans of them usually imported the names and didn't use the package name in the first place. |
| Unfortunately, the `attr` package name started creaking the moment we added `attr.Factory`, since it couldn’t be morphed into something meaningful in any way. |
| A problem that grew worse over time, as more APIs and even modules were added. |
| |
| But overall, *attrs* in this shape was a **huge** success -- especially after Glyph's blog post [*The One Python Library Everyone Needs*](https://glyph.twistedmatrix.com/2016/08/attrs.html) in August 2016 and [*pytest*](https://docs.pytest.org/) adopting it. |
| |
| Being able to just write: |
| |
| ``` |
| @attr.s |
| class Point: |
| x = attr.ib() |
| y = attr.ib() |
| ``` |
| |
| was a big step for those who wanted to write small, focused classes. |
| |
| ### Dataclasses Enter The Arena |
| |
| A big change happened in May 2017 when Hynek sat down with [Guido van Rossum](https://en.wikipedia.org/wiki/Guido_van_Rossum) and [Eric V. Smith](https://github.com/ericvsmith) at PyCon US 2017. |
| |
| Type annotations for class attributes have [just landed](https://peps.python.org/pep-0526/) in Python 3.6 and Guido felt like it would be a good mechanic to introduce something similar to *attrs* to the Python standard library. |
| The result, of course, was {pep}`557`[^stdlib] which eventually became the `dataclasses` module in Python 3.7. |
| |
| [^stdlib]: The highly readable PEP also explains why *attrs* wasn't just added to the standard library. |
| Don't believe the myths and rumors. |
| |
| *attrs* at this point was lucky to have several people on board who were also very excited about type annotations and helped implement it; including a [Mypy plugin](https://medium.com/@Pilot-EPD-Blog/mypy-and-attrs-e1b0225e9ac6). |
| And so it happened that *attrs* [shipped](https://www.attrs.org/en/17.3.0.post2/changelog.html) the new method of defining classes more than half a year before Python 3.7 -- and thus `dataclasses` -- were released. |
| |
| --- |
| |
| Due to backwards-compatibility concerns, this feature is off by default in the {func}`attr.s` decorator and has to be activated using `@attr.s(auto_attribs=True)`, though. |
| As a little easter egg and to save ourselves some typing, we've also [added](https://github.com/python-attrs/attrs/commit/88aa1c897dfe2ee4aa987e4a56f2ba1344a17238#diff-4fc63db1f2fcb7c6e464ee9a77c3c74e90dd191d1c9ffc3bdd1234d3a6663dc0R48) an alias called `attr.dataclass` that just set `auto_attribs=True`. |
| It was never documented, but people found it and used it and loved it. |
| |
| Over the next months and years it became clear that type annotations have become the popular way to define classes and their attributes. |
| However, it has also become clear that some people viscerally hate type annotations. |
| We're determined to serve both. |
| |
| |
| ### *attrs* TNG |
| |
| Over its existence, *attrs* never stood still. |
| But since we also greatly care about backwards-compatibility and not breaking our users' code, many features and niceties have to be manually activated. |
| |
| That is not only annoying, it also leads to the problem that many of *attrs*'s users don't even know what it can do for them. |
| We've spent years alone explaining that defining attributes using type annotations is in no way unique to {mod}`dataclasses`. |
| |
| Finally we've decided to take the [Go route](https://go.dev/blog/module-compatibility): |
| Instead of fiddling with the old APIs -- whose names felt anachronistic anyway -- we'd define new ones, with better defaults. |
| So in July 2018, we [looked for better names](https://github.com/python-attrs/attrs/issues/408) and came up with {func}`attr.define`, {func}`attr.field`, and friends. |
| Then in January 2019, we [started looking for inconvenient defaults](https://github.com/python-attrs/attrs/issues/487) that we now could fix without any repercussions. |
| |
| These APIs proved to be very popular, so we've finally changed the documentation to them in November of 2021. |
| |
| All of this took way too long, of course. |
| One reason is the COVID-19 pandemic, but also our fear to fumble this historic chance to fix our APIs. |
| |
| Finally, in December 2021, we've added the *attrs* package namespace. |
| |
| We hope you like the result: |
| |
| ``` |
| from attrs import define |
| |
| @define |
| class Point: |
| x: int |
| y: int |
| ``` |