These are sample chapters from the book Python Brain Teasers: 30 brain teasers to tickle your mind make you a better developer. by Miki Tebeka.

Buy the book at Gumroad (ePub & PDF)

Foreword by Raymond Hettinger

In my Python conference talks, I frequently check in with the audience to ask, "Who learned something new?". Getting a "yes" over and over again fills everyone with delight and tells us that our time is being well spent. Miki’s collection of brain teasers will give you that immediate gratification, once per puzzle. Expect to have a lot fun with his stream of "Aha!" moments.

Miki and I have worked together three times, once in a trading company, once at a web services company, and again as Python trainers. Working with him always gives you that "I learned something new" experience.

As trainers, we’ve that found that a key skill is the ability to read code and to know, really know, what it does. With Miki’s well chosen examples, you can rapidly learn this essential skill. He gives you an interesting code fragment, asks you to make a prediction, and then gently explains the outcome. As icing on the cake, he also provide links to authoritative references to deepen your knowledge.

Python is not a difficult language, but there is much more to it than meets the eye. It is easy to assume you know the language well when you really don’t. The Dunning-Kruger effect is pervasive in the Python world. Miki’s brain teasers will help you quickly discover what you don’t know, and his explanations will fill-in the missing knowledge to build your expertise.

Here’s an example that I’ve asked during interviews. What does this code do?

for i in range(10):
    print(i)
    i = 5
print(i)

The answer quickly reveals whether someone understands iterators and scoping in Python. Miki’s book is full of such gems.

Hope you enjoy the ride,
Raymond Hettinger
Python Core Developer with a PSF Distinguished Service Award

The Brain Teasers

Eduction is not the learning of facts, but the training of the mind to think.
— Albert Einstein

1. A Task to Do

tasks.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from heapq import heappush, heappop

# Tasks priority queue
# A task is a tuple of (priority, payload)
tasks = []
heappush(tasks, (30, 'work out'))
heappush(tasks, (10, 'wake up'))
heappush(tasks, (20, 0xCAFFE))
heappush(tasks, (20, 'feed cat'))
heappush(tasks, (40, 'write book'))

while tasks:
    _, payload = heappop(tasks)
    print(payload)
Try to guess what the output is before moving to the next page.

This code will raise a TypeError exception.

The built-in heapq module implements min-heap over lists.

It’s common to use a heap for a priority queue. Pushing and deleting form the heap are log(N) operations and the first item in the heap (e.g. tasks[0]) is always the smallest.

To compare items in the heap, heapq uses the comparison defined it the object’s type (using the < operator which maps to the specific type’s __lt__ special method). The objects in the tasks heap are tuples. Python orders tuples in a lexicographical order - very much like books are ordered in the library. [1] Lexicographical order compares the first two items, then the second two …​ finally if all items equal, the longer tuple is considered bigger.

In line 13, you pop the first item from tasks, which is (10, 'wake up'). After this item is removed from the heap, heapq will move the smallest item to the top of the heap. There are two candidates (20, 'feed cat') and (20, 0xCAFFE), [2] since the first items in these tuples are equal - Python will try to compare the second items. Comparing 'feed cat' (a str) with 0xCAFFE (an int) - will raise an exception.[3]

1.1. Further Reading

2. Will It Fit?

assign.py
1
2
3
a = [1, 2, 3, 4]
a[1:2] = [10, 20, 30]
print(a)
Try to guess what the output is before moving to the next page.

This code will print: [1, 10, 20, 30, 3, 4]

Python’s slicing operator is half-open [4], meaning you’ll get from the first index, up to but not including the last index. a[1:2] is in size 1, yet we assign a list of size 3 to it.

The assignment documentation is a bit hard to read (see below if you’re interested). Here’s an excerpt (my clipping and emphasis):

If the target is a slicing: …​ Finally, the sequence object is asked to replace the slice with the items of the assigned sequence. The length of the slice may be different from the length of the assigned sequence,…​

In short, when you write a[1:2] = [10, 20, 30] it’s like writing a = a[:1] + [10, 20, 30] + a[2:].

2.1. Further Reading


1. This is also how strings and lists are ordered.
2. 0xCAFFE is a hexadecimal (base 16) number. Writing "English" this way is called Leet.
3. In Python 3, Python 2 will happily compare str and an int.
4. [) in math.