I've watched new and inexperienced programmers trying to learn full, modern Python up close and personal. (That is, they were trying to get into real code bases that were using the many features in Python, not that we were in a teaching space incrementally introducing things.) Anyone who has done that in the last ten years knows it's not simple anymore.
New in 3.3:
- yield from: I'm okay with this
- PEP 420 / namespace packages: I think this is/was the best compromise, but this remains a fundamentally ugly topic for something that is sort-of necessary to have
- raise from None: This is okay for library code
- __qualname__: It's a mess. It's not like I'm against having it, but this is a corner of the language that practically invites you to hang yourself one way or another.
- importlib: It's better than the previous iteration but this package was and still is a mess. Again, lots of this is due to legacy aka "the other packaging packages".
New in 3.4
- asyncio beta (no coroutine syntax yet): While coroutines are highly useful, asyncio and Python coroutines simply are not good. This is the beginning of the badness.
- pathlib: Ok!
- enum: Ok!
New in 3.5
- async def, await: This is the release that really set colored functions in stone for Python.
- "@" operator: I've never used this once I think, but that's because I don't write math code in Python. If I did, I think I'd like it, so I'm okay with this.
- type hints: Another oof moment
New in 3.6
- f-strings: Yay!
- dict now ordered: Yay!
- async generators and such: Again, I think colored functions are a big blunder, but if you have them, you need this.
- Variable annotations
New in 3.7
- PEP 563: Type annotations can be turned into quoted annotations without a syntactic change through __future__ import.
- contextvars: "thread-locals for coroutines"
- PEP 562: Added the Python 2 misfeature back where Python 2 interpreted __get__ and __set__ on modules
- Typing becomes part of the core language, I think this is when you could start writing things like "list[int]" (instead of "typing.List[int]").
New in 3.8
- := Yuck
- "Positional-only parameters" because the simplified CPython API for defining functions has this, so ... Python code needs that as well? Wot?
- More typing
New in 3.9
- More typing
New in 3.10
- And even more typing
- Pattern matching
I pulled this out of your post because it's something I think about a lot of the individual changes. Individually, they generally make sense.
It's just that the sum total of all the individual sensible things is a large mess, getting larger every release.
I first used Python in the 20th century, and have clocked many thousands of hours with it overall. Now I mostly program in Go. (Actually, even after ~7 years part-time professional with Go, I'm not sure which I've used more yet! I may still technically have more time with Python. If I do, though, I'm close to the crossover.) Occasionally, I miss iterators, and with Go getting generics it's probably my #1 ask in Go. Other than that, though... I just can't help but notice that I don't actually miss that many features of Python very often. I miss Haskell features, I sometimes miss Rust features, every once in a while I'll miss something more exotic like an Erlang feature, but I almost never think "gee, I really miss this Python feature". It isn't that I can't see where I'd use them if they exist, I just don't sit there pining for them all the time. I consider this a big data point. I should also point out I'm talking about subconscious inclinations, it's not like I'm setting out and daily disciplining myself to not miss Python features. It's just a thing that happens or doesn't. YMMV. I'm probably more often grateful for a missing Python feature than I'm missing it (e.g., it's actually kinda nice after a certain scale to know what "x.y" does, rather than have to go resolving down an arbitrarily-complicated chain of classes, inheritance, method overrides, etc.).
Yet, if you use it more than few years, you see how it's just a disguise.
Underneath superficial simplicity there's ton of leaking abstractions, semantics which designers designed themself into the corner, and gigantic C API that blocks any performance improvements.
def foo():
return "hi"
foo = my_decorator(foo)Similarly for context managers and many other features.
We can go back much farther than that even: why have lists and tuples, when they're basically interchangeable in most cases? Why have list comprehensions when a loop does the job just fine and might arguably be clearer?
Only for personal projects, with no outside contributors, no external dependencies, etc.
If you want to read someone else's code, e.g. to learn more, or see how one of your dependencies works, etc. then you may need to face these features.
If you want to allow external contributions to your project, then you'll have to face these features (even if that just means an informal coding standard that says "don't use XYZ").
If you want to work more closely/collaboratively with others, e.g. in an organisation, then you'll have to face these features (even if it's just a discussion about whether to allow them).
If you want to write tooling for the language, e.g. parsers, doc generators, linters, formatters, diffing tool, etc. then you'll need to face them.
Finally, if you're writing library code that's intended for use by others, you need to be aware of such language features, in case they break any of your assumptions. For example, a library might rely on the fact that lambdas can't re-bind variables (they can mutate the content of an object, but they can't change the reference, since '=' is a statement rather than an expression); the introduction of ':=' expressions violates that assumption.
It is really not my experience that people read PEPs and try to shoehorn in language tricks they don't need into their random scripts at $DAYJOB.
I would actually be thrilled to see a walrus operator show up in a PR from one of my coworkers. But I'd have to shoot it down because we need to be compatible with 3.6.
It used to be that Python was mostly similar to pseudocode, easy to understand and reason about. Now all this "syntactic sugar" just makes it harder to read IMO.
- context manager
- decorators
- inline conditions
- else keywords in loops, which nobody knows even exist.
- raise from (empty, or with params).
- yield from (espacially with send())
- comprehension lists
- advanced unpacking. This is valid: (a, (b, *c)) = [1, {*range(4)}]
- advanced slicing. This is valid: foo[2:8:2] = bar[:4]
Most of which existed in Python 2 already !
But it has never been a problem before. Why ?
Because the community doesn't abuse features, it's part of the creed. But also because half of the python coders don't even know they exist (they are just trying to solve their problem, but dev is not their job), and are productive without it, so they just don't use it.
In fact, a lot of tutorials on the internet never even mention those.
Since the walrus came out, I used it once, and never saw it on a client code base.
But when the community do use the features, they usually make things better: decorators made flask/click what it is, slicing made numpy/pandas what it is, type hints made fastapi/pydantic what they are.
Those worries are mostly that: worries. They don't reflect what happens in practice.
The other stuff is mostly designed by framework authors to by used by framework authors. And having nice libraries and frameworks is what really makes this language so useful for everyone.
It's trendy to claim Python is meant to be some kind of "pure environment" where the source code of Flask or SQLAlchemy or NumPy should just like a tutorial, but Python has never been that in the 10+ years I've been getting paid to use it.
I thought that was the aspiration of Go. As far as I could tell, Hacker News commenters hated Go and now it's mostly ignored and forgotten here.
Pythons massive advantage and the reason it’s so successful is it’s simplicity. Adding syntactic features only adds more surface area for people to learn when coming to the language and barriers to entry for people looking to contribute to an unfamiliar codebase.
There are a few over the last few years I truly love, such as “f strings”, the typing syntax though I’m less convinced by (and I really like TypeScript).
The recently added “walrus operator” is a good example of a syntactic addition that is both brilliant for experienced Python coders but also adds friction to new people learning the language. It’s presence in a codebase could confuse people.
Python is supposed to “favour explicit over implicit” and has always been more “wordy”, symbol based operators that aren’t inherently explicit in their meaning (like this proposed =>) are the type I’m particularly worried about.
Edit:
I just want to add, I don’t think these additions “damage” the language, it’s just the additional friction for people to pick it up or approach an unfamiliar codebase that worries me (maybe unduly). I love Python and these don’t put me off wanting to continue to use it as my primary language. In many ways I look forward to using them as an experienced Python developer.