I liked f-strings. Asyncio has its place but is waaay overused. Typing was a cool idea, but so awkward in practice.
All of it has really turned me off, personally. It’s getting to where reading libraries is just painful. The simplicity of the language was really beautiful and when I wanted type safety, etc. I’d use something else. I can still write simple Python, but it’s more all the other code I need to grok.
I find myself going to Go more and more for stuff I used to use Python for. It’s easier to set up a dev environment for other people, easier to distribute code, and gives me type safety and concurrency as first class citizens while being a very small language.
I miss dictionary comprehensions and other shortcuts from time to time. But it actually feels more ergonomic now.
All personal opinion. I’m fine shifting languages. I still write C from time to time to bang bits. I play with various LISPs to exercise my brain. I never used Python for performance critical code. I guess ML is changing that need.
But I know others that want Java to have functional features and Python to have this stuff.
I’m not saying I’m “right”, just uncomfortable in a place I used to love to hang out in.
I'm very happy python survived the 2-to-3 migration and is now thriving more than ever! Looking forward to the 3.11 speed improvements.
Go and Python code may have significant difference. But there is spiritual overlap in the sense that Go really encourages a straightforward imperative programming style, which makes its code more accessible to a wider range of developers.
The Go fans keep insisting that this error handling in Go is new and fresh. I looked at my Go code, I saw that 2/3 of it is basically checking if a function returned an error, and I moved on. I've done it this way - I am not going there again.
And no, it doesn't force you to handle errors. You can simply not check for errors, just as you would swallow an exception.
My impression of the Go culture is that of the hip kids discovering vinyl.
As my former boss used to say - "there is an easy way, and there is the cool way".
As an anecdotal data point... I like Python and prefer it to Go for the most part. However, in the case of error handling, Go seems to get it right and Python does it very wrong. Exceptions are extremely confusing, I just can't wrap my head around them. I don't even accept, philosophically speaking, the concept of exception: there are no "exceptions" when running a program, only conditions that you dislike.
I work in both Python and Go. I love both languages. Here are a few reasons why I chose Go over Python for a lot of things these days:
Alot faster, self-contained executables instead of virtualenv, scales natively, no GIL, linear code in goroutines instead of asyncio.
Especially considered the obligatory compilation step and lack of IPython.
And while there it certainly does provide a lot of benefits, it does make the language look more and more like a huge pile of patchwork and afterthoughts. It neither feels simple nor consistent and the tooling is a mess as well, both overly complex (e.g. half a dozens tools to do basic type checking) and lacking really basic important features (e.g. easy way to compile ship able binaries).
Especially after all the pain of the python3 transition, it feels just frustrating how much of a mess it all is. The "only one way to do it" philosophy seems to have been thrown overboard a long while ago.
Type annotations, imho, are actually useful and not really hard to read.
Walrus is...fine. I don't use it much, but I defo see how it's one more thing.
Python's biggest complexity, imho, is its almost unrivaled dynamicism. There's just too many degrees of freedom at times.
The import system continues to be a nuclear footgun. I consider myself a python expert with 15 years under my belt and today I was still fighting with imports in a pytest suite. Like wtf.
I’d be cool with a statically typed flavor that wouldn’t allow for the type gymnastics.
But that’s gross with the None situation where you’d have to fall back to magic values vs ‘nil’ or similar concepts. None was one of my favorite things back when other languages didn’t address it.
And a lot of my opinion is probably based on the fact that I came to the language in the late 90s from a Pascal/C background. Probably just getting to old :).
In the end, I'm glad that Python is changing to meet the changing developer zeitgeist, even if I might have to play catch up with new features sometimes. It means Python will stick around and won't be considered "old and outdated" and fall out of use because it failed to keep up with what the developer community wants and expects. It's also the same reason why I'm glad that Go got generics.
I love stuff like 'match', the walrus operator, Typing, etc. but it definitely feels like a lot to grok and it will be increasingly hard for new programmers to read arbitrary python code without grokking all of the new stuff.
But then yea, 1 line of python using an inbuilt function solves a 10 line go routine in a hackerrank.
I'm not sure I get this. Asyncio is a serious commitment. You don't really want it unless you really want it. And even then in the libraries you may want both the sync and async path. I feel like most asyncio libraries would just not exist without it - so if you're not interested in that area, can't you ignore them?
Most people thought otherwise, because they don't bother to read the reference manuals.
Which is why making an already complex language even more difficult a very big mistake.
These days Python is targeted towards general scripting, data processing, scientific computation, etc. It’s really good as a glue language or for anything involving numpy. I use Python every day but I would not use it to build applications.
It’s really only good for apps or things I (or other Python people) are going to run.
Part of it is that I don’t do analytics work. Now that PERL is niche, my use cases are no longer the target.
Can't you still use all those old features just as you did before? If you don't like the complexity of newer libraries and features, they're still optional aren't they?
Or did something fundamental shift (aside from the obvious P2->P3 switch) that has made existing features harder to use?
Compared to C++, where you can do incredible zero-overhead magic with the type system; for example duck-typing, once The Way, is now an absolute nightmare in Python's type system. Python typing is little but baggage and warts unless you've drunk the cool-aid and then you're horrified that people like me haven't.
(and, all that said, I'm quite excited about py3.11, it's great on performance and ergonomics)
Chances are you're not programming in a vacuum. These features will be used by third party libraries and you will have to read/understand this code.
Even in your in house codebase, there will always be co-workers that want to use these features.
The optionality of language features is not real. If they exist, they will be used, you will have to understand them, they will leak from other libraries, etc.
A lot of these issues just aren’t there in C++, and you probably wouldn’t even try to share a codebase for something with someone who’s not another expert. …and you’re most likely working on something big.
The big thing that’s plagued Python since 3.x is the distribution problem. You can’t just hand someone a script and maybe another one to user-install some requirements. You need to BYO Python, dependencies, etc.
Things like types, library management, etc. are being handled by external tools. If I want to onboard someone to collaborate with me they really have to have my version of Python (so probably pyenv), Pipenv or Poetry, mypy or whatever, pytest or whatever, black, and have the same config for some of them.
The verbosity overhead of Go (or Nim, etc.) is worth the trade off to me for most of my uses of Python. I can just hand someone a binary and the dev just needs the language installed and maybe like goimports.
I’ve never been married to one language. I like to use them for their wheelhouse. I’ll still use Python for 1-offs or some things I can ship in a container, etc. that I’m only working by myself or with experienced Python people on.
A lot of this is that it’s really turning into more of an Analytics language. The output isn’t expected to be shared and everyone around you is a Python dev.
And I’m old. I started when Python was so pretty to my Pascal/C self compared to PERL (shudder). The documentation is still amazing for the language and standard library. (No Google, I want the actual docs.) And you could onboard almost anyone to work on it if they had any programming background because it was such a simple language.
The 2->3 shift was really only bad for complex legacy codebases and when you primarily deal with bytes vs Unicode. The latter is really painful anywhere. It didn’t bother me much. I usually just had to fix a few small things or it gave me a good excuse to kill something that should have been retired anyway.
For the same reasons that you can't "just" use C++11 and ignore C++20.
The ecosystem moves forward, and you can't just sit behind no matter how much you would like to.
What I did want to mention was the improvements to the errors where you can see exactly what line, function call, etc., is causing the error will be a huge game changer in my opinion for the language and for adoption. It's ergonomic changes like that that make me bullish on the language going forward.
I’ve rewritten 3 CLIs because it’s too much of a pain for people to get them set up.
I still use it for myself to whip through a CSV, collate data from APIs. Sometimes a tiny API or web app (I really don’t like webdev) where I can deploy a container or pipeline deployment.
And I don’t have to use all the new bells and whistles. But so many do.
The type hinting system in particular is so much more painful to read (to me) than in a language with actual types.
Not specifically a new feature, but I remember wasting a good bit of time when coming across a line that was essentially:
return headers.get('x-foo') == settings.FOO_KEY is not None
and being extremely confused until discovering it was a use of comparison chaining[1] and was actually doing the right thing.[1] https://docs.python.org/3.10/reference/expressions.html#comp...
> Python 3.11 is up to 10-60% faster than Python 3.10. On average, we measured a 1.25x speedup on the standard benchmark suite. See Faster CPython for details.
* https://docs.python.org/3.11/whatsnew/3.11.html
* https://docs.python.org/3.11/whatsnew/3.11.html#faster-cpyth...
Very nice.
The gains are real.
Maybe I'm being too negative and a faster browser API will be developed.
Many a student turns away from a language because the developers underestimate the importance of error messages.
I doubt that typing Python code will ever become as ergonomic as typescript, simply because typescript doesn't have the constraint of modifying the syntax of it's "target language" (JS) when it wants to add a feature or whatever, whereas typed python must still be Python, and syntactic changes therefore need to be added much more conservatively.
But still, the typing story is improving before my very eyes with every release, and given that Python types are actually available at runtime (allowing for things like pydantic and FastAPI), and that Python has massive adoption in the ML space, all this is making Python a very enticing language to get back to using!
1. No way to fully type `args` and `kwargs` which are sadly common in Python code. 2. Type semantics aren't defined so there are multiple competing interpretations. Not great if your dependencies use one type checker and you use a different one. Not that that will matter because... 3. A depressingly huge part of the Python community doesn't get static typing so a ton of your dependencies won't have types. 4. The most popular type checker - MyPy - is full of bugs and deliberate unsoundness. Pyright is much better but also newer so lots of projects don't use it.
I definitely wouldn't choose Python if I had a choice.
I think "parameter Specification Variables"[0] come pretty close to solving that no? I'd love to hear your take if you've used them before.
> Type semantics aren't defined so there are multiple competing interpretations
> The most popular type checker - MyPy - is full of bugs and deliberate unsoundness.
100% agreed. I hope Pyright "wins" as the dominant type checker, because like you said, it's MUCH better in every aspect.
> A depressingly huge part of the Python community doesn't get static typing so a ton of your dependencies won't have types.
100%. I have so many half hearted attempts at typing my dependencies![1]
> I definitely wouldn't choose Python if I had a choice.
At this point, I'd have to agree, except of course for data science/ML things.
[0] https://peps.python.org/pep-0612/
[1] https://github.com/davidatbu?tab=repositories&q=stubs&type=&...
I think working with dicts is still a little clunky in python, and there are some rough edges (eg mypy being a little weaker), but I genuinely think I like types in python better than TS!
That's a step further than I'm willing to go :D. For example, the other day, I was able to do
settingToToggle: KeysWithTypeOf<SomeObject, boolean>
which (after properly defining `KeysWithTypeOf`) allowed me to specify that `settingToToggle` should be a string literal that is a key on `SomeObject` whose corresponding value is a boolean.Typescript comes closest to fully letting me express my intentions statically and curtly, and while I hope Python's static typing will grow to be as expressive as Typescript, I think that day is still afar off.
I really want a declarative solution like Rust's Clap library, but I haven't yet found anything like it out there.
I would actually consider this to be one of the weakest points of python types
Variadic protocols don't exist; many operations like stacking are inexpressible; the synatx is awful and verbose; etc. etc.
I've written more about this here as part of my TorchTyping project: [0]
[0] https://github.com/patrick-kidger/torchtyping/issues/37#issu...
I hope that these things could be solved with future iterations. For example:
Variadic protocols don't exist.
Hopefully they are added sometime. the syntax is awful and verbose.
Hopefully we can settle on allowing `1` instead of `Literal[1]` as a type (and other similar improvements). many operations like stacking are inexpressible.
These could be expressed if things like "multiplying"/"adding" literal numeric types would be supported.The variadic generics PEP was partly motivated by the ML use-case (and took input from maintainers of numpy ..etc), so I hope future iterations will also improve the usage in the ML space.
I agree that tracking array shapes at build time could catch certain classes of bugs (and I would love to have that capability). However, it's not as easy as it may seem. Consider the following example. It shows that even a simple slicing operation can change the number of array dimensions in ways that are only known at run time.
>>> def f(arr, elem):
... return arr[elem]
...
>>> arr=np.zeros((1, 1))
>>> f(arr, 0).shape
(1,)
>>> f(arr, ...).shape
(1, 1)
>>> f(arr, np.newaxis).shape
(1, 1, 1)The only issue now is that pytorch has their own "shapes" solution[0], and last I checked, were kinda reserved about participating in the standardization of variadic generics because they don't expect to use it.
I really really hope that the ML community comes together to use variadic generics because I believe it will save researchers and devs so many debug cycles (as well as compute resources, tbh).
(Judging by https://docs.python.org/3/library/, there are 17 of them already, disregarding zlib, mostly niche and of ancient lineage. Comparing with https://docs.python.org/2/library/, it looks like three have been added in the py3k era. reprlib in 3.0 (with unpythonic naming conventions, dunno what’s up with that), pathlib in 3.4, and graphlib in 3.9 (though the version it was added in is missing from the page; I invite someone else to fix this or file a bug report)—so I guess -lib suffixes aren’t quite as dead as I’d thought.)
However you prefer to work, your tools may now be easier to test and maintain. This change is good for everyone.
Variadic generics will (I hope) be very nice for typing pre-2.0 SQLAlchemy code. `Query[*Ts]` or similar would be a natural type for many values from the ORM API, and hopefully allow expressing types for query transformations like adding or removing columns, joining to specific query shapes, adding arbitrary subqueries, etc.
The self-type might unblock a testing tool I haven't shipped because the DX isn't where I want it. IIRC: normal TypeVar binding plus the TypeVar hack for self-types, interacting with Mypy's particular way of detecting the type of a descriptor, interacting with the way the project uses mixins, sometimes produce types that technically aren't wrong but lead to spurious type errors in user-defined data fixtures. So I'm probably the only one on earth with this particular problem.
I don't know, it's been a while. Maybe it won't work. But the self-type hack is noisy anyway and getting rid of it is nice.
Shipping TOML in the standard library means I can drop a bunch of Tox gymnastics in projects that support pyproject.toml or TOML-based configuration in addition to the older standards, and now I'm not stuck either forcing certain TOML libraries on people or working out a way to plug in their choice. It's not that hard, but complexity multiplies, and now it's one less thing to deal with.
Part of that has been that Ubuntu 22.04 has 3.10 in it. So, while I'd like to go up to 3.11, I'm not sure there's a good story for adding 3.11 to a 22.04 system.
Am I missing out?
TaskGroups should make things even less awkward, too.
Using multiprocessing creates a LOT of overhead and is really intended for parallelism and is only the better solution if you're CPU-bound. If you're I/O-bound, then what you're really looking for is concurrency, and using asyncio will be more performant.
pool+map is useful but it is not general.
https://docs.python.org/3.11/library/typing.html#typing.reve...
To be able to say that a method returns the same type as the class itself, you had to define a TypeVar, and say that the method returns the same type T as the current class itself.
This kind of stuff is so crazy to me. I see how types are useful for defining general categories of data (int, string, float), but isn't it better to have as few types as possible? It just makes reading and using code more confusing to keep multiplying "types" like this.
What Python really needs is an easy, built-in way to create mutable structs, without an overhead of creating classes. I think it's a failure of language design to miss such a useful and basic feature.
For everything else, use a function.
It has been talked about for a long time, probably 4 years now.
> It is crystal clear, that data['profits']['yearly'] was None.
I wouldn't call that exactly "crystal clear" for someone not somewaht experienced with the language or software in general.
For example, the changes to TypedDict in 3.11 might actually get me to try them out again.
Consider the statement "I would like to learn C, but the lack of significant indentation is a huge turn-off for me". It has the exact same validity as your statement.
But stay smug.