A reverse chronology of some Python features
I occasionally hear people lament that Python is "bloated", "too big", "going enterprise", or some other phrasing to suggest there was once an "ideal" version of Python that had less bloat and was closer to what the person wanted.
But then this lament is also often followed by, "but I would want this one feature from a newer version of Python". 🤨 What I think a lot of people don't realize is that various features are interconnected in ways that may not be obvious. And this can go beyond the typical cause-and-effect scenario where you can't cleanly separate inspiration, etc. from each other.
Take, for instance, pattern matching. Plenty of people complained that it wasn't going to be necessary and they could live without it. But the adding of pattern matching necessitated writing a new parser for Python. That new parser allowed for improved error messages (something I have yet to hear anyone say was a bad thing to introduce and for a lot of people their favourite feature in Python 3.10). So if you don't want the match
statement, are you willing to give up the better error messages as a trade?
To help those who wish for the "good old days" of some older Python version, I thought I would write down a bunch of the major language features that were added in Python in reverse chronological order. Start from top and work your way down until you come across a feature you aren't willing to give up. The Python version which has that feature is the version you would be okay going back to.
By the way, you also have to give up all subsequent performance improvements when you go back as new features lead to code clean up, new insights on how to make something better, etc. And I can tell you right now, if you like performance you are probably going to be making Python 3.11 your favourite version of Python. 😁
- Python 3.10 (2021-10-04)
- 3.9 (2020-10-05)
- 3.8 (2019-10-14)
- 3.7 (2018-06-27)
- 3.6 (2016-12-23)
- f-strings (
f"{yes}!"
) - Variable annotations (
var : int
) - Underscores in numeric literals (
1_000_000
) - Asynchronous generators (
await
inasync def
) - Asynchronous comprehensions (
[i async for i in aiter() if i % 2]
) __init_subclass__()
__set_name__()
for descriptors__fspath__()
- Preserving class attribute definition order
- Preserving keyword argument order
- f-strings (
- 3.5 (2015-09-13)
- 3.4 (2013-08-03)
- 3.3 (2012-09-29)
- 3.2 (2011-02-20)
- 3.1 (2009-06-27)
I'm going to stop at Python 3.1 because Python 3.0 is intertwined with Python 2.6 and the list is way too long to want to write out. And I'm not going past Python 3.0 because it's time to move on already. 😁