At the next Python Frederick meetup, I'm going to speak about "Pythonic" code. Pythonic code is code that fits well with the design of the Python language. Some people might call this idiomatic Python. These design patterns give Python some of its elegant feeling. Since I'm going to do an entire talk on Pythonic code, I've decided to put together a series of posts that will explore some of the patterns in greater depth.
To start, let's talk about list comprehensions.
List comprehensions
A list is one of Python's core data structures. Along with its buddies, dictionaries and tuples, you can get a ton of amazing stuff done with the language.
# A list example
some_numbers = [1, 2, 3, 4, 5]
You can learn a lot about lists by reading the Python tutorial on data structures, but this post will cover one of the list's more iconic features, list comprehensions.
If you are working with a list of data, you may eventually want to modify it. Let's work with some numbers. What would you do if you want to double your values?
Here's a first attempt:
numbers = [1, 2, 3, 4, 5]
doubled = []
for number in numbers:
doubled.append(2 * number)
This works, but it manages to feel a bit clunky. A list comprehension is a way to build a new list with a more compact syntax.
Let's make our doubled list with a list comprehension.
numbers = [1, 2, 3, 4, 5]
doubled = [2 * number for number in numbers]
With a list comprehension, the for
loop has moved inside the list's constructor syntax (i.e., []
). We saved two lines of code and avoided the explicit use of append
. I think we can confidently say that the code is cleaner.
If that's all that list comprehensions could do, it would a nice little syntax addition, but might feel like a parlor trick. Thankfully, list comprehensions can do more. They can also filter if they are provided with a predicate. For instance, maybe you want odd numbers instead of doubled values. For that, you'd write:
numbers = range(10)
odds = [number for number in numbers if number % 2 != 0]
This example uses range
to create a list of numbers from 0
to 9
. The list comprehension builds the odds
list and only includes number
when it is odd (number % 2 != 0
checks that a number is odd).
One more feature of list comprehensions that is probably less used is the ability to use multiple for
loops.
Imagine that you need a list of coordinate pairs in an x/y plane. Instead of writing nested for
loops, like so:
xs = range(10)
ys = range(10)
pairs = []
for x in xs:
for y in ys:
pairs.append((x, y))
You can write the more concise version.
xs = range(10)
ys = range(10)
pairs = [(x, y) for x in xs
for y in ys]
We've covered a few key attributes list comprehensions.
- They're a concise way to build new lists.
- They enable elegant filtering.
- They permit nested looping in a clear style.
List comprehensions are a fantastic way to clean up your loops. Once you start using them, you'll be on your way to writing more Pythonic code.
Top comments (3)
Thanks for the post!
I think "pythonic" is such an important yet hard to grasp concept.
Quick question: how does the following compare with your last example in terms of trying to be more pythonic?
Yes, the last example was a bit contrived to illustrate the possibility of nested comprehensions. I'm a big fan of knowing built-in function so I think using
zip
is a perfectly reasonable and Pythonic alternative.Yep, list comprehensions are a very core concept, and there are some sneaky corners in there that not everyone is aware of. I'm glad the article was able to show you one of those corners!