Skip to content
Snippets Groups Projects
Commit 075ba9b9 authored by Gary Steele's avatar Gary Steele
Browse files

added some more info about execution order

parent 82e8642c
No related branches found
No related tags found
No related merge requests found
%% Cell type:markdown id: tags:
# Python Basics
In this course, you will work 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 these 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 (<a href=https://en.wikipedia.org/wiki/Interpreted_language>interpreted</a>) 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 (<a href=https://en.wikipedia.org/wiki/Markdown>markdown</a> 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.
%% Cell type:code id: tags:
``` python
print("Hello world")
```
%% Cell type:markdown id: tags:
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":
<img width=60% src="behind_the_scenes.png"></img>
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:
%% Cell type:code id: tags:
``` python
print("Hello")
print("world")
print("Goodbye")
```
%% Cell type:markdown id: tags:
In the above, the text in the code cell are all python commands. In addition, if you start a line in a code cell with a `#`, python will ignore this line of text. This is use to add **comments** to your code. It is good programming practice to use comments to explain what the code is doing:
%% Cell type:code id: tags:
``` python
# This will print out a message
print("This is a message")
```
%% Cell type:markdown id: tags:
**Exercise 1.1** Print your own string to the command line. Can you use special characters as well?
%% Cell type:code id: tags:
``` python
# Your code here
```
%% Cell type:markdown id: tags:
## 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 use the following syntax:
%% Cell type:code id: tags:
``` python
a = 5
```
%% Cell type:markdown id: tags:
In python, the `=` symbol represents the **assignment operator**: it is an instruction to **assign** the value of `5` to the variable `a`. If variable `a` already exists, it will be over-written with the new value. 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:
%% Cell type:code id: tags:
``` python
print(a)
```
%% Cell type:markdown id: tags:
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:
%% Cell type:code id: tags:
``` python
print("The value of a is",a)
```
%% Cell type:markdown id: tags:
**Exercise 1.2** Combine a string, a variable, and a numerical values in a single `print` statement using the `,` separator.
%% Cell type:code id: tags:
``` python
# Your code here
```
%% Cell type:markdown id: tags:
**Exercise 1.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?
%% Cell type:code id: tags:
``` python
a =
```
%% Cell type:markdown id: tags:
As you can see in notebooks that the location of your code doesn’t matter, but the order in which you execute them does!!
*(Note that this is very different from code that you would write in a python script and run with a more conventional normal python interpreter, like on a command line or in the matlab-like Spyder interface. In this case, code is always executed top to bottom. For those of you familiar with conventional computer programming, notebooks might seem confusing at first. You can think of it though as having a bunch of different chunks of code you can send to the interpreter in any order you want.)*
Although the code does not need to be executed from top to bottom in a notebook, it is important to remember that if you send somebody else your notebook, they will probably try to execute the cells in order. Therefore, when you (think) you are done making a notebook, you should **ALWAYS** restart the kernel and run the cells from top to bottom just to make sure it works.
We can also use variables to set the values of other variables:
%% Cell type:code id: tags:
``` python
b = 0
print(b)
b = a
print(b)
```
%% Cell type:markdown id: tags:
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:
%% Cell type:code id: tags:
``` python
a=5
%whos
```
%% Cell type:markdown id: tags:
_(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`:
%% Cell type:code id: tags:
``` python
c = 10
d = 15.5
```
%% Cell type:code id: tags:
``` python
%whos
```
%% Cell type:markdown id: tags:
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.
%% Cell type:markdown id: tags:
## 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 <a href="."> "tree" view </a> 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:
%% Cell type:code id: tags:
``` python
from time import sleep
sleep(10*60)
```
%% Cell type:markdown id: tags:
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:
<img src="stop_button.png"></img>
or the menu item "Kernel / Interrupt".
**Exercise 1.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?
%% Cell type:code id: tags:
``` python
# Your code here
```
%% Cell type:markdown id: tags:
## 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:
%% Cell type:code id: tags:
``` python
a = 1
b = 2
c = 3.3
%whos
```
%% Cell type:markdown id: tags:
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:
%% Cell type:code id: tags:
``` python
a = a/2
```
%% Cell type:code id: tags:
``` python
%whos
```
%% Cell type:markdown id: tags:
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:
%% Cell type:code id: tags:
``` python
c = 1.5e-8
```
%% Cell type:markdown id: tags:
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$:
%% Cell type:code id: tags:
``` python
d = 1+1j
```
%% Cell type:code id: tags:
``` python
%whos
```
%% Cell type:markdown id: tags:
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,
%% Cell type:code id: tags:
``` python
0.5*d
```
%% Cell type:markdown id: tags:
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 `"`:
%% Cell type:code id: tags:
``` python
e = "This is a string"
f = 'This is also a string'
```
%% Cell type:code id: tags:
``` python
%whos
```
%% Cell type:markdown id: tags:
You can also make multiline strings using three single quotes:
%% Cell type:code id: tags:
``` python
multi = \
'''
This string
has
multiple lines.
'''
print(multi)
```
%% Cell type:markdown id: tags:
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 1.5** Discover what the `+` operator does to a string, i.e. print the output of the sum of two strings.
%% Cell type:code id: tags:
``` python
# Your code here
```
%% Cell type:markdown id: tags:
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).
%% Cell type:code id: tags:
``` python
g = False
```
%% Cell type:code id: tags:
``` python
%whos
```
%% Cell type:markdown id: tags:
We will use boolean types much more later when we look at program control flow, but a simple example is the `if` statement:
%% Cell type:code id: tags:
``` python
if True:
print("True is always true.")
if g:
print("g is true!")
if not g:
print("g is not true!")
```
%% Cell type:markdown id: tags:
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`.
%% Cell type:markdown id: tags:
**Exercise 1.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.
%% Cell type:markdown id: tags:
## 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:
%% Cell type:code id: tags:
``` python
float(5)
```
%% Cell type:code id: tags:
``` python
int(7.63)
```
%% Cell type:markdown id: tags:
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:
%% Cell type:code id: tags:
``` python
round(7.63)
```
%% Cell type:markdown id: tags:
Round also converts the data type to an `int`.
The idea of converting one variable type to an another using functions based on the variable type you want to convert to works for many variable types. All variable types in python "provide" such a function.
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:
%% Cell type:code id: tags:
``` python
float(1+1j)
```
%% Cell type:markdown id: tags:
A very useful feature is that Python can convert numbers into strings:
%% Cell type:code id: tags:
``` python
str(7.54)
```
%% Cell type:markdown id: tags:
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!
%% Cell type:code id: tags:
``` python
float('5.74')
```
%% Cell type:code id: tags:
``` python
int('774')
```
%% Cell type:code id: tags:
``` python
complex('5+3j')
```
%% Cell type:markdown id: tags:
**Exercise 1.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.
%% Cell type:code id: tags:
``` python
# Your parameters list
a=
b=
# Parameter formats in the computer
%whos
```
%% Cell type:markdown id: tags:
## 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!
%% Cell type:code id: tags:
``` python
1+1
```
%% Cell type:markdown id: tags:
Calculations also work with variables:
%% Cell type:code id: tags:
``` python
a = 5
print(a+1)
```
%% Cell type:markdown id: tags:
**Exercise 1.8** Discover what the following Python operators do by performing some math with them: `*`, `-`, `/`, `**`, `//`, `%`. Add your own code to the following cells to "discover" what the operators do. You may need to try them out for a couple of different cases printing out the result.
%% Cell type:code id: tags:
``` python
# What does * do?
your code
```
%% Cell type:code id: tags:
``` python
# What does - do?
your code
```
%% Cell type:code id: tags:
``` python
# What does / do?
your code
```
%% Cell type:code id: tags:
``` python
# What does ** do?
your code
```
%% Cell type:code id: tags:
``` python
# What does // do?
your code
```
%% Cell type:code id: tags:
``` python
# What does % do?
your code
```
%% Cell type:markdown id: tags:
Another handy built-in function is `abs()`:
%% Cell type:code id: tags:
``` python
print(abs(10))
print(abs(-10))
print(abs(1j))
print(abs(1+1j))
```
%% Cell type:markdown id: tags:
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
%% Cell type:markdown id: tags:
## Sending data to python using input()
So far, we have seen examples of "output": Python telling us stuff, like in the first "Hello world" example above.
And we have seen example of "code": us giving instructions to python to do things.
In addition, we can also send stuff to Python. Often in physics, we do this by having Python read data files, which we will cover later, but we can also send information to python using the `input()` command:
%% Cell type:code id: tags:
``` python
a = input()
print()
print("The input was:")
print(a)
```
%% Cell type:markdown id: tags:
Even if we type a number into the input box, it will always return a string variable of type `str`:
%% Cell type:code id: tags:
``` python
type(a)
```
%% Cell type:markdown id: tags:
If we want to use our input as a number, we have to convert it to a number, for example, by using the `float()` function:
%% Cell type:code id: tags:
``` python
a = input()
a = float(a)
print("\nThe value of a is:", a)
print("a has the type:", type(a))
```
%% Cell type:markdown id: tags:
You can also specify text for the label of the input box:
%% Cell type:code id: tags:
``` python
a = input("Enter a number: ")
```
%% Cell type:markdown id: tags:
**Exercise 1.9** Use the `input` function to create variables in the kernel of the following types: an `integer`, a `float`, and a `string`.
%% Cell type:code id: tags:
``` python
# Your code here
```
%% Cell type:markdown id: tags:
## Tab completion in Jupyter Notebooks
Computer programmers often forget things. They even sometimes forget the names of the variables they have created themselves! Fruthermore, 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 href=https://en.wikipedia.org/wiki/Command-line_completion>a long time</a> in unix 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:
%% Cell type:code id: tags:
``` python
this_is_my_very_long_variable_name = 5
this_is_another_ones = 6
```
%% Cell type:markdown id: tags:
Now click on the following code cell, go the end of the line in this cell and try pushing `Tab`:
%% Cell type:code id: tags:
``` python
this
```
%% Cell type:markdown id: tags:
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. If you want, you can then use the arrow keys to pick one of them and type `Enter` to finish the tab completion. Try it here:
%% Cell type:code id: tags:
``` python
th
```
%% Cell type:markdown id: tags:
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. Try it here:
%% Cell type:code id: tags:
``` python
th
```
%% Cell type:markdown id: tags:
**Exercise 1.10** 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!
%% Cell type:code id: tags:
``` python
# Your code here
```
%% Cell type:markdown id: tags:
## 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:
%% Cell type:code id: tags:
``` python
a = 5
printt(a)
```
%% Cell type:markdown id: tags:
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:
<img src="anatomy_of_an_error.png" width=80%></img>
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:
%% Cell type:code id: tags:
``` python
print(non_existant_variable)
```
%% Cell type:markdown id: tags:
Another common type of error is a `SyntaxError`, which means you have typed something that python does not understand:
%% Cell type:code id: tags:
``` python
a = a $ 5
```
%% Cell type:markdown id: tags:
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:
%% Cell type:code id: tags:
``` python
"You cannot " / "divide strings"
```
%% Cell type:markdown id: tags:
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: googling the error message can help, and there are also lots of useful posts on <a href=https://stackexchange.com>stack exchange</a> (which you will also often find by google).
%% Cell type:markdown id: tags:
**Exercise 1.11** Run the following code and try to understand what is going wrong by reading the error message.
%% Cell type:code id: tags:
``` python
a=10
b=0
c=(a/b)
```
%% Cell type:code id: tags:
``` python
4 + practicum*3
```
%% Cell type:code id: tags:
``` python
d='practicum is great' + 2
```
......
......@@ -113,7 +113,11 @@ print("The value of a is",a)
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!!
As you can see in notebooks that the location of your code doesn’t matter, but the order in which you execute them does!!
*(Note that this is very different from code that you would write in a python script and run with a more conventional normal python interpreter, like on a command line or in the matlab-like Spyder interface. In this case, code is always executed top to bottom. For those of you familiar with conventional computer programming, notebooks might seem confusing at first. You can think of it though as having a bunch of different chunks of code you can send to the interpreter in any order you want.)*
Although the code does not need to be executed from top to bottom in a notebook, it is important to remember that if you send somebody else your notebook, they will probably try to execute the cells in order. Therefore, when you (think) you are done making a notebook, you should **ALWAYS** restart the kernel and run the cells from top to bottom just to make sure it works.
We can also use variables to set the values of other variables:
......
%% Cell type:markdown id: tags:
# Functions in Python
In this notebook, we will explore the implementation of functions in Python.
**Learning objectives for this notebook:**
* Student is able to define functions with input parameters to execute a piece of code (use functions as "methods")
* Student is able to create and use functions that return a value (or multiple values)
* Student is able to import functions from libraries / modules
* Student is able to use Shift-Tab to bring up the help for a function from a library
* Student is able to predict if a variable name in a function refers to a local variable or a global variable
## Functions to save typing
In programming, you often want to repeat the same sequence of commands over and over again.
One way to do this is to copy and paste the same piece of code over and over again. This is actually quite easy, but runs quickly into a problem: let's say you want to change a little bit what that code will do, then you need to change it in many places. If you change it in one place but forget in another, then your program might crash (ie. give an error). Or even worse, and even harder to <a href=https://en.wikipedia.org/wiki/Debugging>debug</a> the mistake may may give an error message but give you the wrong answer!
For this reason (among others), programming languages allow programmers to define "functions". Functions are pieces of code that you can give a name and then enable you to them use over and over again, without having to retype the code text.
As an example, let's say that we want to print out the value of a variables named `a` and `b` using a long sentence:
%% Cell type:code id: tags:
``` python
a = 6
b = 4
print("The value of variable a is", a)
print("The value of variable b is", b)
a = a/2
b = 3
print("The value of variable a is", a)
print("The value of variable b is", b)
a = a+1
b = 1.5
print("The value of variable a is", a)
print("The value of variable b is", b)
a = a-20
b = -1e4
print("The value of variable a is", a)
print("The value of variable b is", b)
a = a+1j
b = 1
print("The value of variable a is", a)
print("The value of variable b is", b)
```
%% Cell type:markdown id: tags:
To save a lot of typing, one can define a simple function to do this work for us. To define a function, you use the following syntax:
```
def function_name():
...
```
Here, you replace the `...` with the code you want to function to execute. The Python the code inside the function should be <a href=https://en.wikipedia.org/wiki/Indentation_(typesetting)>indented</a> by starting each line with a <a href=https://en.wikipedia.org/wiki/Tab_key>tab</a>. By default, adding a tab will produce 4 spaces in your code. You can also "indent" your code by manually adding spaces, but you must make sure to add 4 spaces each time. The Jupyter notebook will try to detect if you make a mistake in your indentation, and will sometimes color your text in red if it detects a mistake.
Tabs in Python are VERY IMPORTANT: python uses tabs to know which code is inside the function and which is not. If you make a mistake with the tabs in such a way that python cannot understand what you mean, it will give you an `IdentationError`.
In notebooks, you can also select a line, or multiple lines, and then use `Tab` to increase their indentation level, or use `Shift-Tab` to decrease their indentation level.
%% Cell type:code id: tags:
``` python
def test_tab_and_shift_tab():
some code
that is indendented
try selecting this text
and then pushing tab
and shift-tab
```
%% Cell type:markdown id: tags:
Once you have defined your function, you can execute it by using the code `function_name()`.
Let's look at how to use a function as a "procedure" to simplify the code above:
%% Cell type:code id: tags:
``` python
def print_status():
print("The value of variable a is", a)
print("The value of variable b is", b)
a = 6
b = 4
print_status()
a = a/2
b = 3
print_status()
a = a+1
b = 1.5
print_status()
a = a-20
b = -1e4
print_status()
a = a+1j
b = 1
print_status()
```
%% Cell type:markdown id: tags:
In this example, it may not be such a big deal, but you can imagine that as the code in your function becomes more and more complicated, it will save you a lot of time. Also, imagine that I wanted to change the wording of the sentence I print: in the case with the function, I would only have to do this once, while in the example without function, I would have to manually change this at 5 different places.
%% Cell type:markdown id: tags:
**Exercise 2.1** Write your own function that contains two lines of code. The first line should make a new variable `var2` that is `var` converted to an integer. The second line of your code should print the value of `var2`.
Using this code, play around with the indentation (add extra tabs and spaces for example) to see how 'critical' Python is with indentation. For example: does three spaces work instead of `Tab`? Does one space work? What about `Tab` on the first line and three spaces on the second line? Can you make Python trigger an `IdentationError`?
%% Cell type:code id: tags:
``` python
var=3.5
# Your function here
```
%% Cell type:markdown id: tags:
## Functions with input variables
Let's say that we wanted to print out the status of variables that we do not know the name of ahead of time, as in the example above. Say we wanted to make a function that could print out a message with the status of value of ANY variable. How could we do this?
In the example above, our function explicitly printed out variables `a` and `b`. But this only works because I know in advance that the person using my function has defined variables `a` and `b`. But what if I want to print the value of variable `c`?
To allow functions to be more generic, and therefore more "reusable" in general, Python allows you to define "input variables" for your function. The syntax for this is the following:
```
def function_name(x):
...
```
When you do this, for the code INSIDE your function, a variable `x` will be defined that will have the value given by the input value given to the function by the user. Let's look at a specific example:
%% Cell type:code id: tags:
``` python
def print_status2(x):
print("The value passed to the function is", x)
a = 1.5
print_status2(a)
a = 1+1j
print_status2(a)
print_status2(1.5323)
```
%% Cell type:markdown id: tags:
How does this work?
When the function `print_status(a)` is called, Python "sends" ("passes" in computer speak) the value of `a` to the function. Inside the function, Python creates a new (temporary) variable called `x`, that is defined ONLY while the function code is running. This temporary variable `x` is then assigned the value that was sent to the function, and then the code is executed. When the function is finished, the variable `x` is destroyed. (Try adding the code `print(x)` above outside the function and see what happens!)
Note, as you can see in the third example, the things you pass to functions do not even need to be variables! This is fine because the function only needs the value of the argument that is passed to the function.
%% Cell type:markdown id: tags:
**Exercise 2.2** Copy your code from exercise 2.1 into the cell below and change it such that it uses a function with input parameters to achieve the same task.
%% Cell type:code id: tags:
``` python
# Your code here
```
%% Cell type:markdown id: tags:
## Functions with multiple inputs
Functions can also take multiple input variables. To do this, you put them all in between the brackets `()`, separated by commas. For example, with 3 variables, the syntax is:
```
def function_name(variable1, variable2, variable3):
...
```
You would then use this function in the following way:
```
function_name(argument1, argument2, argument3)
```
When you do this, inside the function, `variable1` will get assigned the value of `argument1`, `variable2` will get assigned the value of `argument2`, and `variable3` will get assigned the value of `argument3`. This matching of the position in the list is called matching by "positional order".
Note that there are several different names used for the "input variables" of a function: often, computer scientists will also use the name "input arguments" (or just "arguements), or "input parameters" (or just "parameters").
%% Cell type:code id: tags:
``` python
def print_status3(x, y):
print("The value of the first input variable is ", x)
print("The value of the second input variable is ", y)
print_status3(1,2)
print_status3(2.5,1.5)
print_status3(a, 2*a)
```
%% Cell type:markdown id: tags:
**Exercise 2.3** Make a new function `print_status4()` that takes three variables as arguments and prints out messages telling the user the values of each of them (as above, but with three input variables).
%% Cell type:code id: tags:
``` python
# Your code here
```
%% Cell type:markdown id: tags:
## Functions that return a value
In addition to receiving values as inputs, functions can also send back values to the person using the function. In computer programming, this is called the "return value".
When you create a function, you can use the `return` command to specify what value should be sent back to the person using the function. Let's look at an example:
%% Cell type:code id: tags:
``` python
def my_formula(x):
y = x**2 + 3
return y
```
%% Cell type:markdown id: tags:
To "capture" the value returned by the function, you can assign it to a varible like this:
%% Cell type:code id: tags:
``` python
result = my_formula(3.5)
print(result)
```
%% Cell type:markdown id: tags:
You can also just directly "use" the result of the function if you want:
%% Cell type:code id: tags:
``` python
print(my_formula(4.6))
```
%% Cell type:markdown id: tags:
Note that as soon as python sees the `return` command, it stops running the function, so any code after it will not be executed:
%% Cell type:code id: tags:
``` python
def myfunction(x):
print("This gets printed.")
return x**2 + 3
print("This does not.")
print(myfunction(5))
```
%% Cell type:markdown id: tags:
If you want to send back more than one result to the user of your function, you can separate the results with commas when you use the `return` command. How do you make use of these two variables that you send back? You will explore this in this exercise:
**Exercise 2.4** Write a function that takes two real numbers as input and returns the sum and product of the two numbers. Catch the two numbers in two separate variables and print them to the command line. If you need more help look on the internet, e.g. google "python function with two variables".
**Exercise 2.4** **(a)** Write a function that takes two real numbers as input and returns the sum and product of the two numbers. In your function, try to send *both* of the calculated numbers back as a return value. We have not taught you that yet so you will have to look it up: I recommend trying a google search for "python function return two variables".
%% Cell type:code id: tags:
``` python
# Your function here
def product_and_sum(...):
...
```
%% Cell type:markdown id: tags:
**(b)** Now USE your function to calculate the sum and product of `a` and `b`, "capturing" the sum and product in variables `s` and `p`:
%% Cell type:code id: tags:
``` python
a=1.5
b=2.5
# Your function here
...some code that uses the return value of your function to set variable s and p...
print("Sum is:", s)
print("Product is:, p)
```
%% Cell type:markdown id: tags:
## Importing functions from libraries
One of the big advantages of python is that there are huge collection of libraries that include code for doing a huge number of things for you! We will make extensive use of the library `numpy` for numerical calculations in Python, and the library `matplotlib` for generating scientific plots. Beyond this, nearly anything you want to be able to do on a computer can be found in Python libraries, which is one of the reasons it is so popular.
In order make use of these libraries of code, you need to "import" them into the "namespace" of your kernel.
("Namespace" is Python-speak for the list of functions and variable names that you can find in the running copy of python that is connected to your notebook.)
Here, we will show you a few examples of different ways of importing code into your notebook from a library (also called a "module"). For this, we will take the example we used already in Notebook 1: in the module `time`, there is a function called `sleep()` that will perform the task of "pausing" for a number of seconds given by the its argument.
You can find out more about the `time` module by looking at its documentation webpage:
https://docs.python.org/3/library/time.html
and specifically about the `sleep()` function here:
https://docs.python.org/3/library/time.html#time.sleep
### Importing a whole module
The simplest way to be able use the `sleep` function of the `time` module is to import it using the following command:
%% Cell type:code id: tags:
``` python
import time
```
%% Cell type:markdown id: tags:
You can see it has been imported by using the `%whos` command:
%% Cell type:code id: tags:
``` python
%whos
```
%% Cell type:markdown id: tags:
Once it has been imported, you can access all the functions of the module by adding `time.` in front of the function name (from the time module) in your code:
%% Cell type:code id: tags:
``` python
print("Starting to sleep")
time.sleep(5)
print("Done!")
```
%% Cell type:markdown id: tags:
If you import the whole module, you will have access to all the functions in it. To see what functions are in the module for you to use type `dir(sleep)`, which will generate this list.
Sometimes, if you will be using the functions from the module a lot, you can give it a different "prefix" to save yourself some typing:
%% Cell type:code id: tags:
``` python
import time as tm
print("Starting to sleep")
tm.sleep(5)
print("Done!")
```
%% Cell type:markdown id: tags:
We will use this a lot when using the `numpy` module, shortening its name to `np` when we import it, and also for the `matplotlib.pyplot` submodule, which we will shorten to `plt`. (These are also typically used conventions in the scientific community.)
%% Cell type:markdown id: tags:
### Importing a single function
If you need only a single function from a library, there is also a second commonly used way to import only that single function using the following syntax:
%% Cell type:code id: tags:
``` python
from time import sleep
```
%% Cell type:markdown id: tags:
When you do this, the function `sleep()` will be available directly in your notebook kernel "namespace" without any prefix:
%% Cell type:code id: tags:
``` python
print("Starting to sleep")
sleep(5)
print("Done!")
```
%% Cell type:markdown id: tags:
Using `%whos`, we can now see that we have three different ways to use the `sleep()` function:
%% Cell type:code id: tags:
``` python
%whos
```
%% Cell type:markdown id: tags:
If you look around on the internet, you will also find people that will do the following
```
from numpy import *
```
This will import all the functions from numpy directly into the namespace of your kernel with no prefix. You might think: what a great idea, this will save me loads of typing! Instead of typing `np.sqrt()` for example, to use the square-root function, I could just type `sqrt()`.
While true, it will save typing, it also comes with a risk: sometimes different modules have functions that have the same name, but do different things. A concrete example is the function `sqrt()`, which is available in both the `math` module and the `numpy` module. Unfortunately, `math.sqrt()` will give an error when using numpy arrays (which we will learn more about in later notebooks).
If you import both of them, you will overwrite these functions by the second import, and if you're not careful, you will forget which one you are using, and it could cause your code to break. It will also "crowd" your notebooks namespace: using the `whos` function, you will suddenly see hundreds or even thousands of functions, instead of only just a module.
For these reasons, it is generally advised not to use `import *`, and it is considered poor coding practice in modern python.
%% Cell type:markdown id: tags:
### Shift-Tab for getting help
Like the tab completion we saw in the first notebook, Jupyter also can give you help on functions you have imported from libraries if you type `Shift-Tab`.
Say I forgot how to use the `sleep()` function. If I type the word "sleep" and then push `Shift-Tab`, Jupyter will bring up a help window for that function.
Try it: click on any part of the word `sleep` in the following code cell and push `Shift-Tab`:
%% Cell type:code id: tags:
``` python
sleep
```
%% Cell type:markdown id: tags:
You can also find the same help as the output of a code cell by using the `help()` function:
%% Cell type:code id: tags:
``` python
help(sleep)
```
%% Cell type:markdown id: tags:
There are extensive online resources for many modules. The most used modules have helpful examples on the functions and how to implement them.
%% Cell type:markdown id: tags:
**Exercise 2.5** (a) Find help for the built-in functions `abs`, `int`, and `input`.
%% Cell type:code id: tags:
``` python
# Your code here
```
%% Cell type:markdown id: tags:
(b) Import the function `glob` from the library `glob` and print its help information. What does the function `glob("../*")` do?
%% Cell type:code id: tags:
``` python
# your code here
```
%% Cell type:markdown id: tags:
## Global variables, local variables, and variable scope
In our first functions above, we saw a couple of examples of using variables inside functions.
In the first example, we used the variables `a` and `b` inside our function that we created outside our function, directly in our notebook.
In the second example, we used the "temporary" variable `x` inside our function.
These were two examples of different variable "scope". In computer programming, <a href=https://en.wikipedia.org/wiki/Scope_(computer_science)>scope</a> define the rules Python uses when it tries to look up the value of a variable.
In the slightly simplified picture we will work with here, variables can have two different types of "scopes": **global scope** and **local scope**.
If Python looks for a variable value, it first looks in the local scope (also called "local namespace"). If it does not find it, python will go up into the global scope (also called the "global namespace") and look for the variable there. If it does not find the varible there, it will trigger an error (a `NameError` to be precise).
How do I create a global variable? By default, if you create a variable directly in your notebook (and not in a function in your notebook), it will always be **global**. So, actually, you've already created a bunch of global variables!
Any variables you define inside a function in your code will be a **local** variable (including the input variables automatically created if your function takes any arguments).
If you want to create a global variable inside a function, or make sure the variable you are referring to is the global variable and not the local one, you can do this by the `global` qualifier, which we will look at in a minute.
Let's take a look at this in more detail by analysing a few examples.
**Example 1** Accessing a global variable inside a function
%% Cell type:code id: tags:
``` python
a1 = 5
def my_func():
print(a1)
my_func()
a1 = 6
my_func()
```
%% Cell type:markdown id: tags:
In this example, when Python is inside the function `my_func()`, it first looks to see if there is a variable `a1` in the local scope of the function. It does not find one, so it then goes and looks in the global scope. There, it finds a variable `a1`, and so it uses this one.
**Example 2** An example that doesn't work (unless you've run the next cell, in which case it will only fail again after you restart your kernel)
%% Cell type:code id: tags:
``` python
def my_func():
print(b1)
my_func()
```
%% Cell type:markdown id: tags:
This code gives a `NameError` because there is no variable `b1` yet created in the global scope. If we run the following code cell and try the code above again, it will work.
%% Cell type:code id: tags:
``` python
b1 = 6
```
%% Cell type:markdown id: tags:
Here you can see one of risks of languages like python: because of the persistent memory of the kernel, code can succeed or fail depending on what code you have run before it...
If you want to see the error message above again, you can delete variable b1 using this code and run it again:
%% Cell type:code id: tags:
``` python
del b1
```
%% Cell type:markdown id: tags:
**Example 3** Variables defined in the local scope of a function are not accessible outside the function
%% Cell type:code id: tags:
``` python
def my_func():
x = 5
my_func()
print(x)
```
%% Cell type:markdown id: tags:
**Example 4** Variables passed to functions cannot be modified by the function (more on this later when we look at more complicated data structures...sometimes this is different)
%% Cell type:code id: tags:
``` python
def my_func(a):
a = 6
a=5
my_func(a)
print(a)
```
%% Cell type:markdown id: tags:
This one is a bit subtle (mega-confusing?) because we re-used the same name `a` for the local variable in the function as the global variable outside of the function. However, the operation is quite logical. When the function code starts running, it creates a `local` variable `a` to store the value it received. And now, because there is already a local variable called `a`, using `a` in the function refers to the `local` variable `a`, not the `global` variable `a` we define before calling the function.
%% Cell type:markdown id: tags:
**Example 5** This one is a tricky one.
%% Cell type:code id: tags:
``` python
a = 6
def my_func():
a = 7
print(a)
my_func()
print(a)
```
%% Cell type:markdown id: tags:
It would seem that the function would refer to the global variable `a` and therefore change it's value. However, it is tricky since we first use `a` in the function in an assignment. An assignment in python will automatically create a variable if it does not exist, and so python creates a new variable named `a` in the local scope. The name `a` inside the function now refers to this newly created local variable, and therefore the global variable will not be changed. In fact, this guarantees that you cannot change global variables inside a function, unless you use the `global` qualifier shown in the next example.
%% Cell type:markdown id: tags:
**Example 6** If you want to make sure that the `a` inside your function is referring to the global variable `a`, you can include the line `global a` inside your function to tell python that you mean the global variable `a`.
%% Cell type:code id: tags:
``` python
a = 6
def my_func():
global a
a = 7
print(a)
my_func()
print(a)
```
%% Cell type:markdown id: tags:
Note that in general, it is considered bad programming practice to use (too many) global variables. Why? When you write longer and bigger sections of code, it is easier to understand what is going in in your function if your function uses only local variables and communicates back and forth using input parameter and return variables. Using too many global variables in a function can be confusing because they are defined in a different place in your code and so you don't have a good oversight of them. (Bigger projects can easily have 10,000+ lines of code!)
In computer science, this is a topic of often intense debate (resulting in what nerds refer to as a <a href="https://www.urbandictionary.com/define.php?term=flame%20war">flame war</a>), with global variables being branded as "dangerous" like in this stack exchange post:
https://stackoverflow.com/questions/423379/using-global-variables-in-a-function
But I personally agree with the comments in this post that "global variables have their place but should be used sparingly".
Summary of the rules for global and local variables:
* If a local variable of the same name exists or is created by python (by assignment, for example), then python uses the local varible
* If you try to use a variable name that does not exist locally, python checks for a global variable of the same name
* If you want to change the value of a global inside a function, then you must use the `global` statement to make it clear to python than you want that name to refer to the global variable
......
......@@ -247,12 +247,24 @@ print(myfunction(5))
If you want to send back more than one result to the user of your function, you can separate the results with commas when you use the `return` command. How do you make use of these two variables that you send back? You will explore this in this exercise:
**Exercise 2.4** Write a function that takes two real numbers as input and returns the sum and product of the two numbers. Catch the two numbers in two separate variables and print them to the command line. If you need more help look on the internet, e.g. google "python function with two variables".
**Exercise 2.4** **(a)** Write a function that takes two real numbers as input and returns the sum and product of the two numbers. In your function, try to send *both* of the calculated numbers back as a return value. We have not taught you that yet so you will have to look it up: I recommend trying a google search for "python function return two variables".
```python
# Your function here
def product_and_sum(...):
...
```
**(b)** Now USE your function to calculate the sum and product of `a` and `b`, "capturing" the sum and product in variables `s` and `p`:
```python
a=1.5
b=2.5
# Your function here
...some code that uses the return value of your function to set variable s and p...
print("Sum is:", s)
print("Product is:, p)
```
## Importing functions from libraries
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment