Table of Contents for Programming Languages: a survey
Python
Because it is so well-known and well-liked, Python gets its own chapter.
Pros:
- Readable
- "There's only one obvious way to do it" (TOOWTDI)
- pervasive data structure protocols: sequences and map ('dict')
Cons:
- GIL
- "Anything to do with package distribution... There are problems with version skew and dependencies that just make for an "endless mess". He dreads it when a colleague comes to him with a "simple Python question". Half the time it is some kind of import path problem and there is no easy solution to offer." -- summary of the Python founder, Guido van Rossum's answer to the question "what he hates in Python" [1]
Features:
Tours and tutorials:
Best practices and style guides:
Respected exemplar code:
Gotchas:
- http://www.toptal.com/python/top-10-mistakes-that-python-programmers-make
- http://lignos.org/py_antipatterns/
- in Python 3 (but not Python 2), 'type('a'[0]) == type('a') == str', and strings are iterables, so if you try to recursive on a tree represented as nested lists with string leaves by checking for iterable-ness and iterating them if so, you'll go into an infinite loop, because a 1-character string is iterable, but the first element in it is another 1-character string...
- "I find the order of iteration in Python for comprehensions totally confusing every time. If you want to convert: for a in x: pass into a comprehension, you write: [pass for a in x] Simple, right? So to convert for a in x: for b in y: pass I obviously write: [pass for b in y for a in x] Bzzt. Nope, you have to write it in middle-endian order to get the same behaviour." [18]
- "One of the biggest design mistakes of iterators in Python is that StopIteration? bubbles if not caught. This can cause very frustrating problems where an exception somewhere can cause a generator or coroutine elsewhere to abort." [19]
- "...many Python modules include initialization functions that run during the import. You don't know what's running, you don't know what it does, and you might not notice. Unless there's a namespace conflict, in which case you get to spend many fun hours tracking down the cause." [20]
- If you assign a reference value into a new variable, it doesn't create a new copy of the value (because it's a reference type, not a value type) [21]
- True is 1 and False is 0. So if you do dict_a[1] = "apple"; dict_a[True] = "mango", the second assignment overwrites the first. [22]
- list.append, list.sort, dict.update, and others update in-place, and don't return the resulting container. So e.g. list_a = list_a.append(6) sets list_a to None. [23]
- in some cases, strings are interned, so two 'different' strings with the same value are actually the same (and this is exposed via the 'is' operator) [24]
- default arguments are evaluated only once. So if you have 'def func(a, lst=[])' and then within this function you call list.append(), you mutate the one copy of lst, and the next time this function is called, lst will no longer be [] [25]
- in at least some cases, variables captured by closures must be marked by the 'nonlocal' keyword (see e.g. [26])
- >>> powers_of_x = [lambda x: x^i for i in range(10)] >>> [f(2) for f in powers_of_x] [512, 512, 512, 512, 512, 512, 512, 512, 512, 512] [27]
- "This PEP ( https://www.python.org/dev/peps/pep-0292/ ) tries to replace % as an operator, because it should not be used for string formatting. " -- [28]
- https://github.com/satwikkansal/wtfpython
- https://stackoverflow.com/questions/17202207/why-does-true-false-is-false-evaluate-to-false
- "Once I wrapped my head around when you can and can't use relative imports, I've been pretty ok with them. The think that irks me is whether they work changes based on where you've invoked Python from. `./bin/my_script.py` behaves differently from `./my_script.py`."
- using a mutable default value [29]
- https://codereviewdoctor.medium.com/5-of-666-python-repos-had-comma-typos-including-tensorflow-and-pytorch-sentry-and-v8-7bc3ad9a1bb7
- https://github.com/satwikkansal/wtfpython
Popularity:
Retrospectives:
Tools:
Opinions:
- "steady and reliable workhorse for years..conservative language..Python isn't going to get super performant..doesn't currently deal with the modern web (async, concurrency) as cleanly as I'd like" -- [31]
- "I've always found the def __init__ method with 4 (!) underscores one of the ugliest things I've seen in a programming language." [32] (but see [33])
- "Take something like __str__ and __repr__ combined with str(), repr(), vars(), dir(), print(), pprint.pprint()... I have no idea how anyone ever thought it was a good idea to have that many ways to output the content of a variable. Even after all the time I used python I never managed to find a consistent way to output variable values." [34]
- " A lot of people like the Python syntax. It has to be about the most readable of all computer languages. " [35]
- "Python really has more than just batteries included, it's probably the language with the most available libraries after C++." jonathanstrange
- but pjmlp replies "Java followed by .NET, beats both reagarding available libraries, unless you mean only open source ones. And JavaScript? with their one liner packages probably beats all." pjmlp
- http://xahlee.info/comp/python_problems.html
- complaints and suggestions about Python's import system: https://news.ycombinator.com/item?id=24252425
- "Ugh... The type hierarchy of Python is painfully intertwined with the implementation of its type system. That implementation varies by Python implementation, and since the Python language is as old as Visual Basic, Borland Pascal, Werther's caramels, and trilobites, it varies dramatically over time. Add to that the fact that Python has a philosophy of wearing leaky abstractions proudly on its sleeve and barfing implementation-level details at its users. Python really is one of the most complex, arbitrary, and poorly abstracted languages in popular use. I have no idea how anyone lives with it for anything except writing extremely repetitive, formulaic, and superficial code, because having to dig into its guts feels like a nightmare. Even Java's type system, as antiquated and simplistic as it is, is easier to understand and work with by comparison. " -- [36]
- "in order to do package management, you have to create a fake python installation and bend PYTHONPATH. Virtualenvs are the canonical way to do it, but to me it feels like a hack - the core language seems to wants all packages installed in /usr. So now I have all these virtualenvs lying around and they are detached from the scripts."
- "Python import system is by far the worst one I dealt with. Using Setup.py and regular or namespace packages, relative import, having complex sub packages and cross importing, running a script from somewhere inside one of your sub packages, and many more craps like these. Import system must be intuitive and easy to use! " [37]
- " Yeah it really tripped me up as a beginner. I think the hardest part to get used to was that the import syntax actually changes based on how, and from where, you are running your code. So depending on how you call your code, your imports might or might not work. This is ESPECIALLY painful when you are building a distribution. There is no syntax that works for all situations, which seems like it would be pretty important for an import system. I had to bookmark this tab, and still refer to it often." [38]
- "Problems are installing libraries and actually distributing your code so others can use it" -- [39]
- "...I...want a do-while" [40]
- https://news.ycombinator.com/item?id=20672051
- "...I’ve never found myself needing lambdas in Python. The statement-oriented nature of the language and the ability to write a function definition basically anywhere means that any place I would reach for a lambda in another language (filtering over a list), I just give the “anonymous function” a name and then use that name immediately below. It’s “hacky” or “uglier”, but it’s also immediately obvious what’s happening and not any slower." -- [41]
- https://avi.im/blag/2023/refactoring-python/ (this is really a complaint about dynamic typing)
- https://news.ycombinator.com/item?id=36018621 (comment on https://kobzol.github.io/rust/python/2023/05/20/writing-python-like-its-rust.html )
- https://lobste.rs/s/dk82je/writing_python_like_it_s_rust (another comment on https://kobzol.github.io/rust/python/2023/05/20/writing-python-like-its-rust.html )
- https://jeffknupp.com/blog/2017/09/15/python-is-the-fastest-growing-programming-language-due-to-a-feature-youve-never-heard-of/
- https://www.linuxjournal.com/article/3882
- https://news.ycombinator.com/item?id=37317590
Python Opinionated comparisons
- "I prefer the more talkative/english style of ruby and the use of functions over list and dict comprehension." "I find chained enumerator / block style code much easier to write and understand than list / dict comprehensions too. It's probably the single biggest thing that I don't like about Python; lambdas are simply too awkward, but lambdas + monadic transformations are probably my primary mental abstractions for writing software these days." [42] [43]
- "...there doesn't seem to be built-in support in Python for having attributes initialized by the the constructor. For example in Perl 6, I can write class Point {has $.x; has $.y;} ... and I get a constructor Pair.new(x => 1, y => 42) for free, no need to write custom initializers." [44] (but "Nothing built in, and generally not recommended, but you can do it! If you want to be really flexible and just allow any attribute, you can do this:
class Foo:
def __init__(self, *args, **kwargs):
self.__dict__.update(**kwargs)And it will automatically assign any keyword arguments you use as attributes to the object. For example `foo = Foo(name='Bob', age=99)`. If you still want to keep a strict list of allowed attributes, you can define them as parameters, and use a shortcut to assign all local variables to attributes.
class Foo:
def __init__(self, name, age):
self.__dict__.update(locals())
del self.self" [45]- vs. Java: "I felt I had been wasting my time writing XMLs and type hierarchies, while there was a simpler way to do about everything: dynamic typing gave me polymorphism for free, the data structures were built-in and had literals, string manipulation was just amazingly easy, you could have standalone functions and pass them like values, a lot of the Java design patterns were reduced to one-liners...But besides it feeling better than Java, what’s still unique for me about Python is the set of principles that are best expressed in the Zen of Python. It felt like every bit of the language followed those principles and it encouraged you to do the same with your own code;..." [46]
- Common Lisp vs Python: "...it just didn’t feel right, in a lot of ways it was the opposite of what I loved about Python: the code was very difficult to read (not because parens or prefix notation, just because it was filled with symbols), the operator set was huge, with really weird names and not dynamic at all, I remember it having like six equality operators for different types. Add to that it’s an old language with a small community and zero chances of getting a job using it..." [47]
- "Truth be told, I don’t think I miss Python all that much, at least not while I’m doing Clojure. JavaScript? is a different story, JavaScript? is a mess. But still, if you bend it in the right directions, it can be a fairly decent functional language. I noticed that Python can’t, there’s stuff that just doesn’t work that way (lambdas come to mind), and that probably would annoy me nowadays." [48]
- "I have found Python to break down as I scale it into a larger system spanning multiple directories and modules. It's fine for bashing out 3-4 file programs. It's a decently high-entropy language. It's a bloody lousy hacking language because of the prescriptiveness. I'd rather use Common Lisp, and I do, for personal stuff." [49]
- "I've never seen such a leaky set of abstractions as Python's collection library. It's ridiculous how very often I find something irritating and the reason behind it is, "This was the first implementation and therefore was crowned 'the simplest.'" That seems to be the pattern in general with Python. A great example is Python's complex iteration system. You have 2 ways to deal with it; a sophisticated but very limited list comprehension syntax and a absolutely primitive direct object iterator that barely abstracts over a coroutine." [50]
- "Python is the "second best language for everything"" [51]
- http://norvig.com/python-lisp.html
- "Peter Norvig here. I came to Python not because I thought it was a better/acceptable/pragmatic Lisp, but because it was better pseudocode. Several students claimed that they had a hard time mapping from the pseudocode in my AI textbook to the Lisp code that Russell and I had online. So I looked for the language that was most like our pseudocode, and found that Python was the best match. Then I had to teach myself enough Python to implement the examples from the textbook. I found that Python was very nice for certain types of small problems, and had the libraries I needed to integrate with lots of other stuff, at Google and elsewhere on the net. I think Lisp still has an edge for larger projects and for applications where the speed of the compiled code is important. But Python has the edge (with a large number of students) when the main goal is communication, not programming per se. In terms of programming-in-the-large, at Google and elsewhere, I think that language choice is not as important as all the other choices: if you have the right overall architecture, the right team of programmers, the right development process that allows for rapid development with continuous improvement, then many languages will work for you; if you don't have those things you're in trouble regardless of your language choice." [52]