Advanced Python Constructs

by Szymon LipiƄski

A simple description of a couple of more advanced, not wildly used, Python constructs like list comprehension, dictionary comprehension, map, and filter.

All examples are using Python 3.5.

List Comprehension

This is a very nice way to create a list. Much shorter, more clear, unless you don’t know the syntax. This can be used instead of creating a list with a multilevel for loop.

Let’s get a list of 100 numbers from 1 to 100. For all smaller than 11 let’s get the value squared. So basically I need to get a list like:

[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

The simple way of implementing such a thing would be the following code:

res = []
for i in range(1,101):
    if i < 11:
        res.append(i*i)

However, using a list comprehension it can be:

[i*i for i in range(1, 101) if i < 11]

Let’s read it from the middle:

So basically, there is a generator which returns one value at a time, which is stored in the i variable, then the if part is applied, if returns True, then the i*i is stored in the list, if returns False, then we take the next value from the generator.

Dictionary Comprehension

This is a similar construct to the above one, but it creates a dictionary. The syntax is similar, the only difference is: different brackets and the key:value part instead of a single value.

Let’s create a dictionary using the previous list comprehension. The key would be the number, the value would be the number squared:

{i:i*i for i in range(1, 101) if i < 11}

the returned value is:

{1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100}

As you can see there are just two differences:

Using Filter

In Python there is a couple of functions widely used in functional languages. One of them is the filter function. The basic syntax is:

filter(function, iterable)

This function takes two objects as arguments: a function and an object which we can iterate on. It returns a generator object which returns values from the iterable argument, for which the function returns True.

Let’s now use it for filtering the values 1..100 from the generator to get the first 10:

f = filter(lambda x: x < 11, range(1, 101))
list(f)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

The above code can be also written like this:

def check_smaller_than_11(x):
    return x < 11

f = filter(check_smaller_than_11, range(1, 100))
[x for x in f]

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Using Map

The map function is similar to the filter one. The arguments are the same:

map(function, iterable)

This function returns a generator, which returns exactly the same number of values as were in the the iterable object. The returned collection values are function(value).

So if we have a list: [1, 2, 3] and a function fun, then map(fun, [1, 2, 3]) returns a generator, which returns the values: [fun(1), fun(2), fun(3)].

Let’s make the same list as in the list comprehension example. Instead of this:

[i*i for i in range(1, 101) if i < 11]

It can also be created with the map and the filter:

m = map(lambda x: x*x, filter(lambda x: x<11, range(1, 101)))
list(m)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

So basically:

Final Notes

Which solution is better? I don’t know. Pyflakes is not happy about map/filter saying This could be changed to list comprehension.

One of the huge cons of using lambdas in the following examples is that lambda can be just a one liner in Python. You can always write a separate function and just use it instead of the lambda. Functions in Python are objects, so you can just pass them to another function.

On the other hand if you need to use the same condition in a couple of list comprehensions, it is always better to move them into one separate function, so the code won’t be duplicated.

This function then can be used in a list comprehension like:

def fun(x):
    return True if x < 1 else return False

[x for x in gen if fun(x)]

but you can also use the same function in filter:

filter(fun, gen)

As usually all the solutions have pros and cons, the real life choice usually depends on many different aspects.