From Python to NumPy (2017)
labri.fr
labri.fr
[0] https://github.com/rougier/scientific-visualization-book [1] https://rr-france.github.io/bookrr
using Random, BenchmarkTools
seq = rand(0:2,10_000); sub = rand(0:2,4);
# translation of the "readable" but slow python code
function_1(seq, sub) = [i for i in 1:(length(seq)-length(sub)) if view(seq,i:i+length(sub)-1) == sub];
@btime function_1(seq, sub);
# 93.858 μs (5 allocations: 1.98 KiB)
Which is more than twice as fast as the vectorized (and quite unreadable) python %timeit function_2(seq, sub)
215 µs ± 1.94 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Having a JIT (like julia, numba...) has really a bit potential to improve code readability and reduce the need of vectorized numpy operations. In some cases, it can even be faster due to be better memory utilization and by computing more directly what you need.Well, if you look at the thing it has actively maintained libraries for with active communities, it's a lot besides ML/DS.
That said, I think a NumPy to Python, or really any big package or framework to Python (e.g. Django to Python) is the normal progression these days. People seem to learn the language through a package and end up with some misleading inferences.
Again, I don't intend to diminish the article, I just see the opposite progression so much I figured I'd comment on it.
It always has been common from the time Python had popular domain-focussed libraries that could be used without deep knowledge of the rest of the language, but once you get there you also tend to move out to other specialized packages.
I remember it being quite fun to build these vectorized numpy functions (in the crosswords and puzzles are fun, kind of way)
Figuring out how to make a transformation in Pandas is always frustrating, when it isn't straightforward.
https://betterprogramming.pub/numpy-illustrated-the-visual-g...
Perhaps even further, the shift now is for "Python" to be written in neither vanilla Python nor NumPy directly, but at times in PyTorch or TensorFlow where each library provides its own workarounds.
Python is a terrible language compared to many others, not the least of which is Ruby (and I would include Java, Clojure, Elixir, and even C++ and probably C# and almost certainly F# if I knew them well).
To make matters worse, there's a common mentality amongst Pythonistas of being aggressively complacent. A typical response to a question about a missing capability is, "Why would you ever need that?"
I have a lengthy collection of "why Python sucks" notes. However, I would argue that anyone who disagrees that Python is bad (excepting the practical capabilities afforded by libraries like Numpy which could just as well have been build for other languages) has simply not spent enough time other other, better languages.
Python is Blub, from the Paul Graham essay Beating the Averages. http://www.paulgraham.com/avg.html
Python's community is one of the greatest and worst parts of the language. Python might not be the best language for anything, but it's the second best language for everything.
I've dabbled in many programming languages, a lot of which are backed by a single large company or consultancy:
- Java (Oracle, at least initially)
- TypeScript, C# (Microsoft)
- Golang, Dart (Google)
- Elixir (Nubank)
- Clojure (Cognitect)
- Kotlin (JetBrains)
- Objective-C, Swift (Apple)
- Julia (Julia Computing)
Then, there are languages like Python, Ruby, JavaScript, PHP, Rust, etc. that are more diversified and distributed in terms of core contributors, maintainers, and evangelists. Meanwhile, there's a long list of other lesser known languages that have failed to grow a community as large as Python's.
As I get older and older, I have realized that our programming languages are not static entities, but they change and evolve over time. They are influenced by users, companies, and researchers. I hope that one day, there's a humanities subject that studies programming languages just like we study natural languages like in linguistics. The closest thing right now seems to be history of science, but it's quite a broad field.
If you come from a Ruby background, and you are now doing Python, you may find yourself on Stackoverflow looking up how to do X idomatically in Python (something you did all the time in Ruby). Or you may ask colleagues who know Python but do not know Ruby.
A common answer from the Pythonista community is, "Why would you ever want to do that?" (or Why would you need that?)
Ternary operators? Who needs that. Ok sure, here, but let's make it awkward. Where did Python get the model to do "x = 1 if y else 2"? But ironically, unlike Ruby where you can say "x = 1 if y", in Python you cannot do that. Oh, but you cannot say "x = 1 if y else return". That's not allowed.
Python has finally, as of 3.10, added a switch statement. This is something that many other languages have had for ages. You should see the contortions people have gone to to approximate a switch statement in Python... https://stackoverflow.com/questions/60208/replacements-for-s...
The Python community may have some good, helpful, and friendly people; but most programming communities have that. That is not a plus for any language, because it should be a given.
My list of python gripes are long, but language constructs are not on the list.
You can say "x = 1 if expr else 2", but you cannot say "x = 1 if expr". You do a two line
if expr:
x = 1
You cannot say "x = 1 if expr else return".[1] is a list with one item, 1.
{1} is a set with one item, 1.
(1) is a number, 1, with pointless parenthesis. Perhaps you mean (1,), a tuple containing only one value.
Or how about mutable default values in function paramters? That makes a lot of sense...
I don't need to keep offering examples. You can search the internet and find many places where people list the warts, inconsistencies, and misfeatures of Python.
This is exactly the kind of response one often hears from the Python community. Translated it means, "I'm ok with it this way, so who cares if it could be better?"
This one liner works:
if expr: x = 1
> Or how about mutable default values in function paramters? That makes a lot of sense...This can be helpful under certain circumstances (e.g. memoization) [1]. The linked article also points out that it can be useful for rebinding global names in optimized code.
[0] https://stackoverflow.com/a/1145781
[1] https://web.archive.org/web/20200221224620id_/http://effbot....
But PEP8 and now the all-too-popular Black formatter disallow this. I think it's fine.
The momoization/optimization point is exactly the opposite of what should be a default use case for a beginner friendly language. And I highly doubt this mutable default design was intentional for these purposes. They are footguns.
But one you need to take that POC and build a real product which needs to be supported, extended, and maintained, then you must choose tools which fit that long term need.
Many of use first experienced school in kindergarten. Kindergarten was an important step in our education and socialization. But we didn't refuse to move on to the next level, no matter how much fun it was or how nice our teachers were. Maybe some people felt unsure about leaving their comfort zone, but there's no progress without risk and effort.
Is a university student elitist because they know something a kindergarten kid doesn't? Should they stop trying to promote continued growth through the educational levels?
If you were to use production Python on any decent sized project, and you had also used Ruby (just one example... some other languages can be even more succinct), then you would hate Python.
Sure, maybe we should be rewriting POC code in a language that's more fit-for-purpose if we're talking about a large project, but for that you need mature libraries, or a team with some fairly serious chops (i.e. decent math background, senior coders, expensive to hire, and hard to replace). There's a lot to be said for a language that makes it easy to be productive on technically demanding problems, while also making it easy to find new talent. As someone who's written code relying on linear algebra and CV in Rust and C#, and I'd honestly pick Python every time for those types of jobs. It's not perfect, but that's not the benchmark.
Just as Python wraps the libraries which do the heavy lifting in C, I would try to keep the Python wrapper as minimal as possible and stick some interface in front of that. Instead what happens often is people choose to either build more and more around this important stuff, staying with Python, or they say, "We're better off just supporting one language in-house, and Python is required already; so we are a Python shop."
If you’re building a production system with a small, stable team, and Python is a good fit for the work, then there is no reason not to use it.
If you’re doing programming in the large with a large and constantly changing team, then you’d be wise to pick a statically typed language that many people know (basically C# or Java)
Your education example is off base. Python is not a more basic form of programming that one passes through on the way to enlightenment. It’s hand screwdrivers vs hammer drills or diesel pickups vs electric sedans. Different tools for different contexts.
Then you turn around and mention Ruby. I recall that there was a lot of hype around it for quite a while, largely due to Ruby on Rails (certainly outhyping Python), and it's popularity in terms of usage was very similar to Python. However, that did not last, so why is that? Are you saying Python "hyping" and default installation only happened after ~2011 (where Ruby really started to decline compared to Python)?
I would argue the success of Python is largely due to people building some packages like numpy and scipy in Python, because they sort it was the best tool for the job. Then came the ML wave and Python due to that ecosystem was ideally placed as a language that was easy to use for ML researchers. That you mention C++ even in that comparison shows that you do not understand why people use Python. I can tell you if I had to get my graduate students to use C++ for their analysis and lab automation tasks, we'd still be graphing by hand.
Numpy is fantastic and so is scipy, but there's no inherent reason why they couldn't have been "numruby" or "numlua".
With that said, I agree with you that your parent's thesis isn't really operative, Python and Ruby are almost exactly the same technology, therefore the decision on which one to use depends on the available ecosystem. Everything else is effectively rounding error.
So it's bad, except for all of the practical things you can do with it. And other languages could have had something like Numpy ... but didn't, for some reason. Not really seeing why this makes Python bad.