Five things I hate about Python
Inspired by Titus (who was in turn inspired by brian d foy), here’s what I hate about Python. I completely agree with Brian that you can’t trust any advocate who doesn’t know enough to find stuff to hate. Given that I spend a lot of time advocating Python, writing down what I hate seems a good exercise. Perhaps I’ll do the same for Django in the future…
Anyway, here are the five things I hate about Python, presented Letterman-style:
5. Where are my interfaces?
I’m pretty convinced that every piece of software has at least one good idea. Even the steaming hunk of crap that we call “Java” has one: interfaces.
Now, I’m pretty convinced that Python strikes a great balance between expressiveness and minimalism; I can do nearly everything I want with the small set of built-in types. However, the one thing I can’t effectively do is codify a set of behaviors into an interface. There are a number of third-party libraries that implement interfaces (PyProtocols, zope.interface), but they feel “clunky” compared to what first-class interfaces would solve. It seems to me that interfaces are a perfect match to duck typing; I shouldn’t need to care care about the difference between something that pretends to be a list and something that really is a list.
4. What the hell is a list, anyway?
I think this next one follows naturally out of the above. Many, many
functions are documented as operating on “list-like” or “file-like” or
“dict-like” objects, but there’s a general lack of agreement about what
exactly constitutes a minimal interface for any of these objects. Is
something that supports read()
and write()
enough to be a “file,” or
is seek()
a necessity, too?
Does a list just need to implement __getitem__
, or is __len__
required? I could go on and on.
This has real – and sometimes nasty – repercussions: the
HttpResponse
object in Django implements (our idea of) a minimal file
interface, and so much of the time you can pass one in anywhere you’d
normally use a file. However, some libraries with different ideas of
“file-ness” will break when handed an HttpResponse
(for a while
reportlab
wouldn’t handle an HttpResponse
; I forget what we had to
add to make that work).
Python 3000 supposedly will solve this with the use of ABCs (Abstract Base Classes); that’s OK, but I’d still rather see interfaces.
3. Concurrency is is really really hard hard
Concurrency in Python sucks balls.
Threading is slow because of the GIL and hard because the built-in
threading
module is rudimentary (at best).
Coroutines/microthreads are only supported if you want to experiment with Stackless.
If you want asyncronous IO you’ve basically got two shitty options: the standard library asyncore module (which is far too low-level and rudimentary to be considered “easy”), and Twisted which is huge and chronically under-documented.
2. Egg on my face
OK, they’re only really a de facto part of Python, but I’m still gonna bitch about Eggs: I freeking hate them.
Sure, easy_install Something
is quite awesome, but I couldn’t be more
unhappy about how that actually works. I could bitch for ages about ’em,
but I’ll just point out the two big things I can’t stand:
First, Have you ever looked at sys.path
with a couple of eggs
installed? Here’s a bit of mine:
...
'.../site-packages/setuptools-0.6c3-py2.4.egg',
'.../site-packages/mechanize-0.1.6b.dev_r36082-py2.4.egg',
'.../site-packages/ClientForm-0.2.6.dev_r36070-py2.4.egg',
'.../site-packages/geopy-0.93-py2.4.egg',
'.../site-packages/kid-0.9.4-py2.4.egg',
'.../site-packages/elementtree-1.2.6_20050316-py2.4.egg',
'.../site-packages/Unipath-0.1.0-py2.4.egg',
'.../site-packages/PyYAML-3.04-py2.4.egg',
'.../site-packages/simplejson-1.5-py2.4.egg',
'.../site-packages/Paste-1.2.1-py2.4.egg',
'.../site-packages/selector-0.8.11-py2.4.egg',
'.../site-packages/resolver-0.2-py2.4.egg',
'.../site-packages/wsgiref-0.1.2-py2.4.egg',
'.../site-packages/svnshelve-0.1-py2.4.egg',
'.../site-packages/paramiko-1.7-py2.4.egg',
'.../site-packages/pycrypto-2.0.1-py2.4-macosx-10.3-fat.egg',
...
That’s right: each egg gets its own entry on sys.path
. This is bad
because of the way Python’s import
mechanism works; importing a
package makes around ten different open
syscalls for each entry on
sys.path
; that is, import foo
looks for:
foo.so
foomodule.so
foo.py
foo.pyc
foo.pyo
foo/__init__.so
foo/__init__module.so
foo/__init__.py
foo/__init__.pyc
foo/__init__.pyo
Multiply that by 10 or 20 or however many eggs you’ve got and you’re
actually talking serious import
overhead here.
Second – and more distressing to me – is the fact that eggs get in the way of viewing source. Packages installed from eggs are installed as ZIP files; this means that viewing their source requires unpacking the egg. Yes, this is a bit trivial, but newbies to Python aren’t even going to know how to do this! I’ve talked to a number of first-time Python people who assume that CheeseShop packages are closed-source in some way.
You can fix this, by the way, with these lines in ~/.pydistutils.cfg
:
[easy_install]
zip_ok = false
But that’s a hack, really.
1. Whose standards are these, anyway?
Finally, we come to the standard library.
I should say off the top that for many, many uses the standard library is awesome. The concept of “batteries included” is fantastic, and the particular batteries that Python includes are for the most part amazing. I feel comfortable critcizing the standard library precisely because it’s so good – with a minimal amount of work, it could be even better.
Titus covered the problems of inconsistancy and spotty documentation pretty well, so I’ll focus on my specific concern: what exactly is considered “standard”, anyway?
There’s a whole set of libraries out there that I basically consider required to get any real work done, but aren’t included in the standard library. A (partial) list would include:
Python would be about 100% more useful if the standard library included more packages that people actually consider “standard”.
Update:
Fixed the spelling of brian d foy’s name. Sorry!