Jupyter Notebooks and Python Basics

Welcome to Computational Science, welcome to Python programming! In this course, you will implement various basic programming techniques in Python and apply them to solving physics problems.

In this course, you will use Python using a platform called "Jupyter Notebooks". Jupyter notebooks are a way to combine formatted text (like the text you are reading now), Python code (which you will use below), and the result of your code and calculations all in one place. Go through the notebooks and run the examples. Try to understand their functioning and, if necessary, add code (such as print statements or variable overviews) to make it clear for you. In addition, there are exercises and practice cells where you can program for yourself. Don't be afraid to start coding yourself, writing code and making mistakes is the best way to learn Python.

In this lecture, you will learn the basic concepts of programming in Python. To get started, we will first explain the basic concepts of what python is and how it works.

Learning objectives for this notebook:

  • Student is able to start the python interpreter and run code in a notebook
  • Student can stop and start notebook kernels
  • Student is able to create variables
  • Student can use %whos to list the variables stored in the memory of the python kernel
  • Student is able to determine the type of a variable
  • Student is able to convert between different variable types (float, int, etc)
  • Student is able to collect user input using the input() command
  • Student is able to print variable values using the print() command

What is Python? And what are Jupyter Notebooks?

Python is an (interpreted) computer programming language. Using python, you can ask your computer to do specific things, like perform a calculation, draw a graph, load data from a file, or interact with the user.

Every time you run Python, either by running it on the command line, or by running it in a Jupyter notebook like you are now, a Python "kernel" is created. This kernel is a copy of the Python program ("interpreter") that runs continuously on your computer (until you stop it).

Jupyter notebooks are a way to interact with the Python kernel. Notebooks are divided up into "cells", which can be either a text (markdown format) cell, like this one, or a code cell (containing your code), like the cell below it.

The selected cell is surrounded by a box. If you type "enter" in a text cell you can start editing the cell. If you push "Run" above, or type "Shift-Enter", the code will be "run". If it is a code cell, it will run a command (see below). If it is a markdown cell, it will "compile" the markdown text language into formatted (HTML) text.

You can give commands to this kernel by typing commands using the python language into the code cells of the notebook. Here, you can find an example of a code cell that contains a simple python command print, which prints a text string to the command line.

In [ ]:
print("I'")

To send this command to the python kernel, there are several options. First, select the cell (so that it is either blue or green), and then:

  1. Click on the Run button above in the toolbar. This will execute the cell and move you to the next cell.
  2. Push Shift-Enter: this will do the same thing
  3. Push Control-Enter: this will run the cell but leave it selected (useful if you want to re-run a cell a bunch of times)

When you run the cell, the code will be sent to the python kernel, which will translate your python command into a binary language your computer CPU understands, send it to the CPU, and read back the answer. If the code you run produces an "output", meaning that the kernel will send something back to you, then the output that your code produces will be displayed below the code cell in the "output" section of the code cell. This is a scheme of what this process looks like "behind the scenes":

After you have run the code cell, a number will appear beside your code cell. This number tell you in which order that piece of code was sent to the kernel. Because the kernel has a "memory", as you will see in the next section, this number can be useful so that you remember in which order the code cells in your notebook were executed.

In the example above, the code cell contained only a single line of code, but if you want, you can include as many lines as you want in your code cell and add comments by starting a line with #. It is good programming practice to use comments to explain what the code is doing, see the example below:

In [ ]:
# This will print out a message
print("Hello")
print("world")
print("Goodbye")

Exercise 1 Print your own string to the command line. Can you use special characters as well? Try it.

In [ ]:
# Your code here

The Python kernel has a memory

In addition to asking Python to do things for you, like the "Hello world" example above, you can also have Python remember things for you. To do this, you can assign the value of 5 to the variable a using:

In [ ]:
a = 5

If variable a already exists, it will be over-written with the new value (in fact, a is a Python object, something that we will explain in the optional notebook in more detail). If variable a does not yet exist, then Python will create a new variable for you automatically.

For you, the cell above will create a "variable" named a in memory of the Python kernel that has the value of 5. We can check this by printing the value of a:

In [ ]:
print(a)

Note that we can also add a message to this by combining this with a message with the print() statement by combining things with commas:

In [ ]:
print("The value of a is",a)

Exercise 2 Combine a string, a variable, and a numerical values in a single print statement using the , separator.

In [ ]:
# Your code here

Exercise 3 Change the value of a to 7 by executing the following cell, and then re-run the above cell containing the command print(a) (the one with output 5). What value gets printed now in that cell?

In [ ]:
a = 

As you can see in notebooks that the location of your code doesn’t matter, but the order in which you execute them does!!

Sometimes, if you execute a lot of cells, or maybe even re-execute a cell after changing its contents, you might lose track of what variables are defined in the memory of your Python kernel. For this, there is a convenient built-in "magic" command called %whos that can list for you all the variables that have been defined in your kernel, along with their values:

In [ ]:
a=5 
%whos

(Some notes about %whos: %whos is not a "native" command of the Python language, but instead a "built-in" command that has been added by the creators of Jupyter. Because of this, you cannot use it outside of Jupyter / iPython...)

If we define some new variables, they will also appear in the list of defined variables if you execute %whos:

In [ ]:
c = 10
d = 15.5
In [ ]:
%whos

In this case the variable named is displayed, its value, but also its type. Type defines the format in which a variable is stored in memory. In this case int stands for integer and float stands for floating point number, which is the usual way in which real numbers are stored in a computer. We will learn more about Python variable types below.

If you want to be sure that your code runs without being based on the exact history of your commands you use "Kernel/Restart and run all". In this case the entire notebook is runned from top to bottom. This is essential, e.g. when handing in an assignment, that is run on a different computer to obtain the same results.

Starting and stopping the kernel

When you open a notebook for the first time, a new kernel will be started for you, which will have nothing in your memory.

Important to understand: if you close the tab of your browser that has the notebook, Jupyter will not shut down the kernel! It will leave the kernel running and when you re-open the notebook in your browser, all the variables you defined will still be in the memory. You can test this by closing this notebook right now, clicking on the link to open it in the "tree" file browser tab of Jupyter, and then re-running the cell above with the command %whos.

How do I shutdown a kernel? And how do I know if a notebook on my computer already has a kernel running?

  • First, as you may have noticed, when you closed this notebook and went back to the "tree" file brower, the notebook icon had turned green. This is one way that Jupyter tells you that a notebook file has a running kernel.

  • Second: in the "tree" view of the Jupyter interface, there is a link at the top to a tab "Running" that will show you all the running kernels and allow you to stop them manually.

Sometimes, you may want to restart the kernel of a notebook you are working on. You may want to do this to clear all the variables and run all your code again from a "fresh start" (which you should always do before submitting an assignment!). You may also need to do this if your kernel crashes (the "status" of your kernel can be seen in the icons at the right-hand side of the Jupyter menu bar at the top of the screen).

For this, there is both a menubar "Kernel" at the top, along with two useful buttons in the toolbar:

  • "Stop": tells the kernel to abort trying to run the code it is working on, but does not erase its memory
  • "Restart": "kill" the kernel (erasing its memory), and start a new one attached to the notebook.

To see this in action, you can execute the following cell, which will do nothing other than wait for 10 minutes:

In [ ]:
from time import sleep
sleep(10*60)

You will notice that while a cell is running, the text beside it shows In [*]:. The * indicates that the cell is being executed, and will change to a number when the cell is finished. You will also see that the small circle beside the Python 3 text on the right side of the Jupyter menu bar at the top of the page will become solid. Unless you have a lot of patience, you should probably stop the kernel, using the "Stop" button, or the menu item "Kernel / Interrupt".

Exercise 4 List the stored variables using the %whos command. Subsequently, restart the kernel. What variables are stored in the memory of the kernel before and after the restart?

In [ ]:
# Your code here

Python variable types

As we saw above, in Python, variable have a property that is called their "type". When you use the assignment operator = to assign a value to a variable, Python will automatically pick a variable type it thinks fits best, even changing the type of an existing variable if it thinks it is a good idea.

You have, in fact, already seen information about the types of variables in the %whos command again:

In [ ]:
%whos

In the second column, you can see the type that python chose for the variables we created. int corresponds to integer numbers, float corresponds to floating-point numbers. You can see that for variable c, python had to choose a float type (because 15.5 is not an integer), but for a and b, it chose integer types.

(In general, Python tries to choose a variable type that makes calculations the fastest and uses as little memory as possible.)

If you assign a new value to a variable, it can change the variables type:

In [ ]:
a = a/2
In [ ]:
%whos

Because 5/2 = 2.5, Python decided to change the type of variable a from int to float after the assignment operation a = a/2.

When you are using floating point numbers, you can also use an "exponential" notation to specify very big or very small numbers:

In [ ]:
c = 1.5e-8

The notation 1.5e-8 is a notation used in Python to indicate the number $1.5 \times 10^{-8}$.

A third type of mathematical variable type that you may use in physics is a complex number:

https://en.wikipedia.org/wiki/Complex_number

In python, you can indicate a complex number by using 1j, which is the python notation for the complex number $i$:

In [ ]:
d = 1+1j
In [ ]:
%whos

The notation 1j is special, in particular because there is no space between the number 1 and the j. This is how Python knows that you are telling it to make a complex number (and not just referring to a variable named j...). The number in front of the j can be any floating point number: for example,

In [ ]:
0.5*d

In addition to the mathematical variable types listed above, there are also other types of variables in Python. A common one you may encounter is the "string" variable type str, which is used for pieces of text. To tell Python you want to make a string, you enclose the text of your string in either single forward quotes ' or double forward quotes ":

In [ ]:
e = "This is a string"
f = 'This is also a string'
In [ ]:
%whos

You can also make multiline strings using three single quotes:

In [ ]:
multi = \
'''
This string
has 
multiple lines.
'''
print(multi)

Note here that I have used a backslash: this a way to split Python code across multiple lines.

Although it's not obvious, Python can also do "operations" on strings, the + mathematical opeartors we saw above also works with strings.

Exercise 5 Discover what the + operator does to a string, i.e. print the output of the sum of two strings.

In [ ]:
# Your code here

There is one more useful variable type we will introduce here: the "boolean" type bool. Boolean variable can have two values: True and False. You type them in directly as True and False with no quotes (you will see them turn green).

In [ ]:
g = False
In [ ]:
%whos

We will use boolean types much more later when we look at program control flow, but a simple example is the if statement:

In [ ]:
if True:
    print("True is always true.")

if g:
    print("g is true!")
    
if not g:
    print("g is not true!")

You can try changing the value of g above to False and see what happens if you run the above code cell again.

Also, useful to know: numbers (both int and float) can also be used in True / False statements! Python will interpret any number that is not zero as True and any number that is zero as False.

Exercise 6 Discover which numbers can be used as True and False in Python by changing the value of g above and re-running the cells.

In [ ]:
# Your code here

Converting variables between different types

We can also convert a value from one type to another by using functions with the same name as the type that we want to convert them to. Some examples:

In [ ]:
float(5)
In [ ]:
int(7.63)

Note that when converting an float to an int, Python does not round off the value, but instead drops all the numbers off after the decimal point (it "trucates" it). If we want to convert to an integer and round it off, we can use the round() function:

In [ ]:
b = round(7.63)
print(b)
In [ ]:
type(b)

This works for conversions between many types. Sometimes, you have will lose information in this process: for example, converting a float to an int, we lose all the numbers after the decimal point. In this example, Python makes a guess at what you probably want to do, and decides to round off the floating point number to the nearest integer.

Sometimes, Python can't decide what to do, and so it triggers an error:

In [ ]:
float(1+1j)

A very useful feature is that Python can convert numbers into strings:

In [ ]:
str(7.54)

That is actually what happens when you use the print() commands with a numeric value.

But also very useful is that as long as your string is easily convertable to a number, Python can do this for you too!

In [ ]:
float('5.74')
In [ ]:
int('774')
In [ ]:
complex('5+3j')

Exercise 7 Define a list of parameters with as many types as possible, i.e. all the examples you see above and maybe a few more. Use %whos to see how they look inside the computers' memory. Try to change their format and rerun the %whos command.

In [ ]:
# Your parameters list
a=
b=

# Parameter formats in the computer
%whos

Python can do math

Python has a set of math functions that are directly built in to the language. You can use Python as a calculator!

In [ ]:
1+1

Calculations also work with variables:

In [ ]:
a = 5
print(a+1)

Exercise 8 Discover what the following Python operators do by performing some math with them: *, -, /, **, //, %. Print the value of the mathematical operation to the command line in susequent cells.

In [ ]:
# Try out *
In [ ]:
# Try out -
In [ ]:
# Try out /
In [ ]:
# Try out **
In [ ]:
# Try out //
In [ ]:
# Try out %

Another handy built-in function is abs():

In [ ]:
print(abs(10))
print(abs(-10))
print(abs(1j))
print(abs(1+1j))

You can find the full list of built-in math commands on the python documentation webpage:

https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex

Tab completion in Jupyter Notebooks

Computer programmers often forget things, and often they what variables they have defined. Also, computer programmers always like to save typing if they can.

For this reason, the people who made Jupyter notebooks included a handy feature called "Tab completion" (which is actually something that has been around for a long time in unix and ms-dos command line environments).

The idea is that if you start typing part of the name of a variable or part of the name of a function, and then push the Tab key, Jupyter will bring up a list of the variable and function names that match what you have started to type. If ony one matches, it will automatically type the rest for you. If multiple things match, it will offer you a list: you can either keep typing until it's unique and type tab again, or you can use the cursor keys to select the one you want.

Here is an example:

In [ ]:
this_is_my_very_long_variable_name = 5
this_is_another_ones = 6

Now click on the following code cell, go the end of the lines in this cell and try pushing Tab:

In [ ]:
this

Handy! Jupyter did the typing for me!

If multiple things match, you will get a drop-down box and can select the one you want. So press Tab : after

In [ ]:
th

You can also keep on typing: if you just type a after you hit tab and then hit tab again, it will finish the typing for you.

Exercise 9 Use tab completion on the initial letters of a few of the commands that have been presented. Along the way you will discover many more Python commands!

In [ ]:
# Your code here

Understanding Python Errors

Sometimes, the code you type into a code cell will not work. In this case, Python will not execute your code, but instead print out an error message. In this section, we will take a look at these error messages and learn how to understand them.

Let's write some code that will give an error. For example, this is a typo in the name of the print() command:

In [ ]:
a = 5
printt(a)

After your code cell, you will see some colored text called a "Traceback". This "Traceback" is the way that python tries to tell you where the error is.

Let's take a look at the traceback:

The traceback contains three important details that can help you:

  1. The type of error
  2. Where the error occurred in your code
  3. An attempt to explain why the error happened

For 1 and 2, Python is pretty good and will communicate clearly. For 3, sometimes you need to have some experience to understand what python is trying to tell you.

In this specific case, the type was a NameError that occured on line 2 of our code cell. (By the way, in the View menu, you can turn on and off line numbers in your cells.)

A NameError means that python tried to find a function or variable that you have used, but failed to find one. If you look already at the line of code, you can probably spot the problem already.

At the very end of the traceback, Python tries to explain what the problem was: in this case, it is telling you that there is no function named printt.

You will also get a NameError if you try to use a variable that doesn't exist:

In [ ]:
print(non_existant_variable)

Another common type of error is a SyntaxError, which means you have typed something that python does not understand:

In [ ]:
a = a $ 5

You can also get errors if you try to use operators that do not work with the data type you have. For example, if you try to "divide" two strings:

In [ ]:
"You cannot " / "divide strings"

Here, you get a TypeError: the division operator is a perfectly fine syntax, it just does not work with strings.

In Python, errors are also called "Exceptions", and a complete list of all error (exception) types, and what they mean, can be found here:

https://docs.python.org/3/library/exceptions.html#concrete-exceptions

Sometimes, you can learn more about what the error means by reading these documents, although they are perhaps a bit hard to understand for beginners.

In last resort, you can also always try a internet search: searching for the error message can help, and there are also lots of useful posts on stack exchange (which you will also often find).

Exercise 10 Run the following code and try to understand what is going wrong by reading the error message.

In [ ]:
a=10
b=0
c=(a/b)
print(c)
In [ ]:
4 + physics*3
In [ ]:
d='physics is great' + 2