What is Pythonic?
What the heck does "pythonic" mean?
This was a question asked a few months ago, on, of all places, the
EuroPython mailing list, which is mainly used to plan the EuroPython
conference. It was an interesting question though. I realized I've
seen the word been used a lot, but that I've hardly seen any attempts
to explain what it means. In the thread that ensued, different people,
including myself, gave their own answers. I rewrote my answer for my
weblog, as it may benefit others.
"Pythonic" is a vague concept, but not necessarily that much more
vague than concepts like "intelligence" or "life", which, when you try to
actually define them, tend to be slippery. That they're hard to define
doesn't mean that they're useless though; humans work well with messy
definitions. "Pythonic" means something like "idiomatic Python", but
now we'll need to describe what that actually means.
Over time, as the Python language evolved and the community grew, a lot of ideas arose about how to use Python the right way. The Python language actively
encourages a large number of idioms to accomplish a number of tasks ("the one way to do it"). In turn, new idioms that evolved
in the Python community has have in turn influenced the evolution of
the language to support them better. The introduction of the dictionary .get() method, which combines in one operation what would be done with a combination of has_key() and item access before, can be considered an example of such an evolution.
Idioms are frequently not straightforwardly portable from another programming language. For example, the idiomatic way to perform an operation on all items in a list in C looks like this:
for (i=0; i < mylist_length; i++) {
do_something(mylist[i]);
}
The direct equivalent in Python would be this:
i = 0
while i < mylist_length:
do_something(mylist[i])
i += 1
That, however, while it works, is not considered Pythonic. It's not an
idiom the Python language encourages. We could improve it. A typical
idiom in Python to generate all numbers in a list would be to use
something like the built-in range() function:
for i in range(mylist_length):
do_something(mylist[i])
This is however not Pythonic either. Here is the Pythonic way,
encouraged by the language itself:
for element in mylist:
do_something(element)
A frequent question on comp.lang.python involves how to pass or modify
references directly, something that is not possible in Python; there's
just assignment (and its close relatives the import, class and
def statements). This is undoubtedly sometimes driven by the desire
to write code that returns multiple values from a function. The
idiomatic way to do this in C and a number of other languages is to
pass to this function pointers or references:
void foo(int* a, float* b) {
*a = 3;
*b = 5.5;
}
...
int alpha;
int beta;
foo(&alpha, &beta);
It's possible in Python to hack up strategies to pass function results
through arguments, such as like this:
def foo(a, b):
a[0] = 3
b[0] = 5.5
alpha = [0]
beta = [0]
foo(alpha, beta)
alpha = alpha[0]
beta = beta[0]
This is however considered to be screamingly unpythonic, as the
idiomatic way to return multiple values from a function is quite
different and looks much nicer. It exploits tuples and tuple
unpacking:
def foo():
return 3, 5.5
alpha, beta = foo()
Code that is not Pythonic tends to look odd or cumbersome to an
experienced Python programmer. It may also be overly verbose and
harder to understand, as instead of using a common, recognizable,
brief idiom, another, longer, sequence of code is used to accomplish
the desired effect. Since the language tends to support the right
idioms, non-idiomatic code frequently also executes more slowly.
To be Pythonic is to use the Python constructs and datastructures with
clean, readable idioms. It is Pythonic is to exploit dynamic typing
for instance, and it's definitely not Pythonic to introduce
static-type style verbosity into the picture where not needed. To be
Pythonic is to avoid surprising experienced Python programmers with
unfamiliar ways to accomplish a task.
The word "Pythonic" can also be applied beyond low-level idioms. For a
library or framework to be Pythonic is to make it as easy and natural
as possible for a Python programmer to pick up how to perform a task.
A library or framework, although written in Python, could be
considered unpythonic if it necessitated programmers using it to write
cumbersome or non-idiomatic Python code. Perhaps it's not using
constructs Python offers, such as classes, even though they would make
the library more convenient or easier to understand. Possibilities
like being allowed to pass functions and methods as arguments to
functions might be overlooked where they could be handy. A class
defined in a library might be trying to do its best to enforce
information hiding like you have in a language like Java, while Python
more operates under the looser strategy of 'advisory locking', where
attributes are typically available but the programmer is hinted about
their privacy by a leading underscore.
Of course, when you get to such a larger scale, to libraries and
frameworks, it gets more contentious whether something is Pythonic or
not. There are still some guidelines though. One is that of lesser
verbosity: APIs of Python libraries tend to be smaller and more
lightweight than those of Java libraries doing the same thing. Python
library which have a heavy-weight, overelaborate API are not
considered to be very "Pythonic". The W3C XML DOM API, for instance,
which has been implemented in Python quite a few times, is not
considered to be Pythonic. Some people think it's "Java-esque", though
from what I heard it's in fact not considered very Java-like either by
many Java programmers...
A Python-based framework can be considered Pythonic if it doesn't try
to reinvent the wheel too much where there already language idioms to
accomplish the same thing. It should also follow common Python
conventions concerning idioms.
Of course the problem is that frameworks, being frameworks, almost
inevitably try to introduce patterns and ways of doing things that
may not be familiar if you're used to smaller applications. That's how
you exploit the power of a framework. Zope 2, a framework I'm
intimately familiar with, is an example of a framework that definitely
introduces a lot of particular ways of doing things that you don't run
into so often elsewhere. Acquisition is an example. As a result, it's
not considered very Pythonic by many experienced Python programmers.
It's difficult to create a Pythonic framework. It isn't helped by that
the fact that the notion of what is cool, idiomatic, good Python code
has evolved quite significantly over the years. Features like
generators, sets, unicode strings and datetimes are now considered
Pythonic. Zope 2 is an example of a framework that definitely shows
its age there, and in part it cannot be blamed for it, as it was first
developed in 1997 or so. Considering that, it's holding up very well
indeed, thank you.
An example of a new trend in Pythonicness that I witnessed myself in
recent years is the movement towards standardizing idioms of package
and module structure in Python. Newer codebases like Twisted, Zope 3,
and PyPy all more or less follow this pattern:
package and modules names are brief, lowercase, and singular
packages are frequently namespace packages only, i.e. have empty
__init__.py files.
I've also tried to follow this convention in libraries I wrote, such
as lxml.
Sometimes I think the condemnation of software as 'unpythonic' may be
somewhat unfair and may obscure other positive aspects of the
software. A less powerful framework that is easy to pick up for a
Python programmer may be considered more Pythonic than a far more
powerful system that takes more of a time investment to learn.
Finally, for another, complementary perspective on what is Pythonic design, try the following in a python interpreter:
import this