5 things I love about Python

I like coding in Python, though I don’t really do too much of it. Last year I did some of Advent of Code 2020, which was challenging but interesting and rewarding. I’ve been meaning to write down a few of the things that I like about Python, with some examples from Advent of Code. Some of these are older features; some of them are newer — but they’re all useful!

List comprehension

introduced in Python 2.0, 2000

A great way to let you not have to complete a for loop syntax. In Day 7, I did this, which is a great example of how a list comprehension can be used — in this case, to apply a type cast to each item in a list.

ints = [int(x) for x in nums]

The list comprehension also comes with friends, like the set comprehension (introduced in Python 2.7, 2010), which I used in day 6. Here, I’m filtering out some of the data and creating a set in a single, simple expression:

{x for x in g if x != "\n"}

Also useful is the dict comprehension (introduced in Python 2.7, 2010), which I used in day 10 to create a dictionary with 0 values for each of the values in the list. I was then able to modify these values later easily as I went through some logic.

paths_to = {k: 0 for k in nums}

Finally, the generator comprehension (introduced in Python 2.4, 2004). This uses the powerful generator functionality of Python, which is sort of like a list, but doesn’t calculate all the values along the way. This helps speed up your code, and is handy with the inbuilt functions any() and all() , as in the example below, from Day 21:

any(len(v) > 1 for v in allergens_to_ingredients.values())

The Python interpreter won’t calculate len(v) for every value of v — once it finds one value which is above 1, it’ll stop calculating and just return True. If we’d used a list comprehension, Python would have calculated all the values and then checked them.

It’s worth giving an honourable mention here to any() and all() — they’re great little functions when you just need to know if anything in an iterable is truthy.

Walrus operator

introduced in Python 3.8, 2019

This is a much more recent addition to the language. Like most changes to a language with Python’s maturity, it’s been a bit controversial, but I think this is a really helpful addition to the language — and I’ve missed it when writing code to run on 3.6 or 3.7. See this instance, from Day 14:

if (m := mask[i]) == "X":
result.append(num)
else:
result.append(m)

Without the walrus operator, I’d probably have done this:

if mask[i] == "X":
result.append(num)
else:
result.append(mask[i])

which I think doesn’t make it as clear — and if I changed one of those instances of mask[i] — say to get_mask(i), there’s a risk that I’d forget to change the other instance. The other old way of doing it would be something like:

m = mask[i]
if m == "X":
result.append(num)
else:
result.append(m)

but I think the walrus operator is much more elegant.

Reduce

reduce() is a nice function which calls a function multiple times on a list. reduce(f, [a, b, c, d, e]) is equivalent to f(f(f(f(a, b), c), d), e) — effectively, reduce() is calling the function on the first pair of elements, and then calling it on the result of that and the next element. This can sound a bit confusing, and it definitely took me a while for me to get my head round it, but let’s look at an example from Day 16:

reduce(int.__mul__, your_ticket_values)

If your_ticket_values was [1, 2, 3, 4], this expression would compute ((1*2)*3)*4. Since int multiplication is associative, this can equivalently be written as 1*2*3*4, but reduce works for non-associative functions too — you just need to be aware that it works left-to-right.

Chained comparison operators

Comparison, like iteration is just one of those really common programming tools, that just comes up again and again. Python has a handy trick for this too, called chained comparison — see Day 2.

if int(fr) <= password.count(letter) <= int(to):

This is equivalent to:

if (int(fr) <= password.count(letter)) and (password.count(letter) <= int(to)):

which I think you’ll agree is much less pretty!

Tuple assignment

Multiple assignment in Python is pretty well known — commonly discussed in the use case of ‘swapping’ variables, such as:

a, b = b, a

rather than

tmp = b
b = a
a = tmp

which would be required in other languages such as C. But there are some additional tricks which you can use in Python. I’ll give an abstract example first:

>>>foo = [2, 3, 4, 5, 6]
>>> first, *middle, last = foo
>>> first
2
>>> middle
[3, 4, 5]
>>> last
6

This syntax can be used to separate first and last items from an iterable, and dump the rest into a list. It’s pretty neat in many situations, and in my opinion is much more elegant than slicing the list. The asterisk is basically doing the work of ‘collapsing’ all those items in the middle into one variable. Here’s my example from Day 7:

i_number, *i_colour, _ = inner_bag.split()

I hope you found some of this interesting or useful!