Don’t know why python gets so much love. It’s a painful language as soon as more than one person is involved. What the author describes is just the tip of the iceberg
The same reason people are not flocking to the Lisps of the world: mathematical rigour and precision does not translate to higher legibility and understandability.
Python's list /dict/set comprehensions are equivalent to typed for loops: where everyone complains about Python being lax with types, it's weird that one statement that guarantees a return type is now the target.
Yet most other languages don't have the "properly ordered" for loop, Rust included (it's not "from iter as var" there either).
It's even funnier when function calling in one language is compared to syntax in another (you can do function calling for everything in most languages, a la Lisp). Esp in the given example for Python: there is functools.map, after all.
"the target"? The OP complaint is not that the statement exists, but that the output expression comes before the context that establishes its meaning. This is not just a problem for autocomplete, but people often report difficulties understanding complex comprehensions.
As for comprehensions themselves, ignoring that problem I find them a powerful and concise way to express a collection of computed values when that's possible. And I'm particularly fond of generator expressions (which you didn't mention) ... they often avoid unnecessary auxiliary memory, and cannot be replaced with an inline for loop--only with a generator function with a yield statement.
BTW, I don't understand your comment about types. What's the type of (x for x in foo()) ?
That one is a generator[Any], and for others it could be set[Any], list[Any] or dict[Any]. You obviously don't get embedded type information, but you still do get the encapsulating type, which is better than an untyped for loop :)
I get that it's about how it's structured and ordered, but that is true for the "for..in" loops in every language as well: you first set the variable, and only then get the context — this just follows the same model as the for loops in the language, and it would be weird if it didn't.
"that is true for the "for..in" loops in every language as well"
No, not at all. The output expression is arbitrary ... it might be f(x, y, z) where all of those are set later. You're confusing the output expression with the loop variable, which is also stated in the comprehension and may or may not be the same as the output expression or part of it. "The same model as the for loops in the language", where the language includes Python, is the comprehension with the output expression moved from the beginning to the end. e.g., (bar(x) for x in foo()) is `for x in foo(): bar(x)`. More concretely: lst = [bar(x) for x in foo()] is functionally equivalent to lst = []; for x in foo(): lst.append(bar(x))
Again, "the output expression comes before the context that establishes its meaning." ... I thought that would be clear as day to anyone who is actually familiar with Python comprehensions.
Note that I am not disputing the order is "inversed": all I am saying is that there are other common language features in most languages where things don't flow the same way, yet nobody finds it a huge problem.
It's like discussion of RPN or infix for calculations: both do the job, one is more rigorous and clear with no grouping/parentheses, yet we manage just fine with infix operators in our programming languages (or maybe not, perhaps all bugs are due to this? :)).
Just like you state a variable and some operations on it early in a comprehension, you do the same in a for loop: you don't know the type of it.
As you are typing the for loop in, your IDE does not know what is coming in as a context being iterated over to auto-complete, for instance (eg. imagine iterating over tuples with "for k, v in some_pairs:" — your editor does not even know if unpacking is possible).
Basically, what I am saying is that comprehensions are similarly "bad" as for loops, except they are more powerful and allow more expression types early.
C/C++ allow even crazier stuff in the "variable's" place in a for loop. Rust allows patterns, etc.
Typing is mostly a nice addendum I mentioned, that's not the core of my point.
> Yet most other languages don't have the "properly ordered" for loop, Rust included (it's not "from iter as var" there either).
The relative order of the variable being iterated on and the loop variable name is not relevant to OP's complaint. OP only requires that the expression which uses the loop variable comes after both, which is the case in Rust.
This hasn't been my experience, but we use the Google style guide, linters, and static type verification to cut down on the number of options for how to write the program. Python has definitely strayed from its "one right way to do a thing" roots, but in the set of languages I use regularly it gives about the same amount of issues as JavaScript (and far, far less than C++) regarding having to deal with quirks that vary from user to user.
Failure to understand something is not a virtue. That it does get a lot of love strongly suggests that there are reasons for that. Of course it has flaws, but that alone doesn't tell us anything; only comparisons do. Create a comprehensive pro/con list and see how it fares. Then compare that to pro/con lists for other languages.
> That it does get a lot of love strongly suggests that there are reasons for that.
Reasons, sure, but whether those reasons correlate with things that matter is a different question altogether.
Python has a really strong on-ramp and, these days, lots of network effects that make it a common default choice, like Java but for individual projects. The rub is that those same properties—ones that make a language or codebase friendly to beginners—also add friction to expert usage and work.
> Failure to understand something is not a virtue.
This is what I want to say to all the Bash-haters that knee-jerk a "you should use a real language like Python" in the comments to every article that shows any Bash at all. It's a pet peeve of mine.
I think you did a good job delineating top complaints people have about shell, but I have to reject the pro/con framing.
Shell operates quite naturally under stream-oriented data-centric design patterns. But that's an architectural familiar to almost nobody in our OOP- and FP-happy industry these days. Try using Python like a relational database language (e.g. SQL), and complex stuff gets messy, the syntax starts feeling hostile, logic becomes obscure, etc. The purported ickyness of shell stems not from shell per se but from trying to force it into a foreign design space.
Principles that work well with shell: Heavily normalize your data; Organize it into line-oriented tables with whitespace-separated fields; Use the filesystem as a blob store; Filenames can be structured data as well; positional parameters form a bona fide list.
> hard to escape everything correctly
IME, all of the really painful escape patterns can always be avoided. You see these a lot when people try to build commands and pass them off to exec. That's mostly unnecessary when you can just shove the arguments in the positional parameters via the set builtin: e.g.
set -- "$@" --file 'filename with spaces.pdf'
set -- "$@" 'data|blob with "dangerous" characters'
set -- "$@" "$etc"
some_command "$@"
which is functionally equivalent to
some_command "$@" --file 'filename with spaces.pdf' \
'data|blob with "dangerous" characters' \
"$etc"
It also helps to rtfm and internalize the parsing steps that happen between line of input and the eventual execve call.
Pro - there is still not another language (generalizing to "shell" here) that allows you to easily write a series of commands and/or pipelines as concisely and understandably as shell.
This is the biggest reason to use shell - if your task is shaped like a command line session with a bit of looping or a few conditionals, shell is perfect.
If you start nesting loops or conditionals in shell, then start considering another language.
It's not so much a matter of exhaustively listing pros and cons, but more a matter of appropriateness to the desired goal or goals IME. I seriously doubt that a comprehensive pro/con list can even be coherent. Is dynamic typing a pro or a con? Depends on to whom and even in what decade you ask. List comprehensions? Interpreted language? First class OOP? Any cost-benefit anaylsis will be highly context-dependent.
> I'm not here to defend tiresome strawmen.
I won't point out that you already tried to (contradiction intended). Perhaps a more interesting discussion would result if we defaulted to a more collaborative[0] stance here?
I don't have anything useful to add per se but I'd like to thank you for validating my intuition that pros and cons lists not only don't generalize as to be useful universally but also that the more "rigorous" you get with it the whole things evolves into an exercise of ridiculousness.
An example I like to give is the wood chipper. Pros it can do a lot of useful things around the yard (that you can list individually), cons it can chop your arm off. How many pros do you need to overcome that con? Though I'll admit there's a difference between "can" and "will", the latter hinging on improper use.
tl;dr I'm a bit tired with people glorifying a semi-useful cognitive device.
> Of course it has flaws, but that alone doesn't tell us anything; only comparisons do.
Comparisons won't tell us anything. If Python were the only programming language in existence, that doesn't imply that it would be loved. Or, if we could establish that Python is the technically worst programming language to ever be created, that doesn't imply that wouldn't be loved. Look at how many people in the world love other people who are by all reasonable measures bad for them (e.g. abusive). Love isn't rational. It is unlikely that it is possible for us to truly understand.
I used to agree with this completely, but type annotations & checking have made it much more reasonable. I still wouldn't choose it for a large project, but types have made it much, much easier to work with others' python code.
Python with strict type checking and its huge stdlib is my favourite scripting language now.
I agree, it's not too bad if you enable strict Pyright checking in CI, and you use `uv` and you don't care remotely about performance.
That's quite a lot of ifs though. Tbh I haven't found anything significantly better for scripting though. Deno is pretty nice but Typescript really has just as many warts as Python. At least it isn't so dog slow.
I've pretty much embraced PowerShell for scripting. The language is warty as hell and seems to be entirely made of sharp edges but I've gotten used to it and it does have a lot of excellent ideas.
Yeah I'm toying with nushell which is like Powershell but with a less offensive syntax.
But when I say "scripting" I don't mean shell scripting. I mean stuff like complex custom build systems or data processing pipelines. I would never do those in any shell language.
Actually while I use it heavily for shell scripting, it's also also my go-to for python-style scripting too. Mostly because it's the one I'm most familiar with because of professionally doing DevOps on an MS stack with a lot of legacy stuff, but still:
It's just object-y enough to be excellent at filtering and shunting and translating records from API endpoints to CSV files to SQL data rows, etc. I'm not sure I'd recommend it to anybody to pick up because of all the sharp ends (eg it uses Javascript falseyness except SQL NULL has the ADO.Net "DBNull" which isn't falsey) but because I'm so familiar with it I find it quite good at that stuff.
As much as people bag on the syntax (like the operators all starting with hyphen, and all the .NET functions speak with an accent), the real problem is how many edge-cases are in there, like strange behaviors of the error stream, overcomplicated scope rules, FileSystem providers for the registry and SQL server and other objects no reasonable person would ever want to use as a "file", empty-array objects getting silently converted into null, etc.
I find list and dict comprehensions are a lot less error prone and more robust than the “manual” alternatives.
I would say unless you have a good reason to do so, features such as meta classes or monkey patching would be top of list to avoid in shared codebases.
> For a language where there is supposed to be only one way to do things
That's not what the Zen says, it says that there should be one -- and preferably only one -- obvious way to do it.
That is, for any given task, it is most important that there is at least one obvious way, but also desirable that there should only be one obvious way, to do it. But there are necessarily going to be multiple ways to do most things, because if there was only one way, most of them for non-trivial tasks would be non-obvious.
The goal of Python was never to be the smallest Turing-complete language, and have no redundancy.
If I have to write Python like Go, I'd rather just write Go. (Not disagreeing with you, but this is one of many reasons that Python is the least-favourite language that I use on a regular basis.)
Thankfully, uv makes this a lot better, but it'll still be a while before that percolates to the entire ecosystem, if ever.
There are real concerns about tying the language's future to a VC-backed project, but at the same time, it's just such an improvement on the state of things that I find it hard not to use.
It's the exceptional codebase that's nice to work with when it gets large and has many contributors. Most won't succeed no matter the language. Language is a factor, but I believe a more important factor is caring a lot.
I'm working on a python codebase for 15 years in a row that's nearing 1 million lines of code. Each year with it is better than the last, to the extent that it's painful to write code in a fresh project without all the libraries and dev tools.
Your experience with Python is valid and I've heard it echoed enough times, and I'd believe it in any language, but my experience encourages me to recommend it. The advice I'd give is to care a lot, review code, and keep investing in improvements and dev tools. Git pre commit hooks (just on changed modules) with ruff, pylint, pyright, isort, unit test execution help a lot for keeping quality up and saving time in code review.
I love python, as long as were are talking about small teams, short, and short lived programs. The lack of static types makes things quick to implement even if it isn't too sound, and the strong typing keeps you from fucking up too badly. That is why I think it gets so much love in data science, it is great for noodling around while you are trying to figure something out.
On the other hand if you are going to be building something that is going to be long lived, with multiple different teams supporting it over time, and\or larger programs where it all doesn't fit in (human) memory, well then python is going to bite you in the ass.
There isn't a one size fits all programming language, you need at least two. A "soft" language that stays out of your way and lets you figure things out, and a "hard" language that forces the details to be right for long term stability and support.
Because everything that tries to fix it is just as painful in different ways.
I've had the displeasure of working in codebases using the style of programming op says is great. It's pretty neat. Until you get a chain 40 deep and you have to debug it. You either have to use language features, like show in pyspark, which don't scale when you need to trace a dozen transformations, or you get back to imperative style loops so you can log what's happening where.
Python was established as a fun and sensible language that was usable and batteries-included at a time when everything else was either expensive or excruciating, and has been coasting in that success ever since. If you'd only coded in bash, C/C++, and late-'90s Java, Python was a revelation.
Lists comprehensions were added to the language after it was already established and popular and imho was the first sign that the emperor might be naked.
Python 3 was the death of it, imho, since it showed that improving the language was just too difficult.
It's my 18th language and I prefer it over the alternatives. If I need to write an app I'll use Swift. If I need to do some web work I'll switch to TypeScript. To get work done it's Python all the way.
I'd argue it's because the web is the dominant platform for many applications and no other languages can offer a first class experience. if WASM had direct access to the DOM and web APIs and maybe a little more runtime support to lessen the bloat, I'd use something else.
JavaScript has been a backend language long before the web was the dominant platform.
And one of the, admittedly many, reasons why web technologies like Electron and React Native exist is because it’s easier to find JavaScript developers vs Kotin, Qt or whatever.
So youre not wrong but you’re also downplaying the network effect that led to the web becoming dominant. And a part of that was because developers didn’t want to learn something new.
Actually I would say that was the turning point when the web started to become the dominant platform. Not the conclusion.
Before then it was Flash and native apps. Before then, smart phones weren’t common (eg the iPhone was just over a year old at that point) and the common idiom was to write native apps for Windows Mobile.
IE was still the dominant browser too, Firefox and WebKit were only starting to take over but we are still a long way from IE being displaced.
Electron didn’t exist so desktop app were still native and thus Linux was still a second class citizen.
It took roughly another roughly 2015 before the web because the dominant platform. But by 2005 you could already see the times were changing. It just took ages for technology to catch up. The advent of v8 and thus node and Electron, for that transition to “complete”.
> It wasn't until 2009 with the introduction of Node.js that JavaScript became a viable option for backend development.
JavaScript was used for backend development since the late 1990s via the Rhino engine (the backends wouldn't be pure JS generally but a mix of JS and Java; Rhino was a JS engine for the JVM with Java interop as a key feature.)
That’s dumb. Your bosses will decide to pay copilot instead of paying you. We should use llms in a way that empowers us, not in a way that replaces us.
The difference between Apple and the CCP is that CCP is the one running the slave labor to make widgets, and Apple is the one paying for it and puts the sticker "Designed in California" to wash it off.
I think the tricky part lies on knowing which things can be done without consulting any product owner. I agree that created_at and updated_at don’t cause any harm. deleted_at on the other hand cannot be decided by engineers only (mainly because of GDPR reasons: if something is expected to be totally deleted, then that must be it). As usual, these kind of things are obvious to engineers with years of experience , not so much to newcomers.
You don’t know their performance at their jobs. You don’t know either if they fulfill family obligations decently. You don’t know how their lifestyle is affecting their health long term.
How big is the jazz band? If you have a small jazz band, that's fine. If you have a big band, you need someone to organize logistics. Somebody to book events. And if you're trying to make a profit, you might even need to adapt your style or respond to customer needs, such as a particular attire for different events. You might even have someone in that particular role. If there is a tour, it might even be a full time role.
But that changes from team to team, and sometimes even from individuals to individuals. It’s rare the company (not faang level) that invests that much in engineering culture.
Of course it verifies from to team. Just like culture varies between any in-group. The culture of a larger group is kinda like the average of smaller inside-group customs.
A company that does not consciously cultivate culture will still have a culture, but it may be like driving a car with no hands on the wheel. At 5 companies so far, someone or group is ay least attempting to steer.
Fresh bread delivery. There’s nothing worse than waking up and find out that you dont have bread. Would be great to have fresh bread at my door every morning.