Write More Pythonic Code by Applying the Things You Already Know
There’s a mistake I frequently make when I learn new things about Python… Here’s how you can avoid this pitfall and learn something about Python’s “enumerate()” function at the same time.
When I learn a new trick for my “Python coding toolbox” I often sense some benefit right away.
It’s like I know this thing is useful for something—
And yet I’m sitting here banging my head against the table trying to find a practical application for it.
How do you take a new function or module you heard about, and turn it into a sprinkling of Pythonic fairy dust that gets you a “ooh, nice!” comment in your next code review?
The other day I got this question from newsletter reader Paul, in response to a piece I wrote about Python’s enumerate()
function:
Yesterday I needed to write a dictionary that reversed the enumeration (so,
{'Bob': 0}
, etc). I used the length of the list plus zip in a dictionary comprehension.Is there a more Pythonic way to do this?
To give you some more context, this is what Paul wants to do:
input = ['Duration', 'F0', 'F1', 'F2', 'F3'] output = {'Duration': 0, 'F0': 1, 'F1': 2, 'F2': 3, 'F3': 4}
The goal is to create a dictionary that maps each item in the input list to the item’s index in that very list. This dictionary can then be used to look up indices using items as keys.
Here’s how he implemented this transformation:
>>> {f:i for f, i in zip(input, range(len(input)))} {'Duration': 0, 'F0': 1, 'F1': 2, 'F2': 3, 'F3': 4}
So far so good—but as Paul suspects, we can clean this up some more.
This is exactly the kind of situation I find myself in all the time. Paul knows intuitively that there’s a way to make his code more Pythonic with the enumerate()
built-in…
But how should he put it in practice?
My first thought was that we could shorten this code a bit by avoiding the dict comprehension:
>>> dict(zip(input, range(len(input)))) {'Duration': 0, 'F0': 1, 'F1': 2, 'F2': 3, 'F3': 4}
That’s slightly cleaner (because it has less visual noise), but just like Paul I’m still not very fond of that range(len(...))
construct.
Let’s try playing with enumerate()
:
>>> list(enumerate(input)) [(0, 'Duration'), (1, 'F0'), (2, 'F1'), (3, 'F2'), (4, 'F3')]
Okay, so I can use enumerate to pair each input key with its index in the list. Let’s turn that into a dictionary:
>>> dict(enumerate(input)) {0: 'Duration', 1: 'F0', 2: 'F1', 3: 'F2', 4: 'F3'}
We’re so close! This is basically what we want, but “in the wrong direction.” Instead of mapping keys to indices it’s mapping the index to the key.
How can we reverse it?
Let’s bring back the dict comprehension:
>>> {f: i for i, f in enumerate(input)} {'Duration': 0, 'F0': 1, 'F1': 2, 'F2': 3, 'F3': 4}
And there you go, that’s it! It’s a beauty!
Now, what’s the takeaway from all of this?
With this sort of thing, it often pays off to go with your gut.
You see, Paul was right all along. There really was a way this code could be cleaned up by using enumerate(). It was just a little unclear of how it would work specifically.
So, when you find yourself in the same situation, keep digging!
Python is an excellent programming language to do this sort of hands-on experimentation with: When I sat down to reply to Paul’s email, the first thing I did was to fire up a Python interpreter session for some explorative code golf.
You can’t really do this with a compiled language like C++. And it’s one of Python’s great features that you should use to your advantage.
That “ooh, nice!” code review comment is waiting for you.