3  Course tool reference

This course comes with a small family of notebook tools. Each of them exists because there is a question you will want to ask the machine, and asking it in ordinary Python either does not work or buries the answer. This page is the place to come back to when you have forgotten which tool answers which question. Every tool gets the same treatment: what it does, how you invoke it, and when you should reach for it.

Cell magics

Some of the tools are cell magics, which means you write the tool’s name on the very first line of a cell, starting with two percent signs. These magics are not part of the python code in the cell. Instead, think of magics as a part of the jupyter notebook interface. They are “magic” way to control change the notebook behavior for one particular cell. I made four different ones for this course that are meant to support your learning in various ways. They are all installed in the course environment but to be able to use them in a notebook, they must be imported at the top of the notebook. That is why you will see cells like one below at the top of some notebooks. Make sure you run this cell before running any cells with magics, otherwise they will not work.

import steps_widget
import puzzle_widget
import codelens_widget
import sandbox_widget
import snippet_cast

Seeing the substitution steps with %%steps

The course keeps telling you to do the substitution and reduction in your head. This is the tool that shows you the steps the machine actually took, so you can hold them next to the ones you took.

Put %%steps on the first line of a cell and write ordinary Python underneath. The code runs as usual, and below the output you get the evaluation laid out one reduction at a time: each variable replaced by its value, each operation replaced by its result, until a single value is left.

Predict what the cell below prints before you run it. Then run it and read the steps.

%%steps
x = 2
y = 3 * x + 1
print(y)
7

Reach for %%steps whenever an expression surprised you. Precedence questions, where you thought the multiplication happened after the addition. Comparison chains. Logical operators with and and or, where the short-circuit means part of the expression was never evaluated at all and the steps will show you that it was skipped. If you can predict the steps for an expression you have never seen, you can read Python.

The same machinery works outside a notebook. In a script you write, put a comment # PRINT STEPS on the line you want traced and run the file through the print-steps command in the terminal.

Exercise 3-1

SOLO

Write a %%steps cell containing result = 2 + 3 * 4 ** 2 and then print(result). Before running it, write down the order in which you think the three operations happen and what the final value is. Run it and compare. If you were wrong, you have just found something worth knowing.

Rebuilding a program with %%puzzle

Reading code and writing code are different skills, and there is a third one in between: knowing what order statements have to go in. That is what %%puzzle drills.

You write a small program in the cell and give the magic the value the program should end up producing. The widget shuffles the lines and presents them to you out of order, and you drag them back into an order that runs and gives the stated result. It is the same puzzle every time and it is never the same puzzle twice, because you can always shuffle again.

The result you give the magic has to be a plain Python value, a number, a string, a list, something you could type as a literal. The last line of your program has to be an expression on its own, because its value is what gets compared against the result you promised. And every line has to be a complete statement with no indentation, because the widget has to be able to shuffle them into any order without producing something that will not even parse.

%%puzzle 12
a = 3
b = 4
a * b

Reach for %%puzzle when you know what the pieces of a program mean but keep getting the order wrong, which is an extremely common place to be in the first weeks. Assignments before use, the loop before what it depends on, the function defined before it is called. Because the widget checks the result for you, you can work through these on your own without waiting for anyone to tell you whether you got it right.

Watching Python memory with %%codelens

%%steps shows you an expression collapsing. %%codelens shows you the whole machine: which variables exist right now, what each one refers to, which line is about to run, and what has been printed so far. You step forward and backward through the execution one line at a time.

Put %%codelens at the top of a cell and write the code underneath. Nothing runs when the cell executes in the ordinary sense; instead the code is traced, and the widget below lets you walk through the trace.

%%codelens
bases = ['A', 'T', 'G']
for base in bases:
    print(base)

The part of this tool you will care about most arrives later, when you meet lists and dictionaries. Values like numbers and strings are drawn inside the variable box. Lists, dictionaries, sets, function objects and instances of your own classes are drawn off to the side, with an arrow from the variable to the object. That picture is the answer to the single most confusing thing in the first half of the course: what happens when two variables refer to the same list, and why changing it through one of them changes it through the other.

Reach for %%codelens for anything involving a reference, a function call frame, a loop whose variable you have lost track of, or a nested structure you cannot picture. It shows execution and printed output only. It cannot show you what is in a file on disk or anything happening outside the code you gave it.

Exercise 3-2

AI: Explainer

Put these four lines in a %%codelens cell: a = [1, 2, 3], then b = a, then b.append(4), then print(a). Predict what the last line prints. Then ask the assistant to explain what happens when you write b = a with a list on the right-hand side. Finally step through the trace and decide whether the assistant’s account matches the arrows you can see. Note the outcome in your logbook.

%%codelens

A fresh interpreter with %%sandbox

A notebook remembers. Everything you have run is still sitting in the kernel’s memory, which is convenient right up until the moment it is the reason your code appears to work. A script has no such memory: it starts from nothing every time.

%%sandbox gives you a script inside your notebook. The cell body runs in a brand new Python interpreter of its own, which is thrown away as soon as it finishes. Nothing from the notebook is visible inside it, and nothing defined inside it survives into the notebook. Run the cell twice and you get exactly the same result both times, because the second run knows nothing about the first.

%%sandbox
codon = "ATG"
print(codon * 3)
ATGATGATG

Reach for %%sandbox when you want to know whether a piece of code stands on its own, which is the question that matters if it is eventually going into a .py file. It is also the safe place to run something that might fail: a sandbox cell reports the error in its own output box rather than raising it into your notebook, so a deliberate failure is contained.

Most of the small exercises in these notes are sandbox cells for exactly that reason.

Drawing with Turtle

The turtle is not a magic. It is an ordinary Python object you make and give instructions to, and it draws as it goes.

You make one with Turtle(), then tell it to move: forward, backward, left, right, goto, circle. You lift and lower the pen with penup and pendown, set its thickness with pensize, and set colors with pencolor and fillcolor. The drawing appears below the cell on its own, animated in the order the commands were given, so you do not need to display anything explicitly.

from turtle_widget import Turtle
t = Turtle()
t.speed(6)
for _ in range(4):
    t.forward(120)
    t.left(90)

The reason the turtle earns a place in a course about molecular biology is that its output is visible. When you write a loop that draws a square and get a triangle, you do not need a test to tell you something is wrong. That makes it the first safe place to let the assistant draft code for you, because you can judge the result at a glance, which is the whole idea you will spend the rest of the term applying to code whose output is not a picture.

Passing show_code=True when you make the turtle puts your source next to the canvas and highlights each line as it executes, which turns a drawing into a trace of your own loop.

Exercise 3-3

AI: Drafter

Ask the assistant for turtle code that draws an equilateral triangle. Before you run what it gives you, read it and predict what will appear. Then run it. If it drew a triangle, work out why the angle it used is the one that works. If it drew something else, work out where its reasoning went wrong. Either way you have verified rather than trusted, and either way it goes in the logbook.

Narrated code with %%snippet-cast

%%snippet-cast
#| echo: false
#| eval: true
                                                                                       #: 1) Let's write a function that adds one.            / 5) The temporary function context disappears, leaving only the returned value where the function was called.
def add_one(n):                                                                        #: 3) The first line "names" the function and define the parameter "n".            / 2) The function call passes the value 7 to the function parameter "n".
    x = n + 1                                                                          #: 4) The second line adds one.                       / 3) Define "x" as "n" plus one. "x" is a temporary function variable that lives only while the function runs.
    return x                                                                           #: 5) The last line returns the value of x.           / 4) The function returns the value pointed to by x.

assert add_one(7) == 8                                                                 #: 2) Begin by writing a test that calls the function / 1) We call the function.

Checking a project with im_pytest

From week six onward you work on weekly projects, and each project comes with a set of tests that decide whether your functions do what they are supposed to. The same tests appear to you in three progressively less friendly forms across the term, and that escalation is the point.

At first you call im_pytest.check("translationproject") in a cell, or put %%test on the first line of a cell with the project name after it. What comes back is a panel with one line per function, a check mark or a cross, the assertion that failed if one did, and a note about which functions you have not written yet. No test source and no traceback, because in week six you have not yet been told what a test is.

Later you run the same file with the real tool from the terminal, as pytest test_translationproject.py, and learn to read what pytest actually prints. Same tests, same results, no cushioning.

Later still you write the tests yourself, using the provided ones as your model. This is where the course arrives at the thing it has been aiming at all along: a test you wrote is how you find out whether code somebody else produced, including an assistant, does what you asked for. A function that passes a test you designed is checked. A function that merely looks right is not.

Exploring visuals with iplot

In the last weeks, when you are working with real data tables rather than lists you typed yourself, iplot gives you a plot with dropdown menus. You hand it a dataframe and pick which column goes on which axis, what splits the colors, and what kind of plot you want, without writing new plotting code for every view you want to look at. It is a way of looking around a dataset quickly, before you commit to the one figure you actually want to make.

from iplot_widget import iplot
import seaborn as sns
data = sns.load_dataset("penguins")
data.head()
iplot(data)

Exercise 3-4

SOLO

For each of these situations, name the tool you would reach for, without looking back up the page. Your function returns None and you do not know why. You wrote x = 5 and y = x * 2 + 1 and cannot work out why y is not what you expected. You copied a working cell into a new file and it stopped working. You changed a list through one variable and a different variable changed too. Then check yourself against the section above.