AE1205 Programming and Scientific Computing in PYTHON - April 2022 2
AE1205 Programming and Scientific Computing in PYTHON - April 2022 2
Delft
University of
Technology
iii
Reading guide
This reader is intended to be used as reference material during the course, as well as during the exams.
To guide you through the reader, the following is useful to know:
Throughout the reader, most material counts as exam material. Some parts, however, are optional.
You can recognise optional parts as follows:
• Within chapters, small optional parts (that for instance dive deeper into a concept, or provide an
alternative approach) are encapsulated in blue boxes that are marked extra:
• When an entire chapter is optional, its chapter number will be marked with a red asterisk: ∗ .
Coloured boxes are also used to mark or reiterate something that is important:
In each chapter, several sections have exercises related to the presented material. The answers to
these numbered exercises can be found in several places:
• In Appendix A.
• As Python files on https://github.com/TUDelft-AE-Python/ae1205-exercises/ in
the folder pyfiles.
• As Jupyter notebooks on https://github.com/TUDelft-AE-Python/ae1205-exercises/
in the folder notebooks.
The exam of the Python course is an open-book exam: you are allowed to use an original bound copy
of the reader during the entire exam. Sometimes it’s useful to read something back in detail in one of
the chapters, but often just a quick reminder will be enough. In the latter case you can use the course
cheat sheet in Appendix C.
v
Contents
1 Getting started 1
1.1 What is programming? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 What is Python? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.3 Installing Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3.1 Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3.2 macOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.3.3 Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.3.4 Explanation of the installed modules . . . . . . . . . . . . . . . . . . . . . 6
1.3.5 Configuring the IDLE editor . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.3.6 Working environments: IDLE, PyCharm, Spyder, and others . . . . . . . . 7
1.3.7 Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.4 Sneak preview: Try the Python language yourself . . . . . . . . . . . . . . . . . . 10
1.5 Variables and the assignment statement . . . . . . . . . . . . . . . . . . . . . . . 11
1.6 Finding your way around: many ways in which you can get help . . . . . . . . . . 13
1.6.1 Method 1: Using help(”text”) or interactive help() . . . . . . . . . . 13
1.6.2 Method 2: Python documentation in Help Pull-down menu . . . . . . . . . 14
1.6.3 Method 3: Get help from the huge Python community . . . . . . . . . . . . 14
2 Variable assignment and types 17
2.1 Assignment and implicit type declaration . . . . . . . . . . . . . . . . . . . . . . . 18
2.2 Short-hand when using original value with an operator. . . . . . . . . . . . . . . . 19
2.3 Number operations in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.4 Float: variable type for floating point values. . . . . . . . . . . . . . . . . . . . . . 20
2.5 Int: variable type for round numbers like counter or index). . . . . . . . . . . . . . 20
2.6 Complex numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.7 String operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.8 Booleans and logical expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.9 The list type: an ordered collection of items. . . . . . . . . . . . . . . . . . . . . . 25
2.9.1 What are lists? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.9.2 Indexing and slicing, list functions and delete . . . . . . . . . . . . . . . . . 26
2.9.3 Quick creation of lists with repeating values . . . . . . . . . . . . . . . . . 27
2.9.4 List methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2.10 Some useful built-in functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
3 Python syntax: Statements 31
3.1 Assignment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.2 The print statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.2.1 Using print . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.2.2 Formatting your output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
3.3 The input function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.4 Checking conditions: the if-statement . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.5 Iteration using a for-loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
3.5.1 Additional tools for for-loops: itertools . . . . . . . . . . . . . . . . . . . . . 39
3.6 Iteration using a while-loop. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
3.7 Controlling a loop: break and continue . . . . . . . . . . . . . . . . . . . . . . . . 41
4 Making your code reusable and readable 43
vii
viii Contents
1
2 1. Getting started
Another way to design and represent algorithms is using simplified natural language. Let’s take as an
example the algorithm to “find the maximum value of four numbers”. We can detail this algorithm as a
number of steps:
Going through these steps, the result will always be that the maximum value of the four numbers is
shown on the screen. This kind of description in natural language is called “pseudo-code”.
This pseudo-code is already very close to how Python looks, as this was one the goals of Python:
it should read just as clear as pseudo-code. But before we can look at some real Python code, we
need to know what Python is and how you can install it. After that, we will have a look at some simple
programs in Python, which you can try out in your freshly installed Python environment.
# Python 3.x:
print(”Hello World”)
(Other major differences relate to integer division and the input() function. You will learn more about
these concepts during the course).
Python is called a scripting language. This means that the Python source is interpreted when you run
the program as opposed to needing to compile all program files first, then link them together with the
correct libraries to make an executable and then run this. This is very user-friendly: there is no need to
compile or link files before you can run the same program on any operating system: Windows, Apple’s
macOS or Linux. While many Python libraries are platform dependent, most are available on all three
supported platforms.
The penalty for this is, depending on your program structure, sometimes some execution speed. Some
runtime, often seconds or milliseconds, are traded for shorter development times, which saves time in
the order of days or weeks. Note that Python libraries like NumPy and Scipy use very fast low-level
modules, resulting in extremely fast execution times for scientific computing. By using a similar syntax,
this is replacing MATLAB (which also uses a small scripting language) worldwide. Using vectorised
programming and the NumPy library makes runtimes comparable with optimised C++ and Fortran for
the scientific computing. The same mechanism allows fast graphics. For example for 2D graphics the
Pygame graphics library, which is a layer over the very fast and famous SDL library, is also extremely
fast.
Using an interpreter instead of a compiler, means you need to have Python, with its interpreter, installed
on the computer where you run the Python program. Would you ever want to make an executable to
avoid this requirement, there are modules, like distutils, or add-ons, like called Py2exe creating self-
contained application which does not need Python installed. Such an executable will only run on the
OS under which it was compiled. How this should be done, is covered by a separate chapter of the
reader, called ‘Distributing your Python program’. Using the Inno Setup tool, you can make a installer
for your application on Windows for example. This tool converts the complete folder with data and the
program into one setup executable file, which is also covered in this chapter.
1.3.1. Windows
In Windows there are three main ways to install Python; starting out with a plain package from the official
distributor, using a bundle like Anaconda, and installing python through the Microsoft Store.
We’ve found that the most robust way to install Python is by starting out with a plain installation of
Python, from the official python website. Go to Python.org and download the latest version of Python 3
64-bits. (You can also check out the steps at Youtube: https://youtu.be/-P7dVLJPows)
If you’re installing Python a second time, then make sure you have uninstalled/removed any other
versions of Python, using the Add/Remove Apps/Programs from the Settings/Configuration panel of
Windows.
Once you’ve downloaded the installer, run it, and:
4 1. Getting started
1. During the installation, when you get the dialog indicated below, make sure you tick the box ‘Add
Python 3.X to PATH’! (otherwise the Python package installer pip will not work)
3. In the dialog that follows, check all options, especially ‘install for all users’
When the installation finishes you’ll not only have Python, but also the Python package manager pip
installed, which makes it relatively simple to install the other packages. Pip makes sure to download
the exactly right version for your installation of Python and OS and will install it.
The next step is to install the Python packages you’ll need during the course. In the Windows Start
Menu, search for command prompt:
1.3.2. macOS
By default, macOS already has Python installed, on recent macOS versions even Python 3.8. However,
these preinstalled versions have some issues, specifically with the installation of packages needed for
the course. It is therefore required to install an additional version of Python on your Mac. The currently
recommended method for this is to use the official Python installer from python.org:
1. In your browser, navigate to python.org, and in the ‘Downloads’ menu, click on the download
link of the current Python version for macOS.
2. Once downloaded, open the installer, which will give you the following dialog:
Follow the dialog steps. Unless you want a different installation location (not recommended),
there’s no need to make any changes to the default settings.
3. Once the installation is finished, a finder window is opened, showing the results of the Python
installation.
4. To install the packages required for this course, you need to open a terminal. Simultaneously
press the ‘command’ and space keys, type the word terminal, and press enter:
1.3.3. Linux
Python is usually installed by default on modern Linux distributions. To ensure that you have all the
required packages, execute the following installation command:
• For debian-based distributions (Debian, Ubuntu, Mint Linux, ...):
sudo apt-get install python3 python3-numpy python3-scipy \
python3-matplotlib python-pygame
• For RedHat-based distributions (RedHat, openSUSE, Fedora, CentOS, ...):
sudo zypper install python3 python3-numpy python3-scipy \
python3-matplotlib python-pygame
sheet. Download the documentation for Scipy (CHM files) and use the link to the pygame/docs
site as well as the Matplotlib gallery.
• Using IDLE: your first program
In the IDLE window, named Python Shell, select File > New Window. This creates a second
window in which you can edit your first program. You will see that this window has a different
pull-down menu. It contains “Run”, indicating this is a Program window, still called Untitled at
first. Enter the following lines in this window:
print(”Hello World”)
print(”This is my first Python program”)
Note: Python is case sensitive, so it matters that you use lower case for the print command.
Now select Run > Run Module. You will get a dialog box telling you that you have to Save it
and then asking whether it is Ok to Save this, so you click Ok. Then you enter a name for your
program like hello.py and save the file. The extension .py is important for Python, the name is
only important for you. Then you will see the text being printed by the program which runs in the
Python Shell window.
• Switching off the dialog box “Ok to Save?”
By default, IDLE will ask confirmation for Saving the file every time you run it. To have this
dialog box only the first time, goto Options>Configure IDLE>General and Select “No Prompt” in
the line: At Start of Run (F5). Now you can run your programs by pressing the function key F5.
Now only the first time you run your program, it will prompt you for a locations and filename to
save it, the next time it will use the same name automatically.
Note: Be careful not to use program names which are identical to modules that you import. So
avoid calling your program “python.py”, “numpy.py”, “math.py”, or any other name that also refers
to an existing module. Also for every new assignment or set of exercises make a new folder to
keep your files well organised.
Make sure to change the settings of file in the dialog box which will pop up the first time you run the file
to allow interaction with the Shell. Then you have similar features to which IDLE allows: checking your
variables in the shell after running your program or simply to testing a few lines of code.
Spyder has chosen to stop offering a standard Python console, so they only have an iPython console.
By tweaking the run setting per file, it is still possible to run your script in an external Python console.
But this would still be a reason to prefer PyCharm as it is more versatile and focused on Standard
Python applications not just the interactive iPython.
My advice would be to first keep it simple and use IDLE for the basics. Use the print() function and
the shell (to inspect variables) as debugger and occasionally www.pythontutor.com. Then later,
and only for larger or more complex problems switch to PyCharm or one of the multi-language IDE’s
(or optionally Spyder).
On the exam, only IDLE and Spyder will be available, unless specified otherwise in the course an-
nouncements.
A different environment especially for Scientific Computing, using iPython is Jupyter, which creates
1.3. Installing Python 9
Python Notebooks, a beautiful blend between a (LaTeX) document, spreadsheet and Python.
1.3.7. Documentation
IDLE, our default editor supplied with Python, has an option to Configure IDLE and add items to the
Help menu. Here a link to a file or a URL can be added as an item in the Help pull down menu.
The Python language documentation is already included,select them with F1, on-line they can be found
at
https://docs.python.org/3/
For Scipy and NumPy, downloading the .HTML files of the reference guides onto your hard disk and
linking to these files is recommended. They are available for download at:
http://docs.scipy.org/doc/
For Matplotlib both an online manual as well as a pdf is available at:
http://matplotlib.sourceforge.net/contents.html
Also check out the Gallery for examples but most important: with the accompanying source code for
plotting with Matplotlib:
http://matplotlib.sourceforge.net/gallery.html
For Pygame, use the online documentation, with the URL:
http://www.pygame.org/docs/
Another useful help option is entering ‘python’ in your favourite search engine, followed by what you
want to do. Since there is a large Python user community, you will easily find answers to your questions
as well as example source codes.
Other useful resources:
Search on Youtube for the course code AE1205 for some videos or the Python 3 playlist:
https://www.youtube.com/results?search_query=ae1205
Stackoverflow contains many answers and will often show up in your Google results: https://
stackoverflow.com/questions/tagged/python-3.x
A nice tutorial and reference can also be found at:
https://www.tutorialspoint.com/python3/index.htm
10 1. Getting started
Now select Run > Run Module. Depending on your settings, you might get a dialog box telling you
that you have to Save it and then asking whether it is Ok to Save this, so you click Ok. Then you enter
a name for your program like temperature.py and save the file. The extension .py is important for
Python, the name is only important for you. Then you will see the text being printed by the program,
which runs in the window named “Python Shell”:
Let’s have a closer look at this program. It is important to remember that the computer will step through
the program line by line. So the first line says:
The computer sees a print() function, which means it will have to output anything that is entered
between the brackets, separated by commas, to the screen. In this case it is a string of characters,
marked by a ” at the beginning and the end. Such a string of characters is called a text string or just
string. When the computer prints this to the screen, it will also automatically add a newline character to
1.5. Variables and the assignment statement 11
jump to the next line for any next print statement (unless you specify otherwise, which will be explained
later).
This is all that the first line does. The next line is slightly more complicated, and will be analysed in the
next section.
Exercise 1.4.2: Make a program that computes the overall resistance for the circuit shown below,
where your inputs are the three resistances (𝑅𝐴 , 𝑅𝐵 , 𝑅𝐶 ).
Different things are happening in this statement. The first part of the line, tempf =, is a so called
Assignment statement, indicated by the equals sign (=). It should be read as: “let tempf be”. In
general it has the following structure:
variablename = expression
In our example it tells the computer that in the computer’s memory a variable has to be created with
the name tempf. (If the variable name already exists, it will be just be overwritten by the result of the
expression at the end.)
To be able to do this, the computer first evaluates the expression on the other side of the “=” sign to
see what the type of this variable has to be. It could for example be a floating point value (float type)
or a round number (integer type), a series of characters (string) or a switch (boolean or logical). It then
reserves the required number of bytes, stores the type and the name. If the name already exists, then
this old value and type are cleared after the expression is evaluated.
The computer evaluates the expression. The outcome is stored in memory and can be used later
in other expressions by using the variable name on the left side of the equals sign. To do this, the
computer saves the name in a table associated with this module. This table contains information on
the type of variable and points to a computer memory address where the value is stored.
For the assignment statement a = 2, this table would look something like:
12 1. Getting started
name ’a’
type 4 byte Integer
memory address 1625981072
⋯ ⋯
Address Byte value
1625981072 0
1625981073 0
1625981074 0
1625981075 2
For numbers, three basic types of variables exist, each stored binary in a different way:
• Integers: 2
• Floats: 2.0
• Complex: 2.0+0j
Integers are whole numbers, used to count something or as an index in a table or grid. Floats are
numbers with a floating point and can have any real value. Complex numbers refer to the mathematical
definition of ‘complex’, which have a real part and an imaginary part. They are like floats but can have
an imaginary part next to the real part. Each type is stored in a different way.
Python looks at the expression to determine the type:
2 => integer type
-4 => integer type
3 * 4 => integer type
2.0 => float type
0. => float type
1e6 => float type
3 * 4.0 => float type
3 + 4j => complex type
In chapter 2 the assignment statement, as well as the different types of variables are discussed in
detail.
Now let us have a look at the expression in our example program. This is not a simple one. The
expression in our example has a combination of twice the following structure:
functionname(argument)
Python knows this is a function because of the parentheses. In our example case, there are two
functions: float() and input(). As the result of the input-function is needed as input of the float-
function, the input function is called first. In general, when functions are called in a nested way (a
function is placed inside of the parentheses of another function), the order of execution is from inside to
outside. Both float() and input() are standard functions included in the Python language. (Later
we will also use functions which we have defined ourselves!)
Most functions do some calculations and yield a value. For example, the abs(x) function returns the
absolute value (modulus) of x, and the function int(x) will try to convert the variable x to an integer
number. The int() function is one of the type conversion functions:
int(3.4) => integer with value 3
int(-4.315) => integer with value -4
float(2) => float with value 2.0
float(0) => float with value 0.0
But some functions are complete little programs in themselves. The input() function for example
does several things: it can be called with one argument (you can pass it a string with some text), which
will be printed on the screen, before the user is prompted to enter a value. When the user presses
enter, the value entered by the user is read, and this is returned in a string variable as the result by the
input function. So in our case, the text Enter temperature in degrees Fahrenheit: is printed, and the
1.6. Finding your way around: many ways in which you can get help 13
user enters something (hopefully a number). The text entered by the user is returned by input() as a
string, which in our example gets passed on to the float() function which tries to convert this string
to a floating-point value, to be stored in a memory location. We call this variable tempf.
The next line is again an assignment statement as the computer sees from the equal sign “=”:
Here a variable with the name tempc is created. The value is deduced from the result of the expression.
Because the numbers in the expression on the left side of the equals sign are spelled like “5.0” and
“32.0”, the computer sees we have to use floating point calculations. We could also have left out the
zero as long as we use the decimal point: 5. / 9. would have been sufficient to indicate that we want
to use floating point values.
When this expression has been evaluated, a variable of the right type (float) has been created and
named tempc, the computer can continue with the next line:
This line prints four things: a variable value, a text string, an expression which needs to be evaluated
and another text string, which are all printed on the same line as with each comma a space character
is automatically inserted as well. The round() function means the result will be rounded off.
Try running the program a few times. See what happens if you enter your name instead of a value.
1.6. Finding your way around: many ways in which you can get
help
1.6.1. Method 1: Using help(”text”) or interactive help()
If you wonder how you could ever find all these Python-functions and function modules, here is how to
do that.
There are many ways to get help. For instance if you need help on the range function, in the Python
shell, you can type:
help(”range”)
Which calls the help-function and uses the string to find appropriate help-information. You can also
directly pass a function or other object to help:
help(list)
You can also use help interactively by typing help(), without arguments, and then type the keywords
at the “help>” prompt to get help, e.g. to see which modules are currently installed.
>>>help()
help>math
And you will see an overview of all methods in the math module. There are some limitations to this
help. When you will type append, you will not get any results because this is not a separate function
but a part of the list object, so you should have typed
14 1. Getting started
>>> help(list.append)
Help on method_descriptor in list:
list.append = append(...)
L.append(object) -- append object to end
So simply Googling a question (or error message) with the keyword Python (or later NumPy) in front
of it as an extra search term will quickly bring you to a page with an example or an answer. For basic
questions this might bring you to the same documentation as in the Help-menu which is at https://
docs.python.org. You will also see that https://www.stackoverflow.com is a site that often
pops up, and where most questions you might have, have already been posed and answered:
1.6. Finding your way around: many ways in which you can get help 15
For instance, for the search query “python append to a list” you will find: https://stackoverflow.
com/questions/252703/python-append-vs-extend (Which shows that next to append()
there apparently is another method called extend() available, which works with a list as an argu-
ment and apparently appends each element to the list.)
In general, thanks to the interactive Python Shell window, simply trying statements or program lines in
the Python shell is a way to try what works as you intended. Or to check the value of variables after
your still incomplete program has run.
To find bugs in your program, for small programs https://www.pythontutor.com can be helpful
to see what is going on inside the computer when your program runs. And of course using the print
statement to see what a variable is, or where the computer gets stuck in your program, is always the
first thing to try.
Be prepared that in the beginning the level of frustration might be high, but so is the reward when your
program runs. And when you get experience, you will see how fast you can make working programs,
not because you won’t create bugs, but because you will immediately recognise what went wrong. The
nice thing of software is that it can always be fixed, so “trial and error” is an accepted way to develop
programs. So do not be afraid to try things and play around with Python to get acquainted with the way
it works.
Appendix C gives an overview of all of the Python statements and most important functions that are
covered in this course, with a short example for each item. Having a copy of these pages at hand may
be handy when starting Python. It will provide the keywords for you to look into the help-functions of
Google searches.
Although we already touched upon most basic elements of the Python language, we have not seen all
statements and types yet, so first go through the next chapters (discover the very useful while-loop, the
string type and its methods and the Boolean/logical and slicing of strings and lists etc.). Just glance over
these chapters to get the essence and then start making simple programs and study the paragraphs in
more detail when you (think you) need them. Programming has a steep learning curve at the beginning,
so good luck with making it through this first phase. Once you have passed this, things will be a lot
easier and more rewarding. Good luck!
2
Variable assignment and types
In this chapter we describe the building blocks which you need to write a program. First we have a look
at variables, which represent the way in which a computer stores data. But there are different types
of data: numbers or bits of text for instance. There are also different kinds of numbers, and of course
there are more data types than just text and numbers, like switches (called “booleans” or “logicals”)
and tables (“lists”). The type of variable is defined by the assignment statement; the programming line
giving the variable its name, type and value. Therefore we first concentrate the assignment statement
and then the different data types are discussed.
In this reader we’ll often use examples to introduce and explain programming concepts. The example
we’ll start out with in this chapter is a program to solve a second order polynomial equation. We will
use this example to discuss the use of variables in systematic fashion.
ABC solver
import math
D = b ** 2 - 4.0 * a * c
Before we start some notes about this program. Run this program and you will see the effect.
• note how ** is used to indicate the power function. So 5 ** 2 will yield 25, and 16 ** 0.5
will yield 4.
• the program uses a function called sqrt(); the square root function. This function is not a
standard Python function. It is part of the math module, supplied with Python. When you need
functionality of such external modules in your code, you can use the import statement at the
beginning of your code to make these modules accessible. The expression math.sqrt() tells
Python that the sqrt() function should be obtained from the imported math module. You’ll learn
more about using modules in chapter 5.
17
18 2. Variable assignment and types
• After you have run the program, you can type D in the shell to see the value of the discriminant.
All variables can be checked this way.
• Note the difference between text input and output. There is no resulting value returned by the
print function, while the input function does return a value, which is then stored in a variable.
The argument of the input-function calls is between the brackets: it’s a prompt text, which will be
shown to the user before he enters his input.
Integers
i = 200
a = -5
Floats, contain a decimal point or “e” to indicate the exponent of the power of 10:
Logicals:
Logicals
swfound = True
alive = False
forward = (v > 0.0)
onground = h <= 0.0 and abs(v) < eps
running = (forward or alive) and not onground
Strings
name = ”Ian ” + ”McEwan”
txt = ”I'm afraid I can't do that, Dave.”
s = 'HAL'
Python even allows multiple assignments in a single line, when the values are separated by a comma:
a, b, c = 1.0 , 3.0 , 5
a, b = b, a # swap two variables
What really happens is that the expression first becomes a type of list (a tuple to be exact) which is
then unpacked. The example below shows that this principle also works with a list, which is defined by
a series of variables within square brackets:
2.2. Short-hand when using original value with an operator 19
lst = [9 , 2 , 5]
a, b, c = lst
Mathematically speaking this statement would be nonsense if we would read the equal sign as ‘being
equal to’. But as an assignment statement, this simply evaluates the expression on the right side, which
is the content of variable i to which one is added, and the result is stored in the variable i again. The
effect is thus that i is increased with 1. Here are a few other example with the same logic:
As especially the increase or decreases with one occurs often, there is a shorthand notation for things:
i = i + => i +=
This short-hand is the same as in the programming language C (and C++), a language which is well
known for how short code can be written (often with an adverse effect on the readability). Also in this
example, the benefit is mainly for the programmer who saves a few keystrokes, (in Python) there is
no execution time benefit as the operations done are the same. This short-hand hence increases the
distance from pseudo-code. Whether you use it is a matter of personal preference.
0.30000000000000004
Often this is not a problem, but it should be kept in mind, that float are always approximations!
One special rule with floats in programming is that you never test for equality with floats. So never
use the condition “when x is equal to y” with floats, because a minute difference in how the float is
stored can result an inaccuracy, making this condition False when you expected otherwise. This may
not even be visible when you print it, but can still cause two variables to be different according to
the computer while you expected them to be the same. For example: adding 0.1 several times and
then checking whether it is equal to 10.0 might fail because the actual result is approximated by the
computer as 9.99999999 when it passes the 10. So always test for smaller than (<) or larger than (>).
You can also use “smaller or equal to” (<=) and “larger or equal to” (<=), but do not forget that a floating
point variable is always an approximation due to the binary storage limitations (which is different from
a decimal notation), so it may be off by a small value. A way to avoid it is to test for closeness:
Floating point values can also be rounded off using the round(x) or round(x, n) function. With
the second argument n, you can specify the number of decimals. When the second argument is not
used, the result will be rounded off to the nearest whole number and converted to the type integer
automatically.
>>> round(10 / 3)
3
>>> round(10 / 3, 4)
3.3333
>>> round(10 / 3, 0) # Explicitly specify zero decimals lets round
↪ return a float
3.0
2.5. Int: variable type for round numbers like counter or index)
Integers are variables used for whole numbers. They are used for example as counters, loop variables
and indices in lists and strings. They work similar to floats but division results will be of the type float.
2.5. Int: variable type for round numbers like counter or index) 21
For example:
a = 4
b = 5
c = a / b
d = a // b
print(c)
print(d)
In this example, the first print statement will print 0.8, but the second statement prints 0. The difference,
as mentioned before, is the difference between regular division (/) and integer division (//). The former
returns a float, whereas the latter returns an integer. Note also that with integer division, the result is
always rounded down.
A useful operator, commonly used with integers, is the ‘%’ operator. This operator is called the modulo
function, and calculates the remainder of a division. For example:
27 % 4 = 3
4 % 5 = 4
32 % 10 = 2
128 % 100 = 28
So to check whether a number is divisible by another, checking for a remainder of zero is sufficient:
if a % b == 0:
print(”a is a multiple of b”)
You can convert integers to floats and vice versa (since they are both numbers) with the functions
int() and float():
a = float(i)
j = int(b) # Conversion cuts of behind decimal point
k = int(round(x)) # Rounded off to nearest whole number
# ..and then converted to an integer type
The int() function will cut off anything behind the decimal point, so int(b) will give the largest integer
not greater than b (in other words, it rounds down). These functions int() and float() can also
take strings (variables containing text) as an argument, so you can also use them to convert text to a
number like this:
txt = ”100”
i = int(txt)
a = float(txt)
n = 20
i = 3
a = n / i
print(”a =”,a)
j = n // i # floor division
k = int(n / i) # rounded down and converted to int
m = round(n / i) # rounded to nearest integer value
print(”j,k,m =”, j, k, m)
a = 6.666666666666667
j,k,m = 6 6 7
As we can see from the missing decimal points in the second output line, the variable j, k, m all are
integers. This can also be verified in the shell using the type() function, which prints the type of a
variable:
>>> type(m)
<class 'int'>
c = 3 + 4j
print (”Length of”, c, ”is”, abs(c))
c = 3 + 0j
a = c.real
b = c.imag
print(”c has”, a, ”as real part and”, b, ”as imaginary part”)
>>> e = 2.718281828459045
>>> pi = 3.141592653589793
>>> e ** (1j * pi)
(-1+1.2246467991473532e-16j)
Which demonstrates Euler’s equation, and also shows the rounding off problems in the imaginary part,
which is practically zero. Both e and pi were binary approximations, as are all floats and complex
numbers.
txt = ”abc”
s = ””
2.8. Booleans and logical expressions 23
Using indices in square brackets [ ] allows you to take out parts of a string. This is called slicing. You
cannot change a part of string but you can concatenate substrings to form a new string yielding the
same effect:
c = ”hello world”
c[0] is then ”h” (indices start with zero)
c[1:4] is then ”ell” (so when 4 is end, it means until and not including 4)
c[:4] is then ”hell”
c[4:1:-1] is then ”oll” so from index 4 until 1 with step -1
c[::2] is then ”hlowrd”
c[-1] will return last character ”d”
c[-2] will give one but last character ”l”
c = c[:3] + c[-1] will change c into ”hel” + ”d” = ”held”
The string variable also has many functions built-in, the so-called string methods. See some examples
below (more on this in chapter 8).
For more methods see chapter 8, or section 5.6.1 of the Python supplied reference documentation in
the Help file.
Strings are stored as lists of characters. Internally, Characters are stored by the computer as numbers,
using the Unicode encoding. This encoding is an extension of the ASCII codes. See table 2.1:
ASCII codes can be derived from the corresponding character using the ord(char) function. Similarly,
the character for a given ASCII code i can be retrieved using the chr(i) function. As you may
have noticed in table 2.1, the first 31 and the last entry are ‘special’ characters. Most of these special
characters you will likely never need. Exceptions to this are the newline character, and the tab character.
These special can be easily entered into a string using an ‘escape character’: a backslash followed
by a character. In this way, you can add a newline to a string by adding '\n', and a tab by adding
'\t'. When you start reading text files from disk (see chapter 8), you might notice that newlines can
also be indicated with other characters: In Windows/DOS text files the newline is indicated by both
a Carriage Return and a Line Feed: chr(13) + chr(10). On Linux and Apple systems only the
newline character chr(10) is used.
0 NUL (Null) 32 SP 64 @ 96 ‘
1 SOH (Start of Header) 33 ! 65 A 97 a
2 STX (Start of Text) 34 ” 66 B 98 b
3 ETX (End of Text) 35 # 67 C 99 c
4 EOT (End of Transmission) 36 $ 68 D 100 d
5 ENQ (Enquiry) 37 % 69 E 101 e
6 ACK (Acknowledgement) 38 & 70 F 102 f
7 BEL (Bell) 39 ' 71 G 103 g
8 BS (Backspace) 40 ( 72 H 104 h
9 HT (Horizontal Tab) 41 ) 73 I 105 i
10 LF (Line Feed) 42 * 74 J 106 j
11 VT (Vertical Tab) 43 + 75 K 107 k
12 FF (Form Feed) 44 , 76 L 108 l
13 CR (Carriage Return) 45 - 77 M 109 m
14 SO (Shift Out) 46 . 78 N 110 n
15 SI (Shift In) 47 / 79 O 111 o
16 DLE (Data Link Escape) 48 0 80 P 112 p
17 DC1 (Device Control 1 (XON)) 49 1 81 Q 113 q
18 DC2 (Device Control 2) 50 2 82 R 114 r
19 DC3 (Device Control 3 (XOFF)) 51 3 83 S 115 s
20 DC4 (Device Control 4) 52 4 84 T 116 t
21 NAK (Negative Acknowledgement) 53 5 85 U 117 u
22 SYN (Synchronous Idle) 54 6 86 V 118 v
23 ETB (End of Transmission Block) 55 7 87 W 119 w
24 CAN (Cancel) 56 8 88 X 120 x
25 EM (End of Medium) 57 9 89 Y 121 y
26 SUB (Substitute) 58 : 90 Z 122 z
27 ESC (Escape) 59 ; 91 [ 123 {
28 FS (File Separator) 60 < 92 \ 124 |
29 GS (Group Separator) 61 = 93 ] 125 }
30 RS (Record Separator) 62 > 94 ^ 126 ~
31 US (Unit Separator) 63 ? 95 _ 127 DEL
found = False
prime = True
swlow = a < b
outside = a >= b
swfound = a == b
notfound = a != b # ( != means: not equal to)
outside = x < xmin or x > xmax or y < ymin or y > ymax
inside = not outside
out = outside and (abs(vx) > vmax or abs(vy) > vmax)
inbetween = 6.0 < x <= 10.0
Conditions are a special kind of expression used in statements like if and while to control the flow
of the execution of the program. In the above statements, often brackets are used to indicate that it is
a logical expression.
expect! So remember: Two equals signs will check the condition and one equals sign assigns an
expression to a variable name.
With combined conditions with many “and”, “or” and “not” statements use brackets to avoid confu-
sion:
not((x > xmin and x < xmax) or (y > ymin and y < ymax))
if inside:
print(”(x,y) is inside rectangle”)
Note that if inside basically means: if inside == True and similarly while not found
means while found == False. This is why Booleans are never compared with True or False as
this serves no function (for more information on if-statements, see section 3.4, for while-statements,
see section 3.6).
Creating lists
a = [2.3 , 4.5 , -1.0, .005, 2200.]
b = [20, 45, 100, 1, -4, 5]
c = [”Adama”, ”Roslin”, ”Starbuck”, ”Apollo”]
d =[[”Frederik”,152000.0], [”Alexander”,193000.0],
[”Victor”,110000.0]]
e = []
This would make the variable a a list of floats, b a list of integers, and c a list of strings. A list of lists
as defined in d is basically a way to create a two-dimensional list. Finally e is an empty list. Accessing
elements from the list is done as indicated below. Another way to make a list of numbers is using the
so-called range function. This is a so-called iterator function used primarily in loops, but it can also be
converted to a list. The range function can contain one two or even three arguments:
range(stop)
range(start, stop))
range(start, stop, step))
26 2. Variable assignment and types
In all these cases start is the start value (included), stop is the value for which the block is not
executed because for will stop when this value is reached. So it is an inclusive start but excludes stop.
Another option is to enter a third argument, to indicate a stepsize. The default start is 0, the default
step is 1. Note that range() only works with integers as arguments:
list(range(5)) gives [0, 1, 2, 3, 4]
list(range(5,11)) gives [5, 6, 7, 8, 9, 10]
list(range(5,11,2)) gives [5, 7, 9]
If we now print elements we can use the indices in many ways. Using one index, indicating a single
element:
lst[0] which holds value 1
lst[1] which holds value 2
lst[9] which holds value 10
lst[-1] last element, which holds the value 10
lst[-2] one but last element which holds the value 9
Here it can be seen that indices start at 0, just as with strings. And similar to strings, the function len()
will give the length of a list and thus the not to reach maximum index for the list. Slicing lists with indices
works in the same way as it does for strings, with three possible arguments: start, stop and step. Only
one index refers to one single element. Two arguments separated by a colon refer to start and stop. A
third can be used as step. If you look at the lists that were made in the previous paragraph about lists
you can see that:
a[1] will return a float 4.5
b[1:4] will return a list [45, 100, 1]
d[1][0] will return ”Alexander” (do you see the logic of the order of the in-
dices for the different dimensions: second element of main list, then first
element of that list?)
Adding and removing elements to a list can be done in two ways: the “+” operator, or the append and
extend functions:
a = a + [3300.0] will add 3300.0 at the end of the list
a = a[:-2] will remove the last two elements of the list (by first copying
the complete list without the last two elements, so not very
efficient for large lists)
a = a[:i]+a[i+1:] will remove element i from the list, when it’s not the last one
in the list
a = b + c will concatenate (glue together) two lists if b and c are lists
Another way is to use the del (delete command) and/or functions which are a part of the list class.
You call these functions using the dot notation: variablename.functionname() so a period be-
tween variablename and functionname. Some examples:
a.append(x) add x as a list element at the end of the list named a, equiva-
lent to a = a + [x]
a.extend(b) extend the list with another list b, equivalent to a = a + b
a.remove(3300.0) removes the first element with value 3300.0
del a[-1] removes the last element of a list named a
del a[3:5] removes the 4th till the 6th element of a list named a
a = a[:3] + a[4:] will remove the 4th element of the list named a
Slicing
2.9. The list type: an ordered collection of items 27
The last example line uses the slicing notation (which can also be used on strings!). Slicing, or cutting
out parts of a list is done with the colon notation. Slicing notation is similar to the range arguments:
start:stop and optionally a step can be added as third argument: start:stop:step. If no value
is entered as start, the default value is zero. If no value is added as end the default value is the last
element. The default step size is 1. Negative values can be used to point to the end (-1 = last element,
-2 is the one-but-last, etc.).
Using the original definition of the lst variable: lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
this will give:
lst[:3] first three element index 0,1,2: [1,2,3]
lst[:-2] all except last two elements
lst[4:] all elements except the first four: except elements nr 0,1,2,3
lst[::2] every second element so with index 0,2,4, until the end
a = 5 * [0]
print(a) # Would print [0, 0, 0, 0, 0]
a[1] = 1
print(a) # Would print [0, 1, 0, 0, 0]
This notation, however, does not work for nested (two-dimensional) lists. When you need this, you can
use a loop to construct your list:
a=[]
for i in range(3):
a.append(3*[0])
a[0][1] = 1
print(a) # Would print [[0, 1, 0], [0, 0, 0], [0, 0, 0]]
This is a safe way of constructing nested lists, as each new element of a is constructed from scratch.
The following would also work:
# You can also initialise your list with dummy values, which you replace
↪ afterwards:
a = [0, 0, 0]
for i in range (3):
a[i] = [0, 0]
If you are not sure, test a simple example by constructing your nested list, changing one of its elements,
and see the effect by printing the entire list. You can also use www.pythontutor.com to visualise
what is going on in the computer’s memory. In the above example we use a for-loop to execute a line
of code a fixed (in this case 3) number of times. You’ll learn more about for-loops in section 3.5.
28 2. Variable assignment and types
a = 3 * [3 * [0]]
print(a) # Would print [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
a[0][1] = 1
print(a) # Would print [[0, 1, 0], [0, 1, 0], [0, 1, 0]]
The result of the second print statement is probably not what you expected! In line 3 we only
change the second element of the first row, but surprisingly all rows (all lists) have their second
element changed into 1!
What happens is related to the fact that some types are mutable (lit. ‘can be changed’), and others
immutable (lit. ‘cannot be changed’). In Python, all basic types, like floats, integers, and booleans
are immutable. So in the following, variable a is copied into b:
However, aggregate types (i.e., combinations of multiple data, such as a list) are mutable, which
in Python means that they are passed by reference. This results in the following:
Here, b literally contains three times the original variable a. Changing the contents of a therefore
results in the same behaviour as the short-hand construction of nested lists:
a[0] = 1
print(b) # Would print [[1, 0], [1, 0], [1, 0]]
Have a look at this Python-tutor example for a visualisation of such construction and referencing!
a = [2, 10, 1, 0, 8, 7]
a.sort()
print(a) # Would print [0, 1, 2, 7, 8, 10]
As we can see the method sort() (you can recognise functions and methods by the presence of
parentheses) sorts the list a. This is a special function in that it changes the actual source list instead
of creating a sorted copy (this is called an in-place operation). Most methods will return the result of
the function as a new variable, and leave the original intact. Examples are the methods index and
count. Index will give the index of the first occurrence of the given value. Count will give the total
number of occurrences:
>>> a = [3,1,8,0,1,7]
>>> a.count(1)
2
>>> a.index(1)
1
2.10. Some useful built-in functions 29
>>> a.index(9)
How would you make a program which will print the index of the first occurrence but also avoids the
error message if it is not found by simply printing -1?
Exercise 2.9.2: Make a list of words input by the user (e.g. aeroplane company names) by using
the append() function, then print them below each other with the length of each word.
Exercise 2.9.3: You can also make a list which consists of different lists, each with a different
length, as shown here:
values = [[10, 20], [30, 40, 50, 60, 70]]
Print the length of each list and all the values it contains.
Exercise 2.9.4: We can also go in the other direction, and construct a list of lists from a list of
values and a list of lengths:
values = [10, 20, 30, 40, 50, 60, 70] lengths = [2, 2, 3]
Construct a list of lists, with each list i containing lengths[i] values from values. Print the
constructed list.
3.1. Assignment
In this chapter we discuss the basic Python statements: commands that you use in a program which
often do something with the data. A summary of all of the basic python statements covered in this
course can be found in Appendix C.
We already discussed the assignment statement at the beginning of the previous chapter. The assign-
ment statement is one of the simplest statements in Python:
Ë Ê
variablename = expression
This general statement is executed in the following order:
1. Evaluate expression, and store the result in memory
2. Point variablename to the area of memory created in step 1.
The type and value of the variable variablename is determined by the expression.
Note that it is possible to overwrite a variable, even when it is used in the expression on the right-hand
side of the equals sign. This is because the computer first evaluates the expression and only then
stores the result in the variable, overwriting anything that was previously stored in that variable.
Some examples of how you can use the assignment are given below:
a = 4.5 will: (1) create a float with value 4.5, and (2) point variable
name a to it
i = 100 will: (1) create an integer with value 100, and (2) point variable
name i to it
total = 0 will: (1) create an integer with value 0, and (2) point variable
name total to it
i = i + 1 will: (1) calculate the result of i + 1, store it in a new integer,
and (2) point variable name i to it
i += 1 equal to previous line
total = total + i will: (1) calculate the result of total + i, store it in a new
integer, and (2) point variable name i to it
total += i equal to previous line
txt = ”ea” will: (1) create a string containing ”ea”, and (2) point variable
name txt to it
31
32 3. Python syntax: Statements
txt = txt + ”sy” will: (1) evaluate the expression txt + ”sy”, store the result
(”easy”) in a new string, and (2) point variable name txt to
it
series = [] will: (1) create an empty list, and (2) point variable name se-
ries to it
series = series + [i] will: (1) create a new list, containing the contents of series,
appended with an additional element i, and (2) point variable
name series to it
Here it should be noted that a statement such as i = i + 1 may seem mathematically incorrect,
but in programming this simply means that the expression i + 1 is evaluated with the old value of
i, after which the variable i is updated to point to the result of the expression. This order of actions
basically means that ‘=’ means ‘will become’ instead of ‘is equal to’ (remember that for the ‘is equal
to’ comparison ‘==’ in Python). Also note the short-hand notation “+=”, which has been discussed
extensively in section 2.1 already.
Adapting our example program from the previous chapter, we add some logic. Edit the program to
match this example precisely, note the margin jumps (use TAB-key) in the if statement, which is
called indentation. Having the same margin, or indentation level, is what Python uses to mark blocks
of code that belong together, for example because they are part of an if-branch or a loop (see later
sections on for and while loops).
D = b ** 2 - 4.0 * a * c
if D < 0.0:
print(”This equation has no solutions”)
else:
x1 = (-b - sqrt(D)) / (2.0 * a)
x2 = (-b + sqrt(D)) / (2.0 * a)
Now the program first checks whether D is negative. If so, it will tell you that there are no solu-
tions.
• Note the structure and syntax(=grammar) of the if-else statement. A colon ( : ) indicates that
a block of code will be given, so it acts as a ‘then’. The block is marked by the increased level of
indentation of the code.
• When it jumps back to the margin of the beginning to indicate the end of this block of code, an
‘else’ follows, again with a colon and an indented block of code.
• It also uses a different way to import the sqrt function from the math module. Note the dif-
ference in syntax in both the line with the import statement as well as the line where the sqrt
function is used. Chapter 5 will explain the different ways to import modules.
3.2. The print statement 33
Exercise 3.1.2: Using the flow chart depicted below, make a program that determines the
maximum value of given numbers a, b, c and d as entered by the user, using a number of if-
else-statements to find the maximum value of these four variables.
start
Enter values
a, b, c, d
a > b?
max1 = a max1 = b
c > d?
max2 = c max2 = d
end
Exercise 3.1.3: Write a program that clamps a float input by the user to [-10, 10] and prints it.
Additionally, if the input is negative, the program should print a warning message.
# Method 1
print(”Hello world”)
# Method 2
print(”Hello”, ”world”, sep=” ”)
# Method 3
34 3. Python syntax: Statements
The above examples also use the use of the ‘sep’ and ‘end’ keyword, which can be used to use a
different separator character or a different end of line. Note how the hash symbol (#), indicates the
start of a comment: this symbol and everything after it is ignored by the Python interpreter when the
program is executed.
The print statement can print one or more texts, variables expressions and the outcome of functions.
Basically anything can be printed. Apart from generating output for the user, print is also very useful
when trying to find bugs in your program. Experienced programmers often put temporary print state-
ments after every so many lines of code to check if everything works as intended. These are then later
deleted or commented out with the hash symbol.
Other examples of valid print statements with other arguments than strings:
print(”abc”, file=fout)
print(”def”, file=fout)
f.close() # Close file
i = 5
j = 10
x = sqrt(7)
txt = ”Hello”
3.3. The input function 35
The above syntax is actually a short-hand version for using the format function which you can also use.
See the example below, using the same data:
# Strings
print(”Look at this: {}”.format(txt))
print(”Look {} times at this:{}”.format(i, txt))
The input function will always return a string. When another type of variable is needed as input, the
converter functions can be used to get the right type:
In some cases this might not work, for instance when we want to enter an expression (maybe even
using variable names). It will also not work when a list of values is entered by the user. In both cases
successful conversion can be achieved by using an alternative method, the evaluation function eval()
can be used for the conversion of the string. A call to eval() will evaluate any expression as an actual
Python expression, and determine the type in the same as the assignment function would do it:
The eval function could also be used in the examples above, but there is a risk of the type ending up
different from what you expected. Users will not always add a decimal point when entering a float for
instance.
Note the difference between print and input, in terms of the arguments they accept. While print
takes any number of arguments, which it combines together in a single printed output, separated by
spaces, input only takes one argument. There are different ways to get around this. The first is to
use print in combination with input:
Another approach would be to generate a single string of text before passing it to input:
The second option in the box above uses f-strings, see the previous section on formatting your print
output. This method is even more powerful than joining strings with the ‘+’ operator.
Ê Ë
if condition:
# Execute only when condition is true
Ì
statements
Here, the elements are:
1. The statement keyword if
3.4. Checking conditions: the if-statement 37
In this example above, only if the condition is True, the statements statement 1 and statement 2
are executed. If the condition is False it will automatically jump to statement 3 without executing
statement 1 and statement 2. The conditional lines of code are marked by increasing the margin,
which is called indenting the lines after the colon. Lines with the same indentation level, so the same
margin, are together called a block of code. In Python a code block is marked only by this same margin.
One indentation step is set to four spaces by default in IDLE but in principle any number of spaces will
do, as long as all statements in one block have the same indentation.
Because statement 3 starts at the beginning of the line, at the previous level of indentation again,
Python knows that this is the end of the conditional block. Note that one of the statements inside
the if-statement could be another if-statement, which would mean that another set of conditional
statements would be indented one more time (nested if-statements).
Optionally the if-statement can be expanded with an else-branch or one or more else-ifs which are
spelled as elif in Python. As many elif-branches can be added as necessary. Only one else can
be added, always at the end of the if/elif branch(es):
if condition:
# Execute only when condition is true
statements
elif condition2:
# Execute only when condition2 is true
statements
else:
# Execute only when all conditions are false
statements
An example:
If/elif/else branching
if x < xmin:
x = xmin
vx = -vx
print(” Ball left via left side of screen. ”)
38 3. Python syntax: Statements
x = x + vx * dt
In this example the condition x > xmax is only checked if x < xmin is False. Which probably is not
a problem in this case. In the above example the assignment statement to update the variable x is
always executed because it is not indented.
print(”Ready. ”)
If you try this code, you will see that it prints the numbers 0 to 9.
Ê Ë
for i in range(10):
# Print Hello!
Ì print(”Hello!”)
total = 0
for i in range(1,11):
print(i)
total = total + i * i
3.5. Iteration using a for-loop 39
print(total)
In this example, a variable total is initialised as zero just before the loop. Then, in every iteration of
the loop i * i is added to total. Since i runs from 1 until but, not including, 11 in this code, the result
is that total will be equal to 1*1 + 2*2 +3*3+ ... + 9*9 + 10*10.
Instead of using range() to generate a range of integers, you can also use a for-loop to iterate
directly over the contents of a list:
If you need to iterate over a range of floats, e.g., from 0.0 to 10.0, with a step of 0.1, you can also use
the following construction. This example will print these values:
for i in range(101):
x = i * 0.1
print(x)
Tip: When setting up loops (while or for), use print statements first (e.g. with a small number of
iterations) to see whether your loop does what you intend it to do.
lst = [1, 2, 3]
for a in permutations(lst):
print (a, end=” ”)
lst = [1, 2, 3, 4, 5]
(1, 2) (1, 3) (1, 4) (1, 5) (2, 3) (2, 4) (2, 5) (3, 4) (3, 5) (4, 5)
There are many more useful functions in itertools, such as cycle, product, etc. In the python shell,
type help(”itertools”) to see a complete overview.
Exercise 3.5.2: Write a Python program to construct the pattern shown below:
*
* *
* * *
* * * *
* * * * *
* * * *
* * *
* *
*
Exercise 3.5.3: Write a Python program that accepts a string and counts the number of digits
and letters in the string.
Because it evaluates a condition, while is just like if: it is used to control the flow of a program
based on a condition. The difference with if is that the condition checking in the case of while is
automatically repeated, until the condition becomes False.
Below is an example of a while-loop:
When you use this as a sort of “repeat until”, you have to prepare the condition which is tested by
while in such a way that the first time it will be True, so the loop is entered at least once. You can see
this in the second example below, where found is set to False first.
Another example:
3.7. Controlling a loop: break and continue 41
import math
found = False
while i <= vn and not found:
if n % i == 0:
found = True
i = i+1
if found:
print(n, ”is divisible by”, i)
else:
print(”No factors of”, n, ”found.”)
While can also be used to wait for a valid answer when using input:
choice = -1
while not(0 <= choice <= 3):
choice = int(input(”Enter your choice (1,2,3 or 0):”))
In this example the computer will keep asking the question until a valid choice 0, 1, 2, 3 is entered.
The two most important things need to be addressed when using while-loops:
• Often (not always) an initialization is required to make sure that the first time the condition in the
while-loop is True, such that the while-loop starts running.
• Within the while-loop, the block of code has to assure that the condition becomes False at some
point, avoiding an endless loop.
Using continue to break off a single iteration and move to the next iteration step
for i in range(100):
# ... Code to execute iteratively in our loop
if i % 4 == 0:
continue
# ... More code to execute in the loop, if continue is not called
Note that continue and break can be used in both loop-types: so both can be used in for- and
while-loops.
Using these two commands on a regular basis is considered to be a bad programming habit, as a clever
definition of the loop can avoid these unpleasant disruption of the flow of the program. Also, take into
account that an if-then statement could also do the job. The break and continue statements
however, can make the code more readable since less indentations are required compared to the if-
then statements.
Summarizing the difference:
continue skip the rest of the code in the block inside the loop and go to the line
with the while or for statement. So in case of a for-loop the next value
of the list will be used.
break jump out of the loop and proceed with the code after the loop
Programming language purists do not like these commands as they disrupt the flow of a program and
can be used to create real “spaghetti-code”, so only use them when there is no other neater way to
achieve the same goal, e.g. to avoid too many indentations in the margin.
Exercise 3.7.2: Write a program that checks the validity of a password input by the user. The
password must be between 8 and 16 characters long, and must contain at least one digit, one
lowercase letter, one uppercase letter, and one special character. The special characters are: !,
@, #, $, %, &, *, +, -, =, ?, �
, _, ~, and the space character.
4
Making your code reusable and readable
When writing a piece of code it is important to make sure your code looks understandable to the outsider
or to yourself when you look at the code in one year from now. You do not want to write code that you
can only use once, so get used to this and do it all the time. Some suggestions:
• At the top of your script, write a small summary explaining the function of your code. You can
use regular comment lines for this (indicated with the hash sign # at the beginning of each line),
but you can also use a so-called docstring. An example:
In addition to an explanation of what is in the file, the top of your file is also a good place to indicate
your name and the date of editing. This is especially relevant when you are working on a large
project with several people.
• Use empty lines to create “paragraphs” in your code, indicating which parts belong together.
Also use spaces in your line, they have no effect during runtime but can increase the readability.
• Use comment lines just before lines, or small blocks of code, indicating in plain language what
the meaning of the code is. Comments can be inserted when you start the line with a hash-
character: #. The hash-sign can also be used to add comments at the end of the line (such as
units of variables). An example:
If you have a line that becomes very long, continue on the next line. Python will recognise that lines
belong together automatically when such a line break is made within a set of parentheses, square
brackets, or curly brackets. If your statement doesn’t have such brackets, you can end the line with a
43
44 4. Making your code reusable and readable
backslash as a continuation character. Then Python will know that the next line needs to be added
behind this line before it executes the command.
Tip: Since it is often not very motivating to add comments afterwards, you can also do it in the other
way around: When you have to design an algorithm, you first have to decompose the problem into
smaller steps. Use comment lines to break your problem down in smaller steps and write in natural
language what each step should do. Then all you have to do is fill in the gaps between the comment
lines with the actual code and you have developed and commented your source code at the same
time.
An example of the same code, both badly and well formatted, is given below (In these examples, def
is the Python keyword used to define new (self-made) functions, and pass is a statement which does
nothing, sometimes used temporary before filling in final code):
Arguments:
- a, b, c: the coefficients of the quadratic polynomial
(floats or integers)
Returns:
- A list with 0, 1, or 2 solutions for x (floats)
'''
45
In this case the function is quite simple, but imagine a more complex function: You would never be able
to understand it or use it again a few months or years later, when coded in the format of the first example.
When you make a habit of doing this while you code, it not an extra effort and it even saves you the
effort of memorising which part does what and what the variables mean during the debugging.
The small summary at the beginning of your code is called the header of a source file. A very good
habit is to add this header at the beginning of each Python file. In this you can quickly describe the
purpose of the program or the inputs and output of a function for potential users. For this you often want
to use multiple lines of text. As stated above, you can do this with regular comments (using the hash
sign), but more common, and as you will see also more useful, is to use docstrings; special multi-line
strings, indicated with triple quotes.
An example with regular comments:
#
# Main module of BlueSky Open ATM Simulator
#
# Start this module to start the program
#
# Created by : Jacco M. Hoekstra (TU Delft)
# Date : September 2013
#
# Modification :
# By :
# Date :
#
”””
Main module of BlueSky Open ATM Simulator
46 4. Making your code reusable and readable
Modification :
By :
Date :
”””
By using docstrings instead of comments, Python will also recognise your header as documentation
for your file. Let’s say that your file is called ‘bluesky.py’, the following would work, and would print
your docstring as part of the help text:
help('bluesky')
Because of this integration with Python, docstrings are the de-facto way of documenting code. When-
ever you use help(), the text you see is also always specified as a docstring in the code of the
module or function you are calling help() for. This means that functions are documented in the same
way:
Remember that in Python, strings can be indicated both with single quotes ('A string'), and with
double quotes (”another string”). This is also the case with docstrings: you can either use three
single quotes or three double quotes. The only restriction, as with regular strings, is that you have to
end the docstring with the same set of quotes as you used to start it.
5
Extending Python: Using modules
Already in the first chapter we saw that although by default, Python already includes many functions,
the real power of Python lies in the huge range of available external modules. This chapter will describe
some of the core modules that are provided with Python by default.
import math
After importing, all of the functions, types, and variables that are defined inside of this module become
available through the dot-notation that we’ve previously already seen when we looked at object methods
such as list.append(). Modules work in exactly the same way. So when we want to use a function
from a module, we type the name of the module, math in this case, followed by a period (.), followed
by the name of the function. For example:
Method 1 to avoid this is explicitly importing selected functions from the module math:
47
48 5. Extending Python: Using modules
It is also possible to import everything from a module into the own namespace:
In the first example the names are explicitly imported. This has two advantages: the reader of the
source can tell from which module which function was imported. This is important if you use more than
one module. Secondly, you prevent problems, which might arise if you import from multiple modules
that define functions or variables with the same name (a common example that you will also run into
is the overlap between the math module and the more extensive numpy module, see chapter 10). a
function or variable name which happens to be the same as some function somewhere in the imported
module.
x = wortel(9)
More common is to create short-hands of modules and sub-modules for easy use later on in your
code:
import numpy as np
import scipy as sp
from matplotlib import pyplot as plt
These are just a couple of examples that are commonly abbreviated with the import ... as syntax.
For more information about these specific modules, see chapter 8 and chapter 10.
Constants
math.exp(x) Return 𝑒 𝑥
math.log(x) Natural logarithm of x (to base e), so ln(𝑥)
log(𝑥)
math.log(x, a) Logarithm of x to base a, so alog(𝑥) (equals )
log(𝑎)
math.log1p(x) Natural logarithm of 1 + 𝑥 (so to base e), but then accurate for
𝑥 near zero
math.log10(x) Base-10 logarithm of x. This is usually more accurate than
log(𝑥, 10)
math.pow(x, y) 𝑥 𝑦 : x raised to the power y (same as x**y)
math.sqrt(x) Square root of x. So this calculates √𝑥. (same as x**0.5)
Trigonometric functions
math.atan2(y, x) Same as atan(y / x), in radians but now the result is be-
tween −𝜋 and 𝜋. The vector in the plane from the origin to
point (𝑥, 𝑦) makes this angle with the positive X axis. The
point of atan2() is that the signs of both inputs are known to
it, so it can compute the correct quadrant for the angle
math.hypot(x, y) The Euclidean norm, √(𝑥 2 + 𝑦 2 ). This is the length of the
vector from the origin to point (𝑥, 𝑦)
Hyperbolic functions
Number functions
50 5. Extending Python: Using modules
def f(x):
return x * sin(x)
And a somewhat larger example of a function that calculates the Fibonacci series until value n:
51
52 6. Defining your own functions and modules
def fibo(n):
''' Generate the Fibonacci series until value n '''
series = [0, 1]
return series
Be aware that the def statement does not yet execute the function, it merely defines it. The code
inside the function will only be executed when you call the function, after it has been defined with def.
You can call your function as part of the rest of your code, but you can also call it manually in from the
Python console (e.g., the IDLE prompt, or ipython) to test your new function.
Here, myfun takes two arguments; a and b. These arguments become available as local variables to
the function body (the indented block of code that is executed when the function is called). This means
that these variables are only available to the code inside of the function. The following, for example, is
therefore not possible:
c = myfun(1, 2)
print(a, b) # This line breaks the code, and would give a NameError
↪ (name 'a' is not defined)
Note that it is also possible to create functions that have no arguments at all! The following would be
a perfectly valid function definition:
In this case, don’t forget to add the parentheses, they still need to be there, even though your function
has no arguments! This is true when you define your function, but also when you call it:
The result is that for our function ‘’increment()’, the ‘’step’ is now optional, and as a result, the
function can be called with either one, or two arguments:
Of course this function can be called in the regular way, by passing, in order, two, three, or four argu-
ments:
But let’s say that we’re fine with the default value for c, we only want to provide a different value for d.
In that case, what we could do is to pass argument d by name:
In its simplest form, the return statement doesn’t provide data back to the caller, but merely indicates
to Python that the end of the function has been reached:
In this simple example, no data is returned to the caller, and therefore return only indicates that the
function should end there. Note that in this case the use of the return keyword is not mandatory, the
following would also be valid:
In other cases, the purpose of your function is to calculate or do something based on the passed
arguments, and return the result. This case be a single return value, as in the previous examples:
but the nice thing about Python is that it also very easily allows you to simultaneously return multiple
values:
By specifying multiple return values, separated by a comma, you can use the return statement to re-
turn multiple values. When you call the function, you can store those results directly in multiple variables
(e.g., plus and minus in the example above), or you can store them in a single variable:
plusminus = plusmin(1, 2)
print(plusminus[0], plusminus[1])
This single variable is stored as a tuple, where you can access the individual contents using the
square-bracket notation.
Some more examples:
def getabc():
a = ”Hello”
b = ”World”
c = ”!”
return a, b, c # Defines a tuple (a, b, c) on the fly and returns it
def gettuple():
a, b, c = 1, 2, 3 # Notice the similarity with getabc!
return (a, b, c) # Explicitly create a tuple using parentheses
6.2. Variable names: Arguments and return variables 55
def getlist():
a, b, c = (3, 4), (4, 5), (5, 6)
return [a, b, c] # Instead of a tuple, you can also return a list
x = 1
3. Create the variable name ‘x’, and point it towards the newly created area of memory
What this means is that there is a difference between the variable name ‘x’ and the data it points to.
This also becomes apparent when you pass variables as arguments to a function. Take for example
the following code:
>>> x = 1
>>> y = 2
>>> fun(x, y)
Here, we define a function that takes two arguments; ‘a’ and ‘b’, that are accessible within the function,
when the function is called. We then define two so-called global variables; ‘x’ and ‘y’, giving them
integer values 1 and 2.
What happens when we call our function like this: fun(x, y), is not that we are passing variables x
and y with name and all, but rather we only pass the data which variables x and y point to:
x = 1 fun(x, y)
‘x’: ‘a’:
Reference Reference
Type: int
Data: 1
Similarly, when we call a function that performs some calculation, stores this in a local variable, and then
returns this local variable, only the data indicated by this local variable is returned, not its name:
56 6. Defining your own functions and modules
>>>x, y = plusmin(1, 2)
‘x’:
Type: int
Reference Data: 3
This also means that, after calling the function, its local variables (in the above example r1 and r2)
are no longer available:
>>> x, y = plusmin(1, 2)
>>> print(r1, r2) # This would raise an error!
x = 1
y = 2
print(f'Address of global variable x: {id(x)}')
z = test(x, y)
print(f'Address of global variable z: {id(x)}')
However, in the end the behaviour you see is similar to languages that also pass by value, because
Python distinguishes between immutable and mutable types (lit. unchangeable and changeable).
When you pass an immutable variable to a function, you have access to its data, but are not
allowed to change it. When you do need to make a change you automatically end up creating a
new variable, making it similar to passing by value. To demonstrate:
def test(a):
print(f'Address of argument a: {id(a)}')
a = a + 1
print(f'Address of argument a after change: {id(a)}')
test(1)
6.3. Returning a result by updating an argument 57
In Python, most basic types are immutable: int, float, bool, and tuple. An example of a
mutable type in Python is list:
def addtolist(lst):
lst.append(1)
mylst = []
addtolist(mylst)
print(mylst)
# Wrong way
def increment(a):
a = a + 1
x = 5
increment(x)
print(x) # Would still print 5, the increment() function only changes
↪ local variable a.
The right way of implementing this function is by using the return statement:
def increment(a):
return a + 1
x = 5
x = increment(x)
print(x) # Will print 6 as expected
Mind you, even for objects of mutable types (such as Python’s list), this use of an argument as out-
put of the function is considered very bad programming practice in the procedural programming style in
languages where this is possible. It only becomes more relevant when considering object-oriented pro-
gramming (see chapter 13), where methods can be defined that behind the scenes update the data of
the object itself. The syntax of the above example would then be something like a.increment(). You
also already know examples of this in-place updating, such as the list.append() method!
You can use this same principle yourself, by defining functions in different files, and importing those files
when you need them. This becomes especially relevant for large programs where you want to keep
an oversight by storing different sets of functions in different files, or as we say in Python: in different
modules. To make things easy for yourself your new module file needs to be in the same folder so
Python can easily find it.
58 6. Defining your own functions and modules
main.py mytools.py
import mytools def myfunc(a):
b = a * a - 3.0 * a
y = mytools.myfunc(10) if b < 0:
b = a * a + 3.0 * a
return b
Suppose you have defined a function fibo() in the file series.py , and you want to use it in your
own program test.py. Then you need to import your module series. We usually do this at the
beginning of the source, since you need to do this only once; once the function is defined, you can
use it as many times as you want. There are now many ways in which you can import and call this
function:
Method 4 is discouraged: you do not know which other names now suddenly have a special meaning,
it also makes it harder to trace where functions are defined. I would personally only use it when I am
testing something (e.g., with math or numpy) in the Python interactive shell.
On the internet you can find many 3rd party modules which extend the functionality of Python to do
a range of things. Once they are installed it is very easy to use them: just add an import call at the
beginning of your script and all functions inside that module are available to you. Most of these modules
(especially the popular ones) are very easily installed using pip:
Note that on Linux and macOS you might have to add sudo in front of pip. Try this when you get
errors when you try without sudo.
Exercise 6.4.2: Make four functions which calculate respectively the surface and volume of a
sphere and the surface and volume of a cone.
The function should repeat string 𝑛 times, separated by the string delim.
Here, string is a user-defined sequence of words separated by a delimiter delim. The function
should split the string into a list of words, find the longest word, and then print this word and its
length. Hint: to split a string by a delimiter, use string.split(delim).
7
File input/output and string handling
As soon as your done with the file, you need to release it again with the close statement:
f.close()
g.close()
h.close()
This will not only release the file for use by other programs but also empty any read/write buffers which
may be in use and whose content might get lost if the program ends abruptly without closing (and
releasing) the files.
61
62 7. File input/output and string handling
function. Note that in this case, you can also leave this second argument out, as read-access is the
default way of opening a file for the open() function.
For read access, there are different functions that you can use, with varying control over how much
data you get from the file with each read action.
The first way we’ll discuss uses the readlines() method of the file object. As its name suggests, this
method reads (all) lines from your file. It returns a list of strings; one string for each line read. Using
readlines() you can do the following:
If the for-loop is the only place where we use the output of readlines(), we can simplify this exam-
ple:
Even simpler is to skip the readlines() method all together, and directly iterate over the file ob-
ject:
Note that you can also process headers and comments with an additional if-statement in your read
loop! For an example of this see section7.2.3
In other cases, even structuring of the file into individual lines is not what you want. If that’s the case
you can use the read() method to read the entire file, or a specified number of characters into one
string:
Suppose we want the program to read this file and store the result in a two-dimensional list with strings
for the label and floats for the value per variable. So in this example, the text file should result in the
following list:
[['alpha', 5.2240000000000002],
['beta', -0.0112],
['gamma', -2.7759999999999998]]
• When no value is provided (as in the example with theta), it should also not appear in the list.
• Empty lines should have no effect, similarly whitespaces in the lines should not be critical (see
gamma in example)
As a first step, we open the file in read-mode, and create a loop to iterate over its lines (see section 7.1).
Inside our loop we’ll start out by cleaning up our line, and making our data processing code case-
insensitive. For this we use two methods of Python’s string type: strip() and lower(). Just like the
append() method of list, you call a variable’s methods using the dot-notation:
line = line.strip().lower()
What happens with an expression like this is that it is evaluated from the inside outwards. In this
case, first line.strip() is evaluated. This generates a new string object, which is not stored in
a variable, but instead immediately used in a next expression: <new string>.lower(). This
generates another new string which is stored in our updated variable line.
Our next step is to distinguish between comment lines and data lines. We can do this with an if-
statement:
The next step is to process each line that contains relevant data. When lines get more complicated,
as in our example, the trick is to find the easiest way to separate the relevant elements of a string
from the parts you want to discard. In our example there are two recurring characters that delimit our
relevant data: the equals sign between the variable name and value, and the square left-bracket that
indicates the unit. With a combination of split() and find() we can extract our variable name and
value:
# Split into the part before and after the equals sign
words = line.split('=')
The end result is that our for-loop goes over every line in our data file, skips empty lines and comment
lines, extracts a name and a value from each data line, converts the value to a float, and stores the
name/value pair in a list.
Writing to a file
xtab = [...]
ytab = [...]
with open('test.dat', 'w') as fout:
for i in range(len(xtab)):
# This writes xi,yi<newline>
fout.write(f'{xtab[i]},{ytab[i]}\n')
As shown here, be sure not to forget the newline! Otherwise all of your data will end up on one
line.
Similar to readline() and readlines(), Python also provides a writelines(), to write a list of
strings to a file in one operation:
lines = [
”Dave Bowman: Hello, HAL. Do you read me, HAL?\n”,
”HAL: Affirmative, Dave. I read you.\n”,
66 7. File input/output and string handling
See also section 3.2 for more information on formatting your output.
You can, of course, also store the result of these methods in a new variable:
str.replace(old,new) Return copy of str with all occurrences of substring old replaced by
new
str.join(lst) Return one string, with the strings from the list, separated by the char-
acter in str
str.startswith(ch) Returns True if string str begins with the substring ch
str.endswith(ch) Returns True if string str ends with the substring ch
str.split(sep) Returns a list of strings: the words in str as separated by sep substring
str.splitlines() Returns a list of strings: str is split using the newline character
str.strip(ch) Remove character ch from begin and end of str, default ch is a space.
See also rstrip() and lstrip() for stripping only one side of str
str.expandtabs(n) Replace tab-character by n spaces. If n is not provided 8 spaces are
used.
7.4. Useful string methods 67
str.lower() Returns copy of string str with all alphabetic characters in lower case
str.upper() Returns copy of string str with all alphabetic characters in upper case
str.title() Returns Copy Of String str But Then In Title Case, Like This
str.capitalize() Returns copy of string str with the first character capitalized
str.islower() Returns True if all characters in the string str are lower case
str.isupper() Returns True if all characters in the string str are upper case
str.istitle() Returns True If The String str Is In Title Case, Like This
str.isalpha() Returns True if all characters in the string str are alphabetic characters
str.isdigit() Returns True if all characters in the string str are digits
str.isalnum() Returns True if all characters in the string str are digits or alphabetic
str.isspace() Returns True if all characters in the string str are whitespace
The next step is the initialisation of our variables. In an XY-plot, each data point of each graph consists
of an x and a y value. Pyplot expects the individual coordinates in separate sequences, so we’ll create
lists for x, sin(x), and cos(x), respectively:
69
70 8. Matplotlib: Data visualisation in Python
These three lists we’ll fill with ranges of x, cos(x) and sin(x) data. Since we want to perform the same
calculation for a fixed number of times we’ll use a for-loop for this. Because the range() function
only works with integers we need to do some scaling to generate our x-values:
xtab.append(x)
y1tab.append(y1)
y2tab.append(y2)
With our data created in three lists the only step left is to actually plot the data. We can do this
with the plot() function from pyplot. To actually reveal the generated plot window we should call
show().
0.25
0.50
0.75
sin(x)
1.00 cos(x)
0 1 2 3 4 5 6
x [rad]
Along the bottom of the plot window you can find the following buttons to interact with the graph:
• Home button: brings you back to default figure as shown in the beginning
• Back button: return to previous plot when you’ve panned or zoomed
8.2. Multiple plots and setting their scaling: subplot and axis 71
• Save: Select a file format and filename to save the plot as currently set up in the window
fig = plt.figure()
sub = fig.add_subplot(2, 1, 1)
...
sub.set_title('For some reason I only want to add a title to my
↪ subplot much later!')
http://matplotlib.sourceforge.net/gallery.html
In this gallery, click on a figure and then select “source code” to see how the figure is made. Ignore all
the complex stuff where they generate the data and check for useful Matplotlib methods in a figure or
a subplot to enhance the way your graphs look. Also the different types of graphs like bar charts are
illustrated.
Exercise 8.1.2: With a simple cosine function you can already make beautiful flowers in python.
Make a flower using the function 𝑟 = 4 cos(2𝜙) in polar coordinates.
8.2. Multiple plots and setting their scaling: subplot and axis
Instead of several lines in one plot, you can also include more than one plot in a figure window with
the subplot() function. In the example below, we want to show the standard trigonometric functions
sine, cosine and tangent in the top row and the hyperbolic functions in the bottom row. This is done
by including the command subplot. For example, to create the top-left subplot in a set of two rows and
three columns, you could call:
plt.subplot(2, 3, 1)
This tells pyplot that we are working on a figure with two rows and three columns of subplots, and that
the next plot we make with plt.plot() should fill the first (top-left) plot.
72 8. Matplotlib: Data visualisation in Python
plt.subplot(231)
One of the functions we want to plot is the tangent. Because this function has vertical asymptotes,
plotting its values over the full x-range would yield very high y-values. To control the range of the axes
from within our program for the third subplot, we can use the axis command:
This will set the x-axis at a range of [−2𝜋, 2𝜋] and the y-axis at [−5, 5]. This shows the shape of the
tangent in a more sensible way.
The full example:
xtab = []
y1tab = []
y2tab = []
y3tab = []
y4tab = []
y5tab = []
y6tab = []
xtab.append(x)
y1tab.append(sin(x))
y2tab.append(cos(x))
y3tab.append(tan(x))
y4tab.append(sinh(x))
y5tab.append(cosh(x))
y6tab.append(tanh(x))
plt.subplot(232)
plt.plot(xtab,y2tab)
8.2. Multiple plots and setting their scaling: subplot and axis 73
plt.title('cos')
plt.subplot(233)
plt.plot(xtab,y3tab)
plt.axis([-2.0 * pi, 2.0 * pi, -5.0, 5.0]) # setting
↪ scale:xmin,xmax,ymin,ymax
plt.title('tan')
plt.subplot(235)
plt.plot(xtab, y5tab)
plt.title('cosh')
plt.subplot(236)
plt.plot(xtab, y6tab)
plt.title('tanh')
You can see in the tangent plot that the asymptotes are nearly straight vertical jumps in the data. You
could draw the tangent graph in such a way that it will not connect these points. You can even add red
dashed lines to represent the asymptotes in your graph, when you know where they are. How would
you do this? Check out the examples and the Matplotlib reference documentation for this. (Another
way might be to draw an independent line in graph for each period!)
74 8. Matplotlib: Data visualisation in Python
Exercise 8.2.2: Plot 𝑟 = 𝜙 and 𝑟 = 𝜙 2 on the interval 𝜙 ∈ [0, 8𝜋] in two graphs. Make sure that
you label all your axes and give titles to the plots.
Exercise 8.2.3: Sometimes it is convenient to plot straight lines in your plot. It works almost the
same as changing your axis system. You use the function plot():
Interactive plot
from math import *
import matplotlib.pyplot as plt
for i in range(100):
x = float(i)/10.
plt.plot(x,sin(x),”ro”) # plot each point as a red dot
plt.draw() # Show the updated result
plt.close()
When run, this example will show data as it is being generated and adjust axes on the fly when neces-
sary. Note that here, pyplot.draw() is used, which you can see as the interactive-plotting equivalent
of pyplot.show().
Exercise 8.3.1
Plot the two functions 𝑦1 = √4 − 𝑥 2 and 𝑦2 = −√4 − 𝑥 2 on the interval 𝑥 ∈ [−2, 2] as an interactive
plot and see which shape appears.
the numpy module. It’s fine to ignore them for now, and leave them for what they are. The numpy
module will be introduced in more detail in chapter ??.
Both examples plot the function 𝑧 = 𝑓(𝑥, 𝑦). For this they use a 2-dimensional array with a grid,
generated with a function called “meshgrid”, to generate the values at an equal distance. You could
convert these examples of source code to a function which plots your own data or your own function.
You should then only change the data X,Y and Z. To do this in a neat way, you could convert these
examples to a function where the X,Y and Z are input arguments to that function.
The 3D plot code:
# 3D surface plot
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1,
cmap=cm.RdBu,linewidth=0, antialiased=False)
# Legend
fig.colorbar(surf, shrink=0.5, aspect=5)
# Show figure
plt.show()
The resulting figures are shown below. When you run the code of the 3D plot, you can move around
the plot to change the viewing angle before saving it to an image file. Both plots also use a colour map
cm.
Exercise 8.4.1
𝑥2
The deflection of a wing can roughly be approximated by 𝑧 = . Make a 3D plot of the deflection
1000
of the wing. The wing is rectangular with a span of 40 m and a chord of 8 m. Take the span as
your x-direction, the deflection in the z-direction and the chord in the y-direction.
”””
Show four flight log files on Google maps window
May 10th, 2014
Jacco M. Hoekstra
”””
import numpy as np
import pygmaps
import os
# Read data
fdata = np.genfromtxt(fnames[i], comments=”#”, skiprows=2)
The resulting map will look like this when you open it in your browser:
78 8. Matplotlib: Data visualisation in Python
8.6. Overview of essential pyplot functions 79
plt.bar(x, y) Create a bar plot. Here, the x argument should give the horizontal
placing, so a list of integers (e.g. arange(5). y is a list/array with
the same number of values indicating the height of each bar
plt.show() Show graph in interactive window (put this at the end of your plot
commands or you will not see anything)
plt.axis(lst) Set range of axes, uses a list (or tuple) with the values
[xmin, xmax, ymin, ymax]
plt.xlabel(str) Use text in str as label on x-axis. Example with text formatting:
plt.xlabel('time [s]', fontsize=14, color='red')
plt.ylabel(str) Use text in str as label on y-axis
plt.title(str) Use text in str as title on diagram
plt.legend() Show a legend in your graph. For example, when your plot has two
graphs: plt.legend([”y1”, ”y2”])
plt.legend(loc=locstr) Keyword argument loc can be used to indicate the location of the
legend within the plot. Possible options are:
”upper right” ”center left”
”upper left” ”center right”
”lower left” ”lower center”
”lower right” ”upper center”
”right” ”center”
plt.subplot(231) Used when creating a figure with several subplots: Specify number
of rows (2), number of columns(3) and finally set the figure number
for next pyplot commands (1st figure in this example)
The graphs below should look very familiar to you: analytical integration of a function. The example here
is as simple as it is common: a constant acceleration, integrated once and twice to obtain respectively
velocity and position as a function of time.
𝑥̈ 𝑥̇ 𝑥
𝑡 𝑡 𝑡
1
𝑥̈ = 𝑎 𝑥̇ = ∫ 𝑎 ⋅ d𝑡 𝑥 = ∫ 𝑎 ⋅ 𝑡 ⋅ d𝑡 = 𝑎 ⋅ 𝑡2
2
In this chapter we’ll instead look at numerical integration; which is what you use to integrate a function
when you can’t, or don’t want to use the analytical approach.
81
82 9. Numerical integration
̇ 2)
𝑥(𝑡
̇ 1)
𝑥(𝑡
𝑡 𝑡 𝑡
̇ 1 )+𝑥(𝑡
𝑥(𝑡 ̇ 2)
𝑥(𝑡2 ) = 𝑥(𝑡1 ) + 𝑥(𝑡
̇ 1 ) ⋅ Δ𝑡 𝑥(𝑡2 ) = 𝑥(𝑡1 ) + ⋅ Δ𝑡 𝑥(𝑡2 ) = 𝑥(𝑡1 ) + 𝑥(𝑡
̇ 2 ) ⋅ Δ𝑡
2
Although for the linear function in the example above, the midpoint method gives an exact solution, for
numerical integration of more complex functions each of these three methods has an error, of which
the magnitude depends on the step size Δ𝑥. In a typical program, the easiest to implement of these
three methods is Euler’s Backward method, since it allows you to use the most up-to-date values of
your integrands:
∑ 𝐹𝑖 # Determine acceleration
𝑎𝑖+1 = # from forces
𝑚 a = Ftot / m
# Integrate acceleration
𝑣𝑖+1 = 𝑣𝑖 + 𝑎𝑖+1 ⋅ Δ𝑡 # to obtain next speed
v = v + a * dt
# Integrate speed to
𝑥𝑖+1 = 𝑥𝑖 + 𝑣𝑖+1 ⋅ Δ𝑡 # obtain next position
x = x + v * dt
These integration steps can be iteratively implemented in a loop. This is typically a while-loop, with
as terminating condition for instance an end time, a target position, or something similar. When your
goal is to plot (integrated) variables as a function of time in a pyplot graph, you can initialise an empty
list before starting the integration loop, and append to it with each step of the integration:
ttab = []
vtab = []
xtab = []
The complete simulation will almost always have the following structure:
start
initialise model
initialise simulation
False
continue? Plot results
True
Num. integration: a, v, x
Use this structure as a template for all your simulations: add the descriptions in the block as comment
lines and then fill these in with real code between the comment lines.
Exercise 9.1.1
Make a program to implement a simulation of a falling mass.
𝑦 = 𝑦0
𝐹𝑔𝑟𝑎𝑣 = 𝑚𝑔
While y remains larger than zero (until the mass hits the ground), repeat the next iterations:
𝑡 = 𝑡 + 𝑑𝑡
𝐹 = 𝑚⋅𝑔
𝑎 = −𝐹/𝑚
𝑣𝑦 = 𝑣𝑦 + 𝑎 ⋅ 𝑑𝑡
𝑦 = 𝑦 + 𝑣𝑦 ⋅ 𝑑𝑡
During each step, append the value t to a table for the time and append the value of y to a
table with the value of y. After all iterations, when the mass has reached the ground, plot the
value of the height y against the time t, using these tables.
b) Add drag to your sum of forces. Use 𝐷 = 𝐶𝐷 1/2𝜌𝑉 2 𝑆, with: 𝐶𝐷 = 0.47, 𝜌 = 1.225kg/m3 ,
𝑆 = 𝜋𝑅 2 , and 𝑅 = 0.15m.
c) Compare the two different datasets in one plot to see the effect of the drag. Try different
values for starting altitude and 𝐶𝐷 .
# Determine acceleration
∑ 𝐹⃗𝑖 ax = Fxtot / m
𝑎⃗𝑖+1 =
𝑚 ay = Fytot / m
# Integrate acceleration
vx = vx + ax * dt
𝑣⃗𝑖+1 = 𝑣⃗𝑖 + 𝑎⃗𝑖+1 ⋅ Δ𝑡 vy = vy + ay * dt
# Integrate speed
x = x + vx * dt
𝑥⃗𝑖+1 = 𝑥⃗𝑖 + 𝑣⃗𝑖+1 ⋅ Δ𝑡 y = y + vy * dt
It gets more complicated, however, when drag is added to the calculation. Because drag is non-linearly
related to the airspeed, the total drag has to be calculated first, before it can be decomposed into its 𝑥
and 𝑦 components.
This decomposition can be calculated by first calculating the direction of the speed vector. For this there
is a very convenient, special arctan-function called atan2(y,x). Look at the following example:
Here we see that first the length of the speed vector is calculated. This is then used to calculate the
length of the drag force. Then the angle is calculated using a special arctan-function atan2(). Had we
9.2. Adding dimensions and the atan2 function 85
used the normal atan() function, there would have been no different outcome for –𝑣𝑦 / − 𝑣𝑥 and 𝑣𝑦 /𝑣𝑥 ,
even though the two vectors point in opposite direction. This is caused by the fact that the tangent,
being the quotient of sine and cosine, has a period of only 𝜋 and not 2𝜋.
To be able to move from Cartesian coordinates (𝑥, 𝑦) to polar coordinates (𝑟, 𝜙) without an extra if-
statement for checking the sign of 𝑥 and 𝑦, atan2() can be used as in the example. This function
performs a quadrant check using the individual signs of the 𝑥 and 𝑦 values passed to the function.
Because of this it works for all quadrants! An illustration of these two coordinate systems, and an
overview of the quadrant rules for the arc tangent are given below.
𝑦 𝑦
𝑥
Point to identify Point to identify
𝑦 𝑟
𝑥 𝑥
𝑦
⎧arctan( 𝑦𝑥 ) if𝑥 > 0
⎪arctan( 𝑥 ) + 𝜋 if𝑥 < 0 and 𝑦 ≥ 0
⎪ 𝑦
arctan( ) − 𝜋 if𝑥 < 0 and 𝑦 < 0
𝜙= 𝑥
𝜋
⎨2 if𝑥 = 0 and 𝑦 > 0
⎪− 𝜋 if𝑥 = 0 and 𝑦 < 0
⎪ 2
⎩0 if𝑥 = 0 and 𝑦 = 0
Exercise 9.2.1
Make a small simulation of a cannon ball which has a mass of 10 kg, that is fired with a starting
speed of 100 m/s, but it is shot at an angle of 30∘ . Besides that, the canon is placed one meter
above the ground and you can assume that this is also the starting height of the cannon ball.
a) Make the simulation as described above. Show a plot of the vertical distance against the
horizontal distance. So you can see the trajectory of the cannon ball.
b) Now extend this program, because this model has too many simplifications. For example
a cannon ball is not frictionless, so we should include the drag. For now we assume it is
constant and N is set to for example 80. Try to find the accelerations with force equilibrium
and plot the trajectory of the ball.
c) The last step to finalise our program is that the drag is not constant. It is actually a function
of speed, in this program use the relation that 𝐹𝑑 = 0.05𝑉 2 . From Intro-I you know that the
gravitational constant changes with altitude, also include this effect in your program. Hint:
𝑅
𝑔 = 𝑔0 𝐸𝑎𝑟𝑡ℎ , where 𝑅𝐸𝑎𝑟𝑡ℎ = 6371 km.
𝑅𝐸𝑎𝑟𝑡ℎ +ℎ
10
NumPy and SciPy: Scientific
programming with arrays and matrices
The modules NumPy and SciPy have provided users of Python with an enormous range of engineering
and scientific computing tools. Many of this has been inspired by the functionality of MATLAB and its
provided toolboxes. The syntax and names of functions are often identical.
Of the two, NumPy can really be seen as Python’s foundation for scientific computing. With array- and
matrix-types as well as a large range of linear algebra functions it forms the basis for SciPy, Matplotlib
and many other modules. On top of NumPy, SciPy adds a whole range of sometimes very dedicated
scientific computing and engineering functions. Thanks to NumPy and SciPy, Python has become the
default language of choice for scientific computing, according to IEEE and many others. Let’s explore
the capabilities of these modules, even though we can only scratch the surface in this course.
To use these modules, they need to be imported. It has become standard practice to rename them
when importing them. NumPy becomes “np” and SciPy becomes “sp”. This means we will often see
the following header in our scientific applications. In this course, we assume you have imported the
modules as follows:
import numpy as np
import scipy as sp
import matplotlib as mpl
import matplotlib.pyplot as plt
In the NumPy and SciPy documentation it is often even assumed that you have imported everything us-
ing from numpy import *. So do not forget to type np. before the NumPy functions and sp. before
the SciPy functions, even though you don’t see this in the NumPy and SciPy documentation.
This would also be a good time to add a link to the Help chm-files containing the NumPy and Scipy
reference to your IDLE Help menu, if you have not already done so. Go to Options>Configure IDLE,
click on the “General”-sheet on the top right. Then click “Add” in the lower part of the window to add
the menu item and the link to these files.
10.1. Arrays
So far we have seen lists and even lists of lists. When we used lists as tables we often kept separate
columns in separate one-dimensional lists, so we could select them individually (like xtab, ytab in the
87
88 10. NumPy and SciPy: Scientific programming with arrays and matrices
previous chapter). And if we used a two dimensional table, we could add small lists of two elements to
the lists like this:
for i in range(1000):
x = x + vx * dt
y = y + vy * dt
postab.append([x, y])
But how can we now select only the first column? Unfortunately, postab[:][0] does not give this
result. This indicates, as well as many other examples, that two dimensional lists are, as a variable
type, despite their versatility, not always the most suitable type for scientific computing and working
with large tables in a fast and user-friendly way.
The module NumPy has an advanced ‘table’-type which solves this: the array. This type forms the
foundation for NumPy and SciPy. With it you can do computations with entire tables, as easy as if they
are one scalar variable.
Look at the example below:
import numpy as np
import matplotlib.pyplot as plt
plt.show()
As you can see, NumPy arrays can be used integrally in equations: they can be multiplied with each
other, or with a scalar, and they can be used in NumPy functions like np.sin(), np.exp(),
np.sqrt(), etc. All functions and operators, work on an element-by-element basis with arrays.
You can also create an array from an existing list, or grow an array with its append() function:
np.array(lst) Create a NumPy array from an existing (nested) list.
np.append(array_in, values) Create a new array based on array_in, with
values appended at the end.
Note that in contrast to list.append(), the np.append() function copies the entire original array
for each append. This has as a drawback that it is slower, especially for large arrays.
# List append
xtab.append(x)
The effect is more than a different syntax. The np.append() function makes a copy of the array with
the value appended to it. In the example above, because xtab is on both sides of the equals sign, the
original variable is overwritten. The end effect is therefore equal to list.append(). The real penalty
however is the decrease in execution speed. With large quantities of data, making a copy takes extra
time which can slow down your code immensely. There are two better alternatives, that you can use,
depending on the situation:
1. Start out by creating your data in a (nested) list. When it is complete convert it to a NumPy array:
arr = np.array(lst).
2. When you already know the size of the array, generate an array of the right size with
np.zeros(shape) (e.g. np.zeros(10) or np.zeros((4, 4)), and change its individual
values using indexing: arr[1] = 10.
As you can see, with nested lists you add a set of square brackets in your expression for each nested
list you want to access: a[i] accesses the outermost list to obtain row i, after which you get the j𝑡ℎ
element of the row: <row i>[j]. With NumPy arrays on the other hand, you index all dimensions
of your array within one set of square brackets. For a two-dimensional array this is arr[i_row,
j_column].
Because with lists Python stores two-dimensional tables as lists of rows, it is possible to access whole
rows, but not whole columns. NumPy arrays do allow accessing whole columns, Using the [:,j] slice
illustrated above. In the same way you can also slice very specific subsets from an array:
Besides the slightly different notation for multi-dimensional arrays, compared to lists, slicing otherwise
works in exactly the same way. Have a look at section 2.9.2 if you feel you could use a refresher on
slicing!
Exercise 10.1.2: Make a function, when you give a certain n x n array (2D) you get the main
2 5
diagonal, so e.g. if you have 𝐴 = [ ], we want our function to print 2, -1. Make sure this
4 −1
function can work for all n x n matrices.
Exercise 10.1.3: Make a small program that gives the first column of a 2-dimensional list and
also make a program that does this for an array. Do you see the difference and how much easier
arrays are for this use.
import numpy as np
Logical expressions, which you know from sections 2.8 and 3.4, can also be expressed using array
comparisons, and arrays of booleans. Take for example two floating point arrays a and b:
10.2. Logical expressions using arrays 91
The following logical expressions result in arrays of boolean values, as shown in the middle col-
umn:
Expression Resulting array Explanation
a > 0 [False, True, True, Per-element greater than zero com-
False, False, True] parison for elements in a
(a > 0) * (a < 10) [False, True, True, Multiplication with booleans works
False, False, False] as AND
(a < 0) + (a >= 10) [False, False, False, Summing booleans works as OR
True, True, True]
a[a > 0] [6, 3, 22] Using a boolean array as an index:
Selects elements of a where a>0 is
True
b[a > 0] [20, 30, 60] Selects elements of b where a>0 is
True. For this to work, a and b need
to have the same size.
np.where(a > 0)[0] [1, 2, 5] Return a list of indices of where con-
dition (a > 0) was True. (The [0]
refers to the first, and in this case
only, dimension of indices. In two-
dimensional tables there would also
be a list of indices which can be re-
trieved by [1])
1 - True = False 1 - 1 = 0
NOT !, 1− 1 - False = True 1 - 0 = 1
not True = False 1 - 1 = 0
There are different ways to use logic in a program which you have vectorised with NumPy. Imagine we
want a behaviour comparable with the unvectorised code below:
import numpy as np
temp = []
for i in range(len(h)):
if h[i] < 11:
temp.append(tropo[i])
else:
temp.append(strato[i])
# This ...
temp = (h < 11) * tropo + (h >= 11) * strato
Using the conditional indexing to only select the values for which the condition is True and gluing these
arrays together with append:
Do not confuse this use with the other function of the where-function: when used with only a condition
it returns arrays with indices of where it was found to be true.
𝑥 2 ∀𝑥 ≤ 1
Exercise 10.2.2: Plot the discontinuous function 𝑓(𝑥) = {
6 − 𝑥∀𝑥 > 1
Exercise 10.2.3: Extract from the array [3, 4, 6, 10, 24, 89, 45, 43, 46, 99, 100]
with Boolean masking all the numbers:
• which are not divisible by 3
• which are divisible by 5
• which are divisible by 3 and 5
• which are divisible by 3 and set them to 42
The examples below also time the execution time of both approaches.
t0 = clock()
for i in range(n):
V = 100.0 + 250.0 * rnd()
hdg = 360.0 * rnd()
vntab.append(V * cos(radians(hdg)))
vetab.append(V * sin(radians(hdg)))
dt = clock() - t0
print(”Lists took”, dtlst, ”seconds”)
n = 10000
dtarr = clock() - t0
The result show that with 10000 elements the NumPy method is about 11 times faster than the list
method! And also the NumPy code looks cleaner.
The difference between the two approaches is that when you apply a function or an operator on the
complete array, or the complete vector, the looping over the elements is handled by low-level code
inside NumPy. The reason why this is faster is twofold:
• the loop takes place in the fast, compiled low-level code of NumPy
• list elements can have different types, while all array elements always have the same type, this
saves the computer from checking the type for each element and this saves execution time
94 10. NumPy and SciPy: Scientific programming with arrays and matrices
Changing the code from treating each element to a complete array (or vector) at once, is called vec-
torising your code. In general, it is a good rule of thumb that when you can vectorise your code, you
should. Sometimes lists are easier or the lists are not that large that it is needed. But for large quantities
of data with the same type, vectorising is nearly always a good idea.
Using two-dimensional arrays and the transpose function, vectorising can be really powerful for geo-
metrical calculations, see the example below:
import numpy as np
print(”Dist = ”)
print(dist)
dx =
[[ 0 10 -23 1]
[-10 0 -33 -9]
[ 23 33 0 24]
[ -1 9 -24 0]]
Dist =
[[ 0. 53.93514624 31.82766093 6.08276253]
[ 53.93514624 0. 45.27692569 47.85394446]
[ 31.82766093 45.27692569 0. 28.8444102 ]
[ 6.08276253 47.85394446 28.8444102 0. ]]
The reshape operation of x and y is necessary to turn these arrays into two-dimensional arrays, with a
size of one along one of its dimensions. While this doesn’t seem to change these arrays (they are still
sized 4 × 1), without this the transpose operation on the following lines (x.T and y.T) would actually
not work: in a one-dimensional ‘world’, transposing has no meaning!
To illustrate the transposition done by x.T:
>>>print(x)
array([[12],
[ 2],
[35],
[11]])
10.4. Matrix operations: Linear algebra with NumPy 95
>>>print(x.T)
array([[12, 2, 35, 11]])
Note that after transposition, x.T is still a two-dimensional array (you can tell this from the double
square brackets).
As you can see in the output of the program, the expression dx = x - x.T results in a 𝑛 × 𝑛 matrix,
in which dx[i,j] equals x[i] - x[j].
The downside of using vectors this way with a lot of data is that all intermediate answers also become
vectors. With large quantities of data, you can easily consume a lot of memory this way. In that sense,
vectorising sometimes means exchanging speed for memory usage. A good way to avoid running out
of memory is therefore to delete arrays that are used only for intermediate results. But together with
some linear algebra, you can speed things up enormously.
import numpy as np
You can also calculate the dot-product between two vectors. The result is a scalar, which is equal to
𝑎⃗ ⋅ 𝑏⃗ = |𝑎||
⃗ 𝑏|⃗ cos(∠𝑎, 𝑏).
Since Python 3.5, there is also the @ operator, with which you can tell Python to calculate a matrix- or
dot-product:
import numpy as np
x = a@b
You can use any of the above three methods that you prefer most. Note, though, that when doing matrix
multiplications, the shape of a vector (and therefore the placement of the nested square brackets)
matters: np.array([[1, 2]]) creates a row vector, while np.array([[1], [2]]) creates a
column vector. If you need to convert between the two you can use the transpose attribute (.T) of an
array:
Below is an overview of the most common matrix operations and their NumPy implementation:
y = A.dot(x) # or
Matrix product: 𝑦⃗ = 𝐴 ⋅ 𝑥⃗ y = A @ x
Extra: np.matrix(): the old way of doing linear algebra with NumPy
Previously, you would define your data as a NumPy matrix (np.matrix()) instead of an array to
indicate that arithmetic with this variable should behave in the algebraic way, instead of element-
wise. However, this type has been deprecated since several years. It is currently still present in
NumPy, but users are advised to use arrays instead.
Exercise 10.4.2: A motorboat makes an upstream trip (i.e., against the current) of 29.5 km on
a river in 3 hours and 10 minutes. Returning the same trip with the current takes 2 hours and 6
minutes. Find the speed of the motorboat and the current speed. Try to solve this with a system
of equations and python.
10.5. Genfromtxt: Easy data-file reading using NumPy 97
Exercise 10.4.3: Make a linear trend line 𝑦 = 𝑏1 + 𝑏2 𝑥 from the ten points with coordinates
shown below. Use the least squares method to calculate the coefficients for this trend line.
𝑥𝑖 = 0.1𝑖, 𝑖 = 1, 2, 3, … , 10
𝑦𝑖 = 5𝑒 −𝑥𝑖 + 2𝑥𝑖
Here we see that the different columns of the two-dimensional array table are copied in independent
one-dimensional arrays x, y1 and y2.
There are many arguments you can pass to numpy.genfromtxt():
As you can see from this list of arguments, numpy.genfromtxt() provides the ability to load data
from a text file, with missing values handled as specified. Above the default values for the keywords
are shown.
Each line past the first skiprows line is split at the delimiter character, and characters following the
comments character are discarded.
A selection of useful arguments with explanation:
fname file object or str File or filename to read. If the filename extension
is gz or bz2, the file is first decompressed.
dtype data type, optional Data type of the resulting array. If None, the
dtypes will be determined by the contents of each column, individu-
ally.
comments str, optional The character used to indicate the start of a comment.
All the characters occurring on a line after a comment are discarded
delimiter str, int, or sequence, optional The string used to separate val-
ues. By default, any consecutive whitespaces act as delimiter. An
integer or sequence of integers can also be provided as width(s) of
each field.
98 10. NumPy and SciPy: Scientific programming with arrays and matrices
skip_header int, optional The numbers of lines to skip at the beginning of the
file.
skip_footer int, optional The numbers of lines to skip at the end of the file
SciPy functionality
10.6. SciPy: a toolbox for scientists and engineers 99
On the internet, many more modules, which use SciPy and NumPy can be found. For nearly all fields
of science and engineering, modules with many tools are available for free.
C
C Output theta in radians to block response of elevator
C Elevator = 0.2 radians between 1 and 2 seconds
C Boeing 737
C timestamp in seconds
C
0.0 = -0.00122299949023
0.1 = -0.0148544598502
0.2 = -0.00128081998763
0.3 = -0.00912089119957
...
19.7 = 0.0375150505001
19.8 = 0.0133852241026
19.9 = 0.0195944297302
When we plot this data we get this figure, showing a very noisy signal:
100 10. NumPy and SciPy: Scientific programming with arrays and matrices
Noisy data
0.150
0.125
0.100
0.075
0.050
0.025
0.000
0.025
0.0 2.5 5.0 7.5 10.0 12.5 15.0 17.5 20.0
In this plot we can see the original signal, to get this line we can try a polynomial fit, to get a signal
more close to the original signal. Note how in this program we use the function genfromtxt(from
NumPy to read the data into a two-dimensional array with one program line! Then we select the two
columns.
To fit the data we use the function polyfit, which returns the polynomial coefficients, in this case set
to a 10th order polynomial. The result is then written to a file.
plt.plot(xtab, ysmooth)
plt.plot(xtab, ytab, ”r+”)
plt.title('Polynomial 10th order')
plt.show()
with open(”filtered.log”,”w”) as g:
g.write(”C\n”)
g.write(”C Data smoothed by fitting a 10th order polynomial\n”)
g.write(”C\n”)
for i in range(len(xtab)):
line = f”{xtab[i]} = {ysmooth[i]}\n”
g.write(line)
The resulting plot shows both the power and limitation of polynomial fitting:
10.6. SciPy: a toolbox for scientists and engineers 101
The curve is indeed a smooth polynomial. The disadvantage is that there are limitations to how a
polynomial can fit the data points. Even when a higher order is chosen (10th order seems to be working
for a smooth curve with 8 local minima/maxima) it does not give a useful result outside the interval. So
while it is very useful for interpolation, it should never be used for extrapolation.
More advanced methods take into account a certain assumed relation of which the parameters will then
be estimated. The least squares method, which minimises the square of the error, can be used for this
and is made available by SciPy.
∗
Tuples, dictionaries, and sets
11
In the previous chapters we learned about different kinds of variables. Some relate to single (scalar)
values, such as float, int, and bool, other variables are actually collections of variables. The
collections we’ve already seen are Python’s list, str (text string), and the NumPy array. In this
chapter we’ll extend this with the built-in collection types tuple, dict, and set.
11.1. Tuples
Python’s built-in type tuple we’ve actually already glanced at in chapter 6, where it was shown that in
Python, tuples can be used to return multiple values from a function.
A tuple can be seen as a list that cannot be modified after it is created: it is immutable (just like
strings). The variable can be overwritten by a new tuple, but individual elements stored in a tuple can-
not be assigned a different value, nor can elements be added or removed. This is the only difference
between lists and tuples! When creating lists and tuples, Python distinguishes between them as fol-
lows: to create a tuple, enclose the values that should be stored in the tuple by round (normal)
parentheses. As you already knew, a list is created when square brackets are used. With a tuple
it is also possible to omit the parentheses, so two valid ways to create a tuple are:
origin = (0, 0)
pos = 3, 4
If you want to call a function, passing an anonymous tuple (d1 in the example below) as one of the
arguments you always need to use the syntax with parentheses. For named tuple variables this is not
necessary (d2 in the example below):
If you would omit the brackets in the second line, Python would think you call the function dist with four
arguments.
It seems like a tuple is a list with a limitation, so what are they used for? Tuples can be seen as
multi-dimensional constants. So for instance if you want to specify a named RGB-colour by its red-
green-blue components you could use the following assignment to defines these colours for later calls
to a graphical library:
black = (0, 0, 0)
white = (255, 255, 255)
brightred = (255, 0, 0)
red = (127, 0, 0)
cyan = (0, 255, 255)
103
104 11. Tuples, dictionaries, and sets
We’ve also already seen that matplotlib.pyplot.legend() uses a tuple for the legend text,
Although this legend()-function can also be called with a list. In the next chapter about Pygame,
tuples are used for colors and positions.
11.2. Sets
There are many more list-like types. In most cases the list type will work for you. But sometimes
a special list-like type can be convenient. Sets are lists used for unordered collections of unique
elements. It is used to check membership of a collection, overlaps of collections, etc.
Sets can be defined using curly brackets:
a = {1, 2, 5, 3, 6, 7, 0}
For a complete overview, including the operators, type “help(set)” in the Python shell.
11.3. Dictionaries
Another special variant of the list that can sometimes be very useful is the dictionary. Dictionaries are
like lists, where instead of using an integer index to access a value, you use a key to look up a value.
Dictionary keys can variables of any immutable type (e.g., str, int, float, or tuple), although string
keys are most common. An example of using a dictionary is given below:
ages[”Jet”]
KeyError: 'Jet'
>>>
This error can be prevented by first checking whether the “Jet” key is present in this dictionary:
if ”Jet” in ages:
print(ages[”Jet”])
We can also add entries in the way that you would intuitively expect:
ages[”Jet”] = 30
Even though the order is not defined you can still iterate over dictionaries. The following code
for a in ages:
print(a)
will print all keys. Then again, if you wanted not the keys but the entries, you can use one of the
following:
import pygame as pg
The init()-call has no immediately visible effect, but it is required to avoid having to initialise each
107
108 12. Pygame: Animation, visualisation and controls
import pygame as pg
Or the same in one line (mind the double brackets, see section ?? for more information on tuples):
The function pygame.display.set_mode() returns a Surface, which is stored here in the variable
name screen (could have been any name). In this case the surface refers to the video memory of the
screen, so we have called this variable screen in our example but it could have been any other name
like win, window1, scr, etc. In our example screen is now a surface we can draw on.
In computer graphics, a coordinate system different from mathematics is used, mainly for historical
reasons. So for Pygame get used to the following: The top left corner is the origin, x-coordinates
are from left (x=0) to right (x=horizontal pixel size of window), and y-coordinates are from top (y=0) to
bottom (y=vertical pixel size of the window). This coordinate system stems from the old text terminals,
which had line zero and column zero in the top left. So the window, which we just created, has the
following coordinate system:
x-coordinate
(0, 0) (600, 0)
y-coordinate
screen
So screen coordinates work in a non-standard way and are also always integers. For these two reasons,
it is common to define your own so-called world coordinates (with a right-handed reference frame),
which you use for your model. Only at the final drawing step (just before you draw) you convert them to
12.3. Surfaces and Rectangles 109
screen coordinates. An added benefit is that your drawing code doesn’t rely on a particular window size
in pixels anymore, but can scale with a resized window, or when changing from windowed to full-screen
mode. An example of a simple 2D world coordinate system is:
x-coordinate
(0, 0) (600, 0)
(0, 1) (1.2, 1)
y-coordinate
screen
(0, 0) (1.2, 0)
In this case we can do calculations using any position in floats, like do numerical integration in floats
and then only when we draw convert our float world coordinates to the integer screen coordinates just
before plotting with the following lines of code. In the example below the world coordinates are (𝑥, 𝑦)
and are converted to screen coordinates (𝑥𝑠 , 𝑦𝑠 ):
xmax = 600
ymax = 500
reso = (xmax, ymax)
screen = pygame.display.set_mode(reso)
...
while running:
x = x + vx * dt
y = y + vy * dt
xs = int(x / 1.2 * xmax)
ys = ymax - int(y * ymax)
When you paste one surface onto another, this is called blitting in the computer graphics world. It stems
from the “block of bits” of the video memory which is transferred with one call to a blit function. Before
we can do this we also need to specify where we want this rectangular image to be pasted.
110 12. Pygame: Animation, visualisation and controls
When we have loaded an image from the disk, we need to know how large it is and where we should
be able to position it onto another surface later. This is where Pygame’s Rect (rectangle) type comes
in. A rectangle is not the actual surface but rather an object that contains the dimensions and position
of a Surface. It has several members, which we can assign values to. The neat thing is that we do
not need to worry about the bookkeeping: when you change one value, the other ones will be changed
automatically when needed. One simple example is given below. To clear the screen, we draw a black
rectangle. For this we need to specify the position and scale first. If we get the Rect from a surface
to measure the size, the position is by default set to (0,0) for the top left corner. To do this, we use the
method get_rect() of a Surface (see the section on Surface in the Pygame documentation for a
full description of the methods of surface).
black = (0, 0, 0)
scrrect = screen.get_rect()
pygame.draw.rect(screen, black, scrrect)
A Rect has the following members, which you can read or assign a value to:
Center is a tuple equal to (centerx, centery). The user can choose to position the rectangle using
any combination of these members.
Another example where the Rect type is used is given below, where an image is loaded from a file
on the hard disk. Generally, we do this loading from a file only once at the beginning of our program,
because accessing the hard disk has a huge execution speed penalty. The example shows how to use
the rectangle: first we get the size from the surface object and then we position it and use it to blit the
surface on the screen surface:
ship = pygame.image.load(”rocket.gif”)
shiprect = ship.get_rect()
...
while running:
shipx = shipx + vx * dt
shipy = shipy + vy * dt
shiprect.centerx = shipx
shiprect.centery = shipy
In the code we can see how the rectangle is used to position the bitmap, which was read from
rocket.gif, on the screen. The same call can be used with a tuple containing the coordinates
of the top-left corner of the surface. As visible in the syntax, blit is a method from the Surface type,
hence it is called as a method, so with the dot-notation, combined with the destination surface (i.e., the
variable named “screen” in our example).
...
while running:
...
shiprect.center = (xs, ys)
scr.blit(ship, shiprect)
As said before, it is important to load the bitmaps before the actual game loop. In general, accessing
files (e.g., to load an image) takes a lot of time. This means it might cause hick-ups or delays in your
program, if you do this during the loop.
Sometimes bitmaps do need some editing before they can be used. When blitting images on a surface a
transparent background is often required. This can be edited with a painting program such as Paint.net,
Gimp, Paint Shop, Corel Draw or Photo shop and deleting the background so it becomes transparent.
Save the file in the PNG format (or GIF) as these formats allow transparent backgrounds. Also use
these programs to change colors, size or perform rotations.
If you need one bitmap in a lot of different orientations, editing it with a paint program can be cumber-
some. These operations can also be done much easier with pygame in your program. This can be
done using the transform module which allows you to manipulate bitmaps/surfaces. Some examples
of functions available in pygame.transform:
pg.transform.flip(surf, xs, ys) flip vertically and horizontally, returns new sur-
face
pg.transform.scale(surf, (w, h)) resize to new resolution, returns new surface
pg.transform.rotate(surf, angle) rotate an image, angle is float in degrees, ro-
tate an image, returns new surface
pg.transform.rotozoom(surf, angle, filtered scale and rotation, angle float in de-
scale) grees, scale factor also float, returns new sur-
face
pg.transform.scale2x(surf) specialized image doubler, returns new sur-
face
pg.transform.smoothscale(surf, (w, h)) scale a surface to an arbitrary size smoothly,
returns new surface
Be aware that you do not scale or rotate inside the game loop unless it is absolutely necessary. In most
cases it pays off to generate different surfaces for all possible orientations beforehand, store them in a
list and just use the appropriate one during the game loop by setting the index with the angle rounded
off to 45, 20 or 5 degrees. The same goes for scaling. Transforming bitmaps is rather computationally
intensive and in general it is the goal to do any time consuming operation as much as possible before
the actual running of the game loop.
The coordinate system used is explained in the section on setting up your screen. It runs from (0, 0) in
the top left corner to the maximum x and y in the bottom right. All sizes and positions are integers.
Colours are specified with a tuple (red, green, blue), three numbers each ranging from 0 – 255 to
indicate the amount of each colour channel present in the mixed color. For readability it helps to define
a few colours at the beginning of your program and use these in the calls:
black = (0, 0, 0)
cyan = (0, 255, 255)
white = (255, 255, 255)
background = (0, 0, 63)
foreground = white
pygame.display.flip()
If you have used the default settings when creating the window with pygame.display.set_mode(),
a call to flip updates the window with the video memory. When you use the option double buffering,
you have two areas of video memory, so two surfaces which will be used simultaneously: one will be
shown on screen, the other you can draw on. Once you’re done drawing the frame, you swap the
function of both surfaces. The advantage is that you do not need to start with an empty frame then, but
can use the one-but-last frame as a start. In practice, to take advantage of this you might need a lot of
bookkeeping but it might result in a higher execution speed.
Using the full-screen option is often only done when the game or simulation is ready, debugged and
tested, since debugging is severely hampered by a crashing full screen application!
12.7. Timing and the game loop 113
import pygame
pygame.init()
black =(0, 0, 0)
ship = pygame.image.load(”rocket.jpg”)
shiprect = ship.get_rect()
shiprect.centerx = 250
pygame.quit()
Now when we run this, it could turn out that our ship moves too fast or too slow. To fix this, we will then
adjust the third argument of the range function in the for-loop, currently set to -2. However, on another
computer the speed would again be different. Also, when another application in the background needs
the CPU for a short period, our rocket will hamper before continuing. This is because we have no
control over the real speed of the rocket. For this we need more than controlling the position, we need
to control the timing in our game as well. Or at least measure it and calculate the elapsed time (time
step) since we last drew our ship, so that we can calculate the required new position with the speed
and right time step based on the elapsed time since the last frame.
There are two principles we can use to make sure our simulated time runs in accordance with the real
time. These will be discussed in the following sub-sections.
# Define constants
tsim = 0.0
tstart = 0.001 * pg.time.get_ticks()
dt = 0.1
...
running = True
while running:
trun = 0.001 * pg.time.get_ticks() - tstart
if trun + dt >= tsim:
114 12. Pygame: Animation, visualisation and controls
tsim = tsim + dt
vx = vx + ax * dt
vy = vy + ay * dt
x = x + vx * dt
y = y + vy * dt
The advantage of this method is that you have a fixed and known time-step. This allows for more
advanced numerical integration methods and guarantees the stability of your simulation. When using
a variable time step (see method II) there is a risk of getting a too large time step, which can result in
overshoots and even a runaway of your simulation.
A disadvantage of this method is that it needs a time-step of which you can be sure that the com-
puter can always keep up with in terms of computing power. If you make this time step too small, the
simulation may lag behind or run at a variable speed, resulting in quirky speeds and movements.
However, the opposite, having a time-step that is much larger than the computer needs to perform a
single simulation cycle is also unwanted: in this case, the code above would repeatedly and very rapidly
run through the while-loop, until it reaches the desired target time. While this effectively amounts to
doing nothing for a while, it does mean that your CPU is loaded to its maximum while running over
the while-loop. To solve this you can use a built-in function that asks your CPU to sleep (literally do
nothing) for a while, making it available for other processes on your computer. The adapted example
below uses the sleep() function from the built-in time module to achieve this:
# Define constants
MINSLEEP = 0.001 # Minimum interval to sleep the CPU
tsim = 0.0 # Simulation time
tstart = 0.001 * pg.time.get_ticks()
dt = 0.1
...
running = True
while running:
# First increment the simulation time
tsim = tsim + dt
# To avoid high CPU usage, calculate remainder to sleep.
remainder = tsim - (0.001 * pg.time.get_ticks() - tstart)
if remainder > MINSLEEP:
time.sleep(remainder)
import pygame as pg
# initialise clock
pg.init()
t0 = 0.001 * pg.time.get_ticks()
...
maxdt = 0.5 # time step limit to avoid jumps
running = True
while running:
t = 0.001 * pg.time.get_ticks()
dt = min(t - t0, maxdt) # set maximum limit to dt
if dt>0.0:
t0 = t
vx = vx + ax * dt
vy = vy + ay * dt
x = x + vx * dt
y = y + vy * dt
...
running = True
while running:
...
...
if y <= 0.0:
running = False
...
Note that you can also use break here instead of setting running to False, but this can be less
traceable, especially when your game loop becomes large.
In general within a game loop we see the following elements:
• check for time step
• get input from keyboard, mouse or any other source
• update model with numerical integration
• draw new frame
• check for quit events
116 12. Pygame: Animation, visualisation and controls
Before the game loop our model is initialised, graphics are loaded and set-up and our simulation is
initialised. We have seen how to control time, how to numerically integrate and how to draw a frame.
But we still need to know how to process input from keyboard and/or mouse.
pygame.event.pump()
keys = pygame.key.get_pressed()
The first line is required because of the way Pygame handles events, like keys being pressed. The
second line collects all key states in a long list of logicals. Each key has a fixed position in this list of
logicals. When this logical is True, the key is currently held down and when the key is not pressed,
the logical is False.
So imagine we want to check for the Escape key, how do we know which logical to check? Pygame
has a list of variables (integers) with indices for every key (check https://pygame.org/docs/ref/
key.html) that you can use. For example to check the Escape key we use (after the storing the logical
in our variable keys with the above piece of code):
if keys[pygame.K_ESCAPE]:
running = False
Some examples for other indices we can use (always with pg. or pygame. in front of this name):
K_0 0 - key K_LEFT Left arrow key
K_1 1 - key K_RIGHT Right arrow key
... K_F1 F1 function key
K_9 9-key K_TAB Tab-key
K_a A-key K_DELETE Del-key
K_b B- key K_RSHIFT Right shift key
... K_LSHIFT Left shift key
K_z Z-key K_LCTRL Left Control key
K_SPACE Space bar K_RCTRL Right Control key
K_UP Up arrow key K_LALT Left Alt key
K_DOWN Down arrow key K_RALT Right Alt key
Similarly we can get the state of the mouse buttons with pygame.mouse.get_pressed(), which
returns three logicals for the three mouse buttons. The function pygame.mouse.get_pos() returns
the current position of the mouse.
Both key- and mouse-functions will return only the useful values if your pygame-window is running in
the foreground (also called: has focus).
Another more versatile way to handle the mouse is to use the windows event handler. For instance for
the quit-event (somebody tries to close the window, e.g. by clicking on the red button). An example of
how this event handling could be used is given in the following piece of code:
...
elif event.type == pygame.MOUSEBUTTONUP:
...
Many of these events contain data, e.g.on where the mouse was clicked, which button or which key
was pressed. See the example above for using the mouse position. Other names of members of the
different event type are listed below:
Event type: pygame. Contains:
QUIT none
ACTIVEEVENT gain, state
KEYDOWN unicode, key, mod
KEYUP key, mod
MOUSEMOTION pos, rel, buttons
MOUSEBUTTONUP pos, button
MOUSEBUTTONDOWN pos, button
JOYAXISMOTION joy, axis, value
JOYBALLMOTION joy, ball, rel
JOYHATMOTION joy, hat, value
JOYBUTTONUP joy, button
JOYBUTTONDOWN joy, button
VIDEORESIZE size, w, h
VIDEOEXPOSE none
USEREVENT code
Most of the times the event handling is not required, but as a minimum handling the quit event is
considered good practice: it allows the user to close the window.
Exercise 12.9.1
With the knowledge you achieve each section, we want you to make a relatively easy game, in
four steps:
a) The first step is to make a screen with a resolution of 750x500 px. The screen should have
a blue background.
b) Place an image of an airplane at the left side of this screen. Google for a nice plane “plane
no background”, flying to the right.
c) Now we want to include a time loop so that your airplane flies straight forward with constant
speed over your screen.
d) The final step to make your game actually fun is to add key input, so make sure your plane
can move up and down when you press the arrow up and when you press the arrow key
down. Also make the escape key such that if you press it you quit the game.
∗
Creating your own types:
13
Object-Oriented Programming
In Python, next to function definitions, you can also define your own variable types and associated
functions. This is what classes are. Imagine we could design a new type of variable called Pos, short
for position. We want it to hold an x- and y-coordinate and we want to be able to do vector-wise addition.
Also a length function will give the length of a two-dimension position vector. Then we would be able
to write a program like below:
posa = Pos(3, 4)
posb = Pos(-1, 5)
distvector = posa.sub(posb)
dist = distvector.length()
To be able to do this we need to tell Python that we want to create a new type, specifying what data it
contains, what should be done to construct objects of this type, and what other methods the type has.
This is done with the class statement:
Ê Ë
Ì
class Myclass:
def __init__(self, a, b):
Í self.a = a
self.b = b
def mymethod(self, x):
# Update member data
Î self.b = x * b
...
1. The statement keyword class: this tells Python you’re starting the definition of a new type.
3. The self keyword: self is a special variable that points to the actual whole MyClass-object. It
is used to let your class access its own data from within its methods.
119
120 13. Creating your own types: Object-Oriented Programming
4. The special method __init__: this method is called whenever you create a new variable of type
MyClass. It tells Python how to create an object of type MyClass; what data it contains, and
what other initialisation is necessary. In object-oriented programming this is called a constructor.
5. The other methods of your class. Remember that a method is a special kind of function: It belongs
to a type, and can be called on an object (a variable) with the dot-notation: myvar.mymethod().
You can create as many methods in your class as you like. Note: all methods have self as a
first argument, but this argument is ‘automatically’ passed, so you can skip it when you call the
method. Calling the example mymethod with 𝑥 = 1 therefore looks like: myvar.mymethod(1).
Using this class definition we can create our new type Pos:
class Pos:
def __init__(self, xcoord, ycoord):
self.x = xcoord
self.y = ycoord
def sub(self,pos2):
rx = self.x - pos2.x
ry = self.y - pos2.y
newp = Pos(rx, ry)
return newp
def length(self):
return sqrt(self.x * self.x + self.y * self.y)
As you can see, our new type ‘Pos’ has two member variables, self.x and self.y. When you create
an object of type Pos you need to provide two arguments; xcoord and ycoord:
p = Pos(1, 1)
In the background, this expression tells Python to reserve memory for one Pos object, and store the
results of the special __init__ method in it.
Our Pos class has two methods: sub() and length(). The sub() method takes one argument:
another Pos object, and returns a new Pos object. The length() method takes no arguments, and
returns a float:
a = Pos(1, 1)
b = Pos(2, 3)
You can build your complete program around classes. By first defining your classes including members
and methods, then building classes consisting of classes on top of each other, your final program could
be very short. This can be done by just calling the highest level of classes like:
# Some initialisation
13.1. Implementing operator behaviour for your type 121
running = True:
while running:
for pressed in kb.get_pressed():
...
if pressed == Keyboard.Escape:
running = False
scr.draw()
This style of programming is called object-oriented programming (as opposed to procedural pro-
gramming) and was for some time very fashionable. It still is, but you also see a return to procedural
programming or a mix of the two styles. A disadvantage of object oriented programming is the addi-
tional bookkeeping, which you have to do, a huge advantage is the reusability of your classes in different
programs. For small to medium sized programs the disadvantages clearly outweigh the advantages.
For most scientific computing purposes a procedural program will do fine. However, it could be useful
to build your own libraries with specific computations for specific types of physics or technology. For
instance a class called aircraft could contain a position, altitude, angles etc.
With this method, the subtraction of two Pos variables can now simply be written as:
a = Pos(1, 1)
b = Pos(2, 3)
You’ll probably recognise the double-underscore syntax from the __init__ constructor. In fact, all
so-called special methods, such as constructors and operators, have fixed names that start and end
with double underscores. The following table gives an overview of the special functions that can be
implemented in a class definition.
122 13. Creating your own types: Object-Oriented Programming
To make your program look professional and easy to use, you might like to add a full-fledged windows
interface with pull-down menus and everything. There are several options to do this. Whichever you
like or find easy to use really depends on your taste. Some prefer an intuitive editor like provided with
PyQt, others like the plain, straightforward typing of Tkinter. In this chapter we’ll discuss three different
options: TkInter, which comes with Python by default, (Py)Qt, which is one of the largest GUI libraries,
GTK, and wxWidgets.
14.1. TkInter
TkInter is already provided with Python, and builds on the Tcl/Tk library. The TkInter module is an easy
way to use the standard window dialog boxes, e.g. the File Open dialog box
(named: tkFileDialog.askopenfilename()) in the example below:
import os,sys
from tkinter import *
import tkinter.filedialog as fd
Other standard dialog boxes with their tk name which you can use, like the above example, are:
123
124 14. Graphical User Interface building in Python
mbox.showinfo() mbox.askquestion()
• Asking a question:
import tkinter.simpledialog as sd
• Open a file:
import tkinter.filedialog as fd
fd.askopenfilename(), fd.asksavefile()
• Choosing a colour:
import tkinter.colorchooser as cs
cs.askcolor()
Some documentation can be found in Python Reference, but more can be found in the pdf file at
https://www.pythonware.com/media/data/an-introduction-to-tkinter.pdf. It con-
tains basic functions to build dialog boxes with many controls as well as the handle to call most stan-
dard windows dialogs. Easy to use but requires hand-coding and is rather basic in terms of graphics.
Still, the IDLE shell and editor you are using, were made in Tkinter. Tcl/Tk has a long-standing record
in the Unix world from times far before Python even existed.
14.2. PyQt 125
14.2. PyQt
Qt from Riverbank Computing (https://www.riverbankcomputing.co.uk/software/pyqt/
intro) provides an environment similar to Tkinter, but much more extensive. It can also be installed
with pip install pyqt5. It comes with many extras, like a Qt Designer, allowing you to graphically
draw the dialog boxes. The Spyder editor and IDE were built using PyQt. It builds on Nokia’s Qt
application framework and runs on all platforms supported by Qt including Windows, MacOS/X and
Linux. QtDesigner can be configured to generate Python code (which is difficult to read), but it’s easier to
have it generate an interface file (which is xml-based), and use Qt’s uic module to use your QtDesigner-
created gui in your Python program.
14.3. wxPython
Can be found at http://wxpython.org/ Also one of the classics in the Python community, wxPython is fully
Open Source, cross-platform (Windows/Linux/Mac OS). It’s something in between Tkinter and PyQt in
terms of functionality.
To give an impression of the code, a simple Hello world example is given below. It shows a window
called “Hello world” and catches the Close event when the user closes the window to ask for a verifi-
cation with an OK/Cancel Messagebox.
import wx
class Frame(wx.Frame):
def __init__(self, title):
wx.Frame.__init__(self, None, title=title, size=(350,200))
self.Bind(wx.EVT_CLOSE, self.OnClose)
app = wx.App(redirect=True)
top = Frame(”Hello World”)
top.Show()
126 14. Graphical User Interface building in Python
app.MainLoop()
14.4. PyGtk
PyGTK can be downloaded from https://www.pygtk.org. In terms of functionality and scale it
is similar to Qt, but unlike Qt it is created and maintained completely by the open-source community.
Gtk also has a graphical GUI design tool called Glade. The Glade program can be found at: https:
//glade.gnome.org/.
As an engineer (and engineering student) it is likely that you come across many different kinds of
data that you have to be able to read from, and write to files with varying file formats. So far in this
reader we’ve mainly looked at simple examples: plain text files, with data either separated by commas
or whitespace, or sometimes in the form of variable = value. Such formats are very easy to
parse yourself with plain Python (just with str.split() and str.strip() you’ll come a long way!).
However, when file formats get more complicated it sometimes
makes more sense to use an additional library that takes care of
parsing the file format. In this chapter we’ll discuss some of the
most common datafile formats, and the libraries to process them.
A library that you will see often in this chapter is Pandas; a very
powerful and versatile data parsing module you can install with
pip install pandas.
Because most spreadsheet editors (like MS Excel, or Apple Numbers) have the option to export sheet
data in CSV format, it is also often used as intermediate format to transfer data from a spreadsheet to
a piece of code.
The figure below gives a typical example of tabular data in a spreadsheet. The remainder of this section
will describe how to read this data when it’s provided as a CSV file. The next section will show you how
to get this data straight from an excel file.
127
128 15. Reading and writing data files: beyond the basics
When exported to CSV, the resulting file will look like this:
Note how each line contains 7 comma’s as a separator between the eight cells per line, even when the
cells are empty. Note also that only data is saved by value, so other features such as lines, formulae
etc are lost. Depending on your (regional) settings the comma separator could even be replaced by
another character. Also the encoding of character beyond the standard ASCII character set for special
characters might vary depending on your settings in Excel and your OS. The following methods handle
this for you in different ways.
To read the data into a two-dimensional text array (a list of lists containing strings), we remove the
newline character (”\n”) and split each line based on the separator character:
””” Reading a full CSV file into a two dimension text array ”””
# Start with empty table, where we will append each line with data
table = []
with open(”ExamOct.csv”) as f:
for line in f:
table.append(line.strip(”\n”).split(”,”))
As a result, the sheet will be stored in the list called table, where the first index is the line number,
starting with index zero, and the second index is the cell number, also starting with index zero. This
means that table[4][2] will give us the value from cell C5: line 5 and column 3 (“C”). Note how
Excel by calling it C5, basically uses the column,row indexing while normally data is accessed in the
row, column indexing format.
””” Reading grades directly into dictionary with names as keys ”””
gradebook = dict() # dictionary for unrounded grades
with open(”ExamOct.csv”) as f:
lines = f.readlines()
Writing to a csv file can be done in much the same way, as it is a text file with a comma as separator.
For instance, if we want to write the text array table from the first example, we can use the join
method of Python’s str type. For instance, ”,”.join(lst), will return a string with all elements of
lst, separated by commas. Our code to save a CSV then becomes:
The resulting CSV file can in turn be easily opened in Excel, Numbers, or your spreadsheet editor of
choice.
130 15. Reading and writing data files: beyond the basics
As you can see this approach is not much different from the first example above. What can save you
some work, however, is csv.DictReader, which automatically names your columns using the labels
in the header row, and outputs this in a dictionary. However, since the CSV module expects a simple
file with the column names on the first row, we need to skip the first two lines in our example, and
explicitly tell the DictReader where to find the column names:
import csv
with open(”ExamOct.csv”) as f:
dummy = f.readline(),f.readline() #skip two lines
colnames = f.readline().strip().split(”,”)
reader = csv.DictReader(f, delimiter = ”,”, fieldnames=colnames)
for row in reader:
if row[”Name”]!=””:
names.append(row[”Name”])
grades.append(float(row[”Rounded”]))
print(names)
print(grades)
files.
In Openpyxl, contrary to the python numbering logic, the indexing of rows and columns starts with one
not with zero (to match the numbering in Excel):
import openpyxl
gradebook = dict()
print(gradebook)
Note that to get the actual values, you need to open the file with the workbook with the data_only
switch set to True:
This is because in openpyxl, by default we will get what is in the top box of Excel, so in the case for
the grades, the formulae which will not be evaluated. So if you leave this option out, you will get the
formulae:
This allows accessing the formula, but it is rarely what you intend when interfacing with Excel files.
To write to an Excel xlsx-file, you can use openpyxl.Workbook() to construct an Excel workbook,
with one or more sheets. The following piece of example code demonstrates how to write two columns
of data with a header row to a new Excel-file:
newsheets = openpyxl.Workbook()
132 15. Reading and writing data files: beyond the basics
sheet = newsheets.active
A1.value = ”Name”
A2.value = ”Grade”
column1.value = name
column2.value = gradebook[name]
row = row + 1
newsheets.save(”Output.xlsx”)
15.2.2. Xlrd and xlwt: Reading and writing the old .xls format
Similar to openpyxl, xlrd and xlwt can be used to respectively read and write Excel workbooks,
but not in the newer xlsx format, but in the older xls format. Like openpyxl, xlrd and xlwt can read
and write workbooks with multiple sheets.
To use these modules, first use pip to install them, by opening a command prompt/console (with ad-
ministrator rights), and running:
An example of its usage can be found below. Different from openpyxl, the index of the first column
and first row is 0, when you use xlrd, following the standard indexing in Python.
import xlrd
gradebook = dict()
print(gradebook)
newsheets = xlwt.Workbook()
sheet = newsheets.add_sheet(”Checked”)
sheet.write(0,0,”Name”)
sheet.write(0,1,”Grade”)
row = 1
for name in gradebook:
sheet.write(row, 0, name)
sheet.write(row, 1, gradebook[name])
row = row + 1
newsheets.save(”Output.xls”)
This stores the results in a new file Output.xls, with two columns of data.
In our example, we would like to skip the first three rows before we get to the actual data:
import pandas
The columns are accessible via the labels on the first row read from the file. The lines, which follow,
are by default indexed using integers:
import pandas
134 15. Reading and writing data files: beyond the basics
gradebook = {}
row = 0
for name in names:
gradebook[name] = grades[row]
row = row + 1
print(gradebook)
To write to an excel file using pandas, we need to make a dataframe and then write this using the
Excelwriter function. For our example we can add the following code:
import pandas
...
Note how we need to specify to engine in the writer, to enable the writing of xlsx-files. Otherwise, as
pandas uses the xlwt module by default, we are limited to writing xls-file (Excel 2007).
{
”__comment”: {
”name”: ”aircraft full name”,
”wa”: ”wing area (m^2)”,
15.3. Hierarchical data: JSON files 135
As you can see, these data are comparable to dictionaries (or rather nested dictionaries: a dictionary
of dictionaries). The standard Python dictionary type therefore provides a good interface with JSON
files. This is used by the JSON module built in to Python. See the example below on how to read JSON
files.
import json
with open(”aircraftdata.json”) as f:
datadict = json.load(f)
print(list(datadict.keys()))
The datadict dictionary contains three keys, which will be printed by the program above:
Equally simple as reading is the writing with the dump function from the json-module, e.g. with the
gradebook dictionary from our previous example. Use the indent keyword gives us a readable lay-out,
and avoids the file being one long line:
import json
with open(”grades.json”, ”w”) as g:
json.dump(gradebook, g, indent=3)
{
”John Cleese”: 9.3,
”Michael Palin”: 9.5,
”Terry Jones”: 5.7,
”Terry Gilliam”: 6.6,
”Eric Idle”: 7.2,
”Graham Chapman”: 6.3
}
In XML format, the same data from the earlier JSON file would look like this:
<root>
<__comment>
<engine_type>Engine typer: TF, TP</engine_type>
<mfc>max fuel capacity (L)</mfc>
<mlw>max landing weight (kg)</mlw>
<mtow>max take-of weight (kg)</mtow>
<name>aircraft full name</name>
<oew>operating empty weight (kg)</oew>
<wa>wing area (m^2)</wa>
</__comment>
<A319>
<engine_type>TF</engine_type>
<mfc>30190</mfc>
<mlw>62500</mlw>
<mtow>75500</mtow>
<n_engines>2</n_engines>
<name>Airbus A-319</name>
<oew>40800</oew>
<wa>122.4</wa>
</A319>
<A320>
<engine_type>TF</engine_type>
<mfc>24210</mfc>
<mlw>66000</mlw>
<mtow>78000</mtow>
<n_engines>2</n_engines>
<name>Airbus A-320</name>
<oew>42600</oew>
<wa>122.6</wa>
</A320>
</root>
To read this with xmltodict is quite straightforward: use the parse() function from the module on
15.5. Binary data: MATLAB mat files 137
import xmltodict
with open('aircraftdata.xml') as f:
xmldata = f.read()
acdata = xmltodict.parse(xmldata)[”root”]
print(acdata)
ages = OrderedDict()
ages['Terry'] = 20
ages['Eric'] = 7
ages['John'] = 86
binary format of datafiles in MATLAB. Luckily, SciPy’s io module provides the functionality to load and
save MATLAB .mat files.
The function to load .mat files is scipy.io.loadmat(). It can take several arguments, but the most
important ones are the filename, and optionally the mdict keyword argument. When this argument is
not passed, the data loaded from the .mat file are put into a new dictionary with the MATLAB variable
names as keys, and the corresponding matrices as values. This dictionary is returned by the function.
When the optional mdict argument is given, however, the data is appended to the indicated dictionary
instead.
For example, loading a .mat file containing a single array called ‘testdata’:
data = loadmat('test.mat')
print(data['testdata'])
When exectuted, this would load and print the testdata array:
Similarly, scipy.io.savemat() can be used to save data from Python to a MATLAB .mat file. Its
arguments are similar to those of loadmat(), but now, mdict is not optional, but should contain a
dictionary of all the data to be stored in the .mat file:
data = {
”testdata”: [1, 2, 3, 4, 5],
”moredata”: [100, 200, 300]
}
savemat('output.mat', data)
”””
Combines all pdf-files in the PDFin folder,
make sure sorting by name works out right
into one PDF file ”merged.pdf” om the PDFout folder.
”””
import os
from PyPDF2 import PdfFileMerger, PdfFileReader
files = sorted(os.listdir(”PDFin”))
merger = PdfFileMerger()
15.7. Binary data: Any binary file 139
if len(files) > 0:
print(”Writing ./PDFcombined/merged.pdf”)
merger.write(”PDFcombined/merged.pdf”)
print(”Ready.”)
The next program rotates any pdf files in the PDFin folder clockwise and writes these to PDFrotated
folder.
”””
Rotates all pdf-files in the PDFin folder,
make sure sorting by name works out right
Output to pdfrotated folder
”””
import os
from PyPDF2 import PdfFileMerger, PdfFileReader, PdfFileWriter
files = sorted(os.listdir(”PDFin”))
print(”Ready.”)
f = open(”icon.bmp”, mode=”b”)
This allows us to access and read the file byte by byte. Every byte is composed of 8 bits, so can take
a value between 0-255. When an 4-byte integer format is used, we need to multiply the following three
bytes by higher powers of 256. Sometimes the highest power (most significant byte) might be the first
140 15. Reading and writing data files: beyond the basics
in the file. When conversion function are used, this order needs to be specified.
As an example we’ll look at the BMP format, a device independent image bitmap format. Looking at
https://docs.fileformat.com/image/bmp/ we can find the following information. It starts off
with a header of 14 bytes with some general information:
Offset Offset Size Purpose
hex dec
00 0 2 bytes The header field used to identify the BMP and DIB file is 0x42
0x4D in hexadecimal, same as BM in ASCII. It can following
possible values.* BM - Windows 3.1x, 95, NT, ... etc. * BA -
OS/2 struct bitmap array * CI - OS/2 struct color icon * CP -
OS/2 const color pointer * IC - OS/2 struct icon * PT - OS/2
pointer
02 2 4 bytes The size of the BMP file in bytes
06 6 2 bytes Reserved; actual value depends on the application that cre-
ates the image
08 8 2 bytes Reserved; actual value depends on the application that cre-
ates the image
0A 10 4 bytes The offset, i.e. starting address, of the byte where the bitmap
image data (pixel array) can be found.
Let’s use the following program to inspect it, byte by byte (so reading with size of 1). A single byte can
be converted to an integer using the ord() function:
Byte 0 : 66 = b'B'
Byte 1 : 77 = b'M'
Byte 2 : 50 = b'2'
Byte 3 : 6 = b'\x06'
Byte 4 : 0 = b'\x00'
Byte 5 : 0 = b'\x00'
Byte 6 : 0 = b'\x00'
Byte 7 : 0 = b'\x00'
Byte 8 : 0 = b'\x00'
Byte 9 : 0 = b'\x00'
Byte 10 : 50 = b'2'
Byte 11 : 2 = b'\x02'
Byte 12 : 0 = b'\x00'
Byte 13 : 0 = b'\x00'
Using the info from this table, we can see that this is a BMP file made on Windows. Then a 4 byte
integer shows the size of the file, least significant byte first, so little value first:
# Go to byte 10 (OA)
skip = f.read(4)
Filetype identifier: BM
BMP file is: 1586
Pixel data starts at: 562
Opening files as binary files, whether for reading or for writing gives you full control of the meaning of
every byte. Using the int methods int.from_bytes() and int.to_bytes(), you can convert
back and forth from integers, as long as you specify the order of the bytes “little” or “big”, depending
on whether the least significant bytes (LSB) first or the most significant (MSB) is first in the sequence
of bytes.
16
Exception handling in Python
A powerful feature of Python is that it allows you to trap runtime errors using the try and except state-
ments. This functionality is very useful to catch and deal with unwanted cases (e.g., a user giving letters
to the input() command where the code only expects numbers:
int(input('Give a number: '))). You should be aware, however, that this is also a way to
obscure errors, especially when you catch too general an exception (more about this later). This can
quickly make your program impossible to debug. In general it is better to prevent errors than to catch
them.
Still, it can sometimes be a very useful feature, so we show a few examples here of how to use try
and except.
A try-except block is structured as follows:
try:
# Block of code to test
Ë
...
Ì Í Î
except ExceptionType as e:
# An error has occurred
Ï print(e)
143
144 16. Exception handling in Python
6. The block of code to execute when an exception (of the specified type) is caught. When nothing
needs to be done when catching an exception you can put the pass keyword here.
Note that in the except block, it is not mandatory to provide a specific exception type; you can also
just write except:, in which case all exceptions will be caught by this except block. The benefit of
this is that you don’t need to know the type of errors that can occur in advance, the downside is that all
errors in your code (aside from indentation errors and syntax errors) will be caught! This can make it
difficult to find bugs in your program.
Note also that you can add more than one except block; one for each type of exception. That way
you can implement different kinds of responses for different kinds of errors in the same code.
Finally, you can also catch more than one type of exception (instead of just one, or all) in one except
block. You do this by providing the list of exception types you want to catch in a tuple:
try:
...
except (Type1, Type2) as e:
...
Now for some examples. For these we revisit the 𝑎, 𝑏, 𝑐 formula solver, and make it check for problems in
the equation coefficients by simply catching the error. Let’s first look at the original abc program:
import math
D = b**2 - 4.0 * a * c
When there are no solutions to the quadratic equation, 𝐷 will be negative, which leads to the following
exception:
It is also possible that the user has entered values for a first-order equation (𝑎 = 0). In this case there
is a division by zero, leading to the following exception:
The easiest way to avoid your program stopping with an error is to use a general try-except block:
import math
D = b**2 - 4.0 * a * c
try:
x1 = (-b - math.sqrt(D)) / (2.0 * a)
x2 = (-b + math.sqrt(D)) / (2.0 * a)
print(”x1 =”, x1)
print(”x2 =”, x2)
except:
print(”There was a problem with the specified polynomial values!”)
This is easy to use, but it doesn’t allow you to make a distinction between the different kind of problems
that can occur, and as described before, it can also hide other errors in your code. You have more
control and knowledge over the caught exceptions when you explicitly specify the type of exception
to catch. In this case there are two types of exception we are interested in: when a is zero, we get a
ZeroDivisionError exception, and when D is negative we get a ValueError. If we catch these
exceptions separately we can do the following:
import math
D = b**2 - 4.0 * a * c
try:
x1 = (-b - math.sqrt(D)) / (2.0 * a)
x2 = (-b + math.sqrt(D)) / (2.0 * a)
print(”x1 =”, x1)
print(”x2 =”, x2)
except ValueError:
print(”This polynomial has no roots”)
except ZeroDivisionError:
print(”This is a first order equation: a=0”)
try:
x = -c / b
print(”Solution x =”, x)
except:
print(”No x found.”)
147
148 17. Creating self-contained bundles of your program using PyInstaller
mazeman.exe
pause
Then run it to test it and see if you need to add any other files. Sometimes you may need other DLLs
from third party module you use.
At some point in the development of software, especially when collaborating in a team, you will find
yourself handling many versions of the same program. You will copy files back and forth, zip up the
state of a project and label it with date and time, etc. Anyone who has worked like this for a while will
have found out that this is an error-prone process, important fixes might get lost, changes inadvertently
overwritten, collaborators work using older versions of files, and at some point it becomes almost im-
possible to combine all code contributions into a final, correct version. This is such a common problem
in software development that people have created Version Control Systems (VCS) for this. With a VCS
you can automate the process of keeping different versions of your software project, and the VCS will
help you on many tasks, like find differences between versions, revert to an older version if you acci-
dentally botched things up, up to incorporating automatic testing of the software in the project. Although
these systems are not specific to Python, we decided to add this section on VCS after hearing from 2nd
and 3rd year students, who eventually picked up using VCS at some point, that they would really have
liked to start with VCS earlier, and that they would have appreciated an introduction and some pointers
on where to learn more. Many VCS exist now, we will discuss two commonly used ones; subversion
and git.
149
150 18. Version Control
Version A Version B
Comparing source code Comparing source code
The oldest tools for version control The oldest tools for version control
are diff and patch. The basics of are patch and diff. The basics of
these tools are still used in modern these tools are still used in modern
↪ VCS. ↪ VCS.
The output of calling diff text1.txt text2.txt from the command prompt
4c4
< are diff and patch. The basics of
---
> are patch and diff. The basics of
This output is commonly called a diff or a patch. From a diff, you can quickly see what changes were
applied. patch is a program that is the companion to diff, it can create the newer file, based on the old
file and the output from diff (it can also work in the other direction, creating the old file from the new
file and patch). VCS work on this basis. Each time you tell the VCS that there is a new version of
your project, the VCS compares the new version to the older version it already has, then determines
and stores the difference between the existing version and the new one. In that way the VCS keeps
the complete history of versions and changes. Asking the VCS for a diff between two revisions of your
software can be a quick way of identifying where changes were made.
To an unsuspecting user, it may seem like the version control system simply has stored every copy ever
committed. However, using a diff, rather than simply storing the latest version and labeling it, has a
side effect. A diff will fail if the file in the working copy does not match the file in the repository, because,
when you perform a commit, the VCS determines how your files have changed using a diff, and tries to
implement that diff on the code in the repository. As an example, if you make a checkout to a file with
following line:
# A program by Lisa
Then you are going to collaborate with Max, who also checks out a copy of the software from the
repository, and corrects to:
Max quickly checks this in by doing a commit, so now the repository has Max’s updated version. In the
meantime you recognise Max’s contribution, and change the line to:
Now, if you commit your version, the VCS tries to get the latest, final version of the software in the
repository by applying a patch from your old version (# a program by Lisa), to your new version
(# a program by Lisa and Max), to the version that is currently in the repository. However, that
will fail with a “conflict”, because the repository version has changed at that specific location. You have
to then manually fix the file (the VCS helps you there by marking the conflicts), and tell the VCS that
you resolved the problem. If you work together in a reasonable fashion (typically by not all working
on the same code at the same time), conflicts can be avoided, and if they occur, it is usually easy
18.1. Basic concepts of version control 151
enough to resolve them. This side effect may seem to be a drawback of using version control systems
over copying files, but there is actually a significant advantage; it prevents you from silently overwriting
previous work. On Windows, a tools called Winmerge can be used to compare source in a GUI and
merge different versions. Many similar tools exist on other platforms.
repository
Server
up
it
da
m
te
co
e
m
t
da
co
m
m
up
it
working
working
copy copy
Local pc 1 Local pc 2
The alternative is a distributed or de-centralised VCS, of which “git” is a prime example. When starting to
work on the code, you must “clone” a git repository from the web. The clone copies the entire repository,
including information about its history, to the local computer. In a second step you can check out any
revision from the now local copy of this repository; by default the VCS checks out the latest development
version. Then any commit will be done to the local clone of the repository, creating a new revision , and
you will not need internet access for that. In a second step these changes can be copied over to the
remote repository, with a “push”. To get changes that may have been added by others to the remote
repository, you need a “pull” command, which gets all the revisions added to the remote repository from
the time you cloned it, or did your last pull. The local copy you are working on is linked to one particular
remote repository, but it is also possible to link multiple remote repositories to the local development
copy, and switch between these. We will not further explore this here, interested readers are referred to
https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes. When you are
working with only one remote repository, there is conceptually not much difference between using a
centralised or a distributed version control system; the only steps you should not forget are the pull and
push steps, to synchronise the local clone with the remote repository.
152 18. Version Control
repository
Server
pu
sh
ll
pu
pu
ll
pu
sh
Local
Local
repository
repository
commit commit
update update
working
working
copy copy
Local pc 1 Local pc 2
Illustration of branching and merging, with a case where a development branch is split off from the
main branch, and fixes and development occur in parallel.
With branching, you can split off a version of the software (i.e., as a complete set of files and folders)
that can from then on be independently developed from the main “branch”. To illustrate this, let’s take
the scenario where your teammates are already running low-speed analysis of the flight performance
using the initial version of the software you developed in your (3𝑟𝑑 year) DSE project, and you now
want to add compressibility effects to properly calculate cruise performance. By branching off, you will
be able to simultaneously have and maintain two versions of the program. You can work on the new
version without risking breaking the version that the others are using. When the work on the branch
is complete, the two versions can be merged again, applying all development from the development
branch to the main version in one step. For this, the VCS can apply all the commits you added to the
18.2. Overview of Git commands 153
new branch to the old branch that people have been using. Also if the main branch has been moving on,
for example when bugs had to be fixed there, the merge will combine the changes on both branches,
keeping both the addition of new capability and the bug fixes.
• Clone (check out to your own computer) the latest version of a project
This clones the repository abd checks out the “master” branch into the local folder “MyProject”.
git pull
git commit
Note that this commits to the local repository, see “git push” to transfer these commits to the
remote repository.
git init
Run this from within the folder where you want the repository.
• Copy the local repository changes over to the remote repository git push
Note that there may not be uncommitted changes in the working directory.
• Determine the difference between the current working copy and a repository commit
git log
154 18. Version Control
MyProject/trunk
MyProject/tags/v0.9
MyProject/tags/v1.0
MyProject/branches/add-mach-effects
At each branch (trunk, v0.9, etc.) the subversion repository show all the files and folders in your project.
Superficially, it looks as if everything is copied, internally, subversion maintains an inventory of differ-
ences and changes in the files.
On Windows TortoiseSVN is an svn-program which works as a Windows shell extension: right clicking
any folder allows you to sync it with a repository. Then simply right clicking a file or folder an selecting
commit or update will do the same as above.
The following has a summary of the most-used actions in subversion.
• Check out the latest version of your software
svn co https://myserver.nl/SVN/MyProject/trunk newfolder
Assumes standard repository layout, checks out project into new folder “newfolder”.
• Update working copy to include changes committed to the repository
svn update
Run this from the working copy folder!
• Commit changes to repository
svn commit
Run from working copy folder.
• Create a branch
svn cp . https://myserver.nl/SVN/MyProject/branches/newbranch
Assumes you run from working copy (indicated by “.”).
• Merge a branch
svn merge -r4:6 https://myserver.nl/SVN/MyProject/branches/abranch
Merges the commits (i.e. changes) from revision 4 to revision 6 on branch abranch, into the
current checked out folder.
• Revert changes to match repository (e.g., after goofing up)
svn revert
• Resolve a conflict
svn resolved <file>
After fixing a conflict (marked by «««<, =======, and »»»> markers in the file), subversion needs
to be told you have fixed this.
• Add a new file or folder to the version control
svn add <file>
18.3. Overview of Subversion commands 155
Do not forget this, subversion does not automatically recognise or add new files.
• Log the commit messages on a working copy
svn log .
or
svn log . –stop-on-copy
the –stop-on-copy flag is useful to figure out when you started a branch. You will need to use
that revision number for a merge (see the merge above).
• Check for changes with a previous copy
svn diff -r <revisionnumber>
• Check which repository is connected to your working folder:
svn info
Note that these git and subversion command summaries are by no means complete, they represent a
minimum set of commands to handle the most common cases. There are also many GUI tools to help
you navigate the git or subversion commands, check out the pages at:
https://git-scm.com/downloads/guis
https://en.wikipedia.org/wiki/Comparison_of_Subversion_clients
∗
Beyond the course: Exploring
19
applications of Python
The purpose of this course is to get you started with writing programs in Python, to explain its basics,
and the basics of scientific computing in Python, and to give you a brief overview of what kinds of
things are possible in Python. Here, many of the things we show you are just examples, with plenty
of alternatives out there as well. A good example of this is Pygame; we introduce you to Pygame
in this course because it is easy to get started with, and allows you to very quickly get visual (and
visually pleasing) output of your work. But there are of course many alternatives! Below are just some
examples.
master = Tk()
w = Canvas(master, width=200, height=100)
w.pack()
w.create_line(0, 0, 200, 100)
w.create_line(0, 100, 200, 0, fill=”red”, dash=(4, 4))
w.create_rectangle(50, 25, 150, 75, fill=”blue”)
mainloop()
Alternatively, even LOGO like graphics have been included in Python using the turtle module. Not te
be used for serious applications but it’s really a lot of fun, like a nice, spirograph-like toy and also a very
good way to teach younger people how to program!
157
158 19. Beyond the course: Exploring applications of Python
for i in range(200):
forward(i)
right(90.5)
done()
19.1.2. Arcade
The Arcade library provides much of the same functionality as Pygame, and is specifically aimed at
making games, for which it also has specific functionality to make your life easier. Have a look at
https://arcade.academy/ for more information. Can easily be installed with pip: pip install arcade.
An example:
# Import the Arcade library. If this fails, then try following the
↪ instructions
# for how to install arcade:
# https://arcade.academy/installation.html
import arcade
import os
# Set the working directory (where we expect to find files) to the same
# directory this .py file is in. You can leave this out of your own
# code, but it is needed to easily run the examples using ”python -m”
# as mentioned at the top of this program.
file_path = os.path.dirname(os.path.abspath(__file__))
os.chdir(file_path)
# Open the window. Set the window title and dimensions (width and
↪ height)
arcade.open_window(600, 600, ”Drawing Primitives Example”)
# Start the render process. This must be done before any drawing
↪ commands.
arcade.start_render()
# Draw a grid
# Draw vertical lines every 120 pixels
for x in range(0, 601, 120):
arcade.draw_line(x, 0, x, 600, arcade.color.BLACK, 2)
# Draw a point
19.1. Alternatives for Pygame for (2D) visualisation 159
# Draw a line
arcade.draw_text(”draw_line”, 243, 405, arcade.color.BLACK, 12)
arcade.draw_line(270, 495, 300, 450, arcade.color.WOOD_BROWN, 3)
(180, 285),
(165, 300),
(150, 300))
arcade.draw_polygon_filled(point_list, arcade.color.SPANISH_VIOLET)
For instance these two lines will already result in the window below to be shown:
sphere()
Objects can be added a simple way (see the axes below to understand the positioning):
19.2.2. Panda3D
Panda 3D is a very complete 3D graphics and game engine developed originally and hosted by Carnegie
Mellon. This includes very advanced graphics functions. It is compatible with the PyODE physics en-
gine. Check out their website for some impressive demos: https://www.panda3d.org and for
documentation and download. Panda3D is Open Source and free for any purpose.
19.2.3. OpenGL
OpenGL has been the standard for 3D graphics for a long time and is at the lowest layer of most other 3D
packages. For the Microsoft gamers: it is a sort of cross-platform DirectX. You can then directly control
the hardware of OpenGl compatible graphics cards. Only to be used by the more daring, demanding
and experienced programmer, it provides the possibility to call OpenGL directly from Python with the
PyOpenGL module. PyOpenGL is compatible with Pygame: so you can use it in a pygame window
(and it often is).
PyOpenGL is very fast and interoperable with a large number of external GUI libraries for Python
including wxPython, PyGTK, and Qt. It can also use the GLUT library to provide basic windowing and
user interface mechanisms (pulldown menus).
As using OpenGL requires many parameters to be
set, example source code tends to be a bit too long
to included here as an example. But check out for
example:
https://www.willmcgugan.com/blog/tech/
2007/6/4/opengl-sample-code-for-pygame/
Or google some examples yourself. The Py-
OpenGL website has documentation at https:
//pyopengl.sourceforge.net/documentation/,
which will help you to understand the code. You can
of course always use some samples as a starting
point for your own applications.
Several students have explored this option and used
it successfully to make a 3D solar system visualiza-
tion, a 3D asteroids game and even a Moonlander
simulation on a hilly moon surface. Working with
OpenGL is quite complex, so it can be hard, but it
is also very powerful.
Relatively easy to use, but realise that the closer you get to reality with your simulation, the more com-
plex your model and your program will become: you’ll need to set a lot of parameters, although many
defaults are supplied. To get an impression of what it can do check put youtube videos like ‘ragdoll
demo python ODE’.
g = gimp.pdb
images = gimp.image_list()
my_image = images[0]
layers = my_image.layers
w = g.gimp_image_width(my_image)
h = g.gimp_image_height(my_image)
print(”Image Resolution: w=%d,h=%d”%(w,h))
new_layer = g.gimp_layer_new( my_image, w, h, \
RGBA_IMAGE, ”LeopardLayer”, 100, NORMAL_MODE)
my_image.add_layer( new_layer )
g.gimp_context_set_pattern(”Leopard”)
g.gimp_edit_fill(new_layer, PATTERN_FILL)
g.gimp_layer_set_opacity(new_layer, 20)
g.gimp_layer_set_mode(new_layer, SCREEN_MODE)
19.4.2. Blender
No matter which 3D library you use, you will need to create 3D objects with complex shapes, surfaces
and textures. For this most Python programmers (and many others) use the freeware program Blender
(https://www.blender.org). Blender a a very advanced, yet easy to use, 3D content creation
suite. You can import objects made in Blender in Python using OpenGL. Even without Python, Blender
can make interactive animations. Make sure to visit the website to check out the beautiful gallery.
Blender is used a lot by professionals in the Computer graphics world (for commercials, movies and
games). Also check out Youtube for some impressive Blender examples. Blender also has a powerful
game engine.
19.5. Interfacing with hardware 165
19.5.2. MicroPython
A relatively new initiative (from MIT) is called MicroPython. This is a special version of Python 3 for a
microcontroller. The Micro Python board, called pyboard, is shown below.
By simply copying the text files with your Python code from your PC onto the microboard, you can run
the programs. To get an impression of the code, there are several examples on the website.
For example, controlling LEDs:
166 19. Beyond the course: Exploring applications of Python
for i in range(50):
numbers.append(randint(1, 100))
When run, this code will print an unsorted array of 50 random integers under 100. Now we will sort it
with the famous Bubblesort algorithm. The logic of this algorithm is based on three steps:
• iteratively check all consecutive pairs in the list
• if a pair is in the wrong order swap the places of the elements
• keep checking all pairs until no swap is needed anymore
This logic is implemented in the following code:
while swapped:
# Now assume everything is done unless proven otherwise
swapped = False
167
168 20. Examples
# The loop ends when swapped is False and no swaps were performed
↪ anymore
print()
print(”After sort:”)
print(numbers)
First we check whether these elements are in the wrong order. When they are the same we should not
swap them (it would keep doing this forever then). If wrong, we save the value of numbers[i] in the
variable a, then we overwrite this with the value of numbers[i+1]. And then we use our saved value
in a to put in numbers[i+1] resulting in a swap of places.
For this to work we need to let i run from 0 till the last place minus 1. Otherwise numbers[i+1] would
result in an error for the last step in the loop. The for-loop therefore reads:
for i in range(len(numbers)-1):
Now all we need to do is add some logic to detect whether we are done. We can use a logical swap
to detect any swapping. For this we first set it to False outside the for-loop and as soon as we swap
two variables we set it to True.
Then we add a while-loop around this which will keep repeating our for-loop until swap remains
False and hence no swap was performed. To make sure we enter the while-loop the first time, we
have to initialise our logical to True outside the while-loop.
This is the order in which this program was made: from the inside out. First putting in elementary
operations and then using a logical to add some logic.
The end result is an already complex looking construction. Look at the three indent levels: while, for
and if are inside each other or nested as we call this in programming.
A
Answers to exercises
Exercise 1.4.1
For our savings/debt calculator we need three inputs from our user: the starting sum, the number of
years to calculate the interest for, and the interest rate. We can use the input command for this.
Because input returns a string variable, we need to convert the user input to numbers before we can
use them:
With the three numbers known we can calculate the resulting sum, and print it as a result back to the
user:
Exercise 1.4.2
In this exercise we also need three inputs from our user, which we will convert to floating-point numbers
before using them to calculate the total resistance:
Rabc = Rab + Rc
print(Rabc)
Exercise 2.9.1
As we can see in the question, we need to ask our user three numbers: the interest rate, the start sum,
and the number of years to calculate the interest for. The number of years can be interpreted as an
int (let’s assume only whole years), the other two values could be floating point values:
169
170 A. Answers to exercises
We can use the interest rate to calculate the growth rate; a multiplication factor with which our total
sum grows every year. Because our interest rate is a percentage we need to divide by 100 to obtain
𝑖𝑛𝑡𝑒𝑟𝑒𝑠𝑡
the correct fraction: 𝑟𝑔𝑟𝑜𝑤𝑡ℎ = 1 + . If we don’t add or subtract any money aside from the
100
interest, we should multiply the starting sum with this growth rate for each year we consider: 𝑣𝑒𝑛𝑑 =
𝑛
𝑣𝑠𝑡𝑎𝑟𝑡 ⋅ ∏ 𝑦𝑒𝑎𝑟𝑠 𝑟𝑔𝑟𝑜𝑤𝑡ℎ . We can implement this in code with a for-loop (for more info on this see
section 3.5):
Exercise 2.9.2
In this exercise we want to ask our user a number of words, but we don’t know how many. To fix this we
could for instance first ask our user how many words (s)he wants to enter, and then use the input()
function that many times to ask each word using a for-loop:
The next step is to print the words, as well as the length of each word. We can again use a for-
loop:
for i in range(nwords):
print(words[i], 'has length', len(words[i]))
Exercise 2.9.3
To print the contents of a list, and the size of each item in a list we can use the same approach with a
for-loop as in the previous exercise. To make our loop with the right number of iterations we need to
find the length of our list. We can obtain this with the len() function:
for i in range(len(values)):
print(len(values[i]), values[i])
Exercise 2.9.4
171
To construct a new list, we start off with an empty one. Looking at lengths, we can see that the first
list will contain two values, the second list will contain two values, and the third list will contain three
values. As in the previous exercise, we can use a for-loop to go over these lengths, and select an
appropriate number of values from values. There’s one catch: we need to make sure we remember
where we left off in values when we re-enter the loop. This can be done with a counter, as shown
here:
result = []
counter = 0
for i in range(len(lengths)):
result.append(values[counter:counter+lengths[i]]) # select a list
↪ of lengths[i] values
counter += lengths[i] # increment the counter by the number of
↪ values we just added
print(result)
Exercise 3.1.1
In this exercise we need to get two numbers (floats) from the user (with input() and float()), and
calculate the hypotenuse using Pythagoras formula. The equation contains a square root, which we can
implement using sqrt() from the math module, or with the built-in power-of operator:
(a**2 + b**2)**0.5.
# Option 1
c = sqrt(a * a + b * b)
# Option 2
c = (a**2 + b**2)**0.5
Exercise 3.1.2
In this exercise we’ll again use input() and float() to obtain the numbers a, b, c, and d from the
user:
Next we’ll follow the steps from the diagram. The first step is to calculate the largest value of a and b.
We’ll store the result in a new variable m1:
m1 = b
Finally in a third comparison we’ll compare the new variables m1 and m2 to find the largest of all four
variables:
Note that the table in section2.10 contains a nice built-in function which gives us a much easier ap-
proach, the max() function. It allows us to do the entire comparison in one line!:
Exercise 3.1.3
First, we need to get a number from the user (with input() and float()) and set the correct bounds.
Next, we can take one of two approaches: using if-else-statements, or using min and max.
Approach 1: using if-else-statements:
minimum = -10.0
maximum = 10.0
print(a)
if a < 0:
print(”The value is negative!”)
minimum = -10.0
maximum = 10.0
print(a)
if a < 0:
print(”The value is negative!”)
Exercise 3.5.1
To find out whether a number is a prime number we need to check if it’s divisible by any other number
than itself or 1. This answer will take a simple approach: For each potential prime ‘n’ it will use a simple
loop that checks for each number smaller than n whether n is divisible by it.
We’ll start by asking the user until which value we should check for primes, and then loop over all these
numbers:
# The first prime, 2, we skip, so that afterwards we can make our loop
↪ faster by skipping all even numbers:
print(”The prime numbers under”, maxvalue, ”are:”)
print(2)
# Now make the loop over all odd values smaller or equal than maxvalue:
# We start at 3, until maxvalue, in steps of 2
# Remember that to include the final value, we need
# to set the end value in the range function one higher!
for n in range(3, maxvalue + 1, 2):
To calculate if the current n is divisible by any other number than itself and one we need to check a
variable number of values. We can do this with a nested loop (a second for-loop inside the loop of n
values):
Exercise 3.5.2
Although you can generate this pattern also with two consecutive loops (first: for i in range(1,6),
then loop back with a second loop: for i in range(5, 0, -1)), a solution with only one loop is
of course neatest. A nice way to obtain the requested triangular shape is by making use of numbers
around zero, in combination with the pyabs() function. To generate the sequence (1, 2, 3, 4, 5, 4, 3,
174 A. Answers to exercises
2, 1), we can use the following equation: 𝑥 = 5 − |𝑖|, 𝑖 ∈ [−4, 4]. In Python, this is done with the
following for-loop:
Instead of the print(x * '* ') you can also use a nested loop:
Exercise 3.5.3
In this program we’ll let the user enter a string with the input() function, and count the number
of digits and letters in that string. For this we can use two built-in methods of Python’s string type:
str.isdigit() and str.isalpha(). We’ll use two integers to store these two numbers, and do
the counting in a for-loop:
Exercise 3.7.1
In the previous version of our prime number checker from exercise 3.5.1, we used a nested loop: an
outer for-loop to go over all values below a certain target value, and a second, nested for-loop to,
for each value of the outer loop, find if there exists a value with which it is divisible. We can make this
inner loop faster by breaking out of it as soon as we’ve found a number with which our current potential
prime is divisible:
# For each n we want to look at all values larger than one, and
↪ smaller than n:
for x in range(2, n):
# If n is divisible by x, the remainder of this division is 0.
↪ We can check this with the modulo operator:
if n % x == 0:
# If we get here the current n is not a prime.
isprime = False
# we don't need to check further numbers
break
# If isprime is still True this is indeed a prime
if isprime:
print(n)
An alternative approach which avoids the isprime logical makes use of a for-else combination:
when else is used connected to a for-loop, its contents are only executed when the for-loop com-
pletely runs. So when a break prematurely stops the loop, the code in the else block is not run.
Exercise 3.7.2
We first need to get the input from the user and construct sets of requirements to check against. We
can check whether a character is in a string or list using in. When a criterion is met, we set the indicator
for that criterion to True. At the end, if all criteria are met, the password is valid.
abc = ”abcdefghijklmnopqrstuvwxyz”
ABC = ”ABCDEFGHIJKLMNOPQRSTUVWXYZ”
digits = ”0123456789”
special = ”!@#$%&*+-=?^_~”
length_indicator = False
abc_indicator = False
ABC_indicator = False
digits_indicator = False
special_indicator = False
# check length
if len(password) >= 8 and len(password) <= 16:
length_indicator = True
if char in digits:
digits_indicator = True
if char in special:
special_indicator = True
# we can add a break to speed things up
if abc_indicator and ABC_indicator and digits_indicator and
↪ special_indicator:
break
Exercise 6.4.1
Our function needs to be able to count the words in a sentence, and print the last word. Assuming that
the word count is a returned output, our function therefore has:
1. One argument: a string with the line of text
2. One return value: an integer with the word count
3. A print statement in the body of the function, which prints the last word.
As words in a sentence are separated by spaces, we can look for all spaces to detect the words in
our sentence. With a for-loop we can iterate over our string, and increase the number of words for
each found space. By keeping track of the last space in the string we can also find the last word in our
string:
def countwords(sentence):
# the first word doesn't have a space in front, the rest does
count = 1
lastspace = 1
# Iterate over the characters in the sentence
for i in range(len(sentence)):
# If character is a space, increase word count
if sentence[i] == ” ”:
count = count + 1
# Keep track of the last space:
# After the loop, lastspace will be equal
# to the last value for i where the
# character was a space
lastspace = i
# Print the last word
print(sentence[lastspace + 1:])
# Return the word count
return count
We can also use the method str.split() to split our string into a list of words (see section 7.4).
Using this list we can count the number of words with len(), and print the last element in the list to
get the last word in the sentence. The function will look like this:
def countwords(sentence):
# First split the string (which, by default splits on whitespace)
words = sentence.split()
177
Exercise 6.4.2
We’ll create four functions:
1. Calculate the surface of a sphere:
A sphere is defined by just one parameter: its radius. Our function therefore has one argument
and one return value. The equation for the surface of a sphere is 𝑆𝑠𝑝ℎ𝑒𝑟𝑒 = 4𝜋𝑟 2 . The function
therefore becomes:
def surfacesphere(radius):
S = 4.0 * math.pi * radius ** 2
return S
def volumesphere(radius):
V = 4.0 / 3.0 * math.pi * radius ** 3
return V
Exercise 6.4.3
To repeat our input string 𝑛 times, we can make use of the expression number * string, which, like
its list counterpart, returns a new string with the original string repeated number times. When we
consider that our delimiter should come after every occurrence of string but the last, we can do the
following:
Exercise 6.4.4
longest = ””
for word in sequence:
if len(word) > len(longest):
longest = word
print(longest, len(longest))
Exercise 7.4.1
def is_palindrome(s):
return s == s[::-1]
Exercise 7.4.2
Of course, we can hardcode str.replace(digit, ””) for all digits 0-9, but we can also put this in
a loop:
def filter_options(option):
for digit in [str(i) for i in range(10)]:
option = option.replace(digit, ””)
if option.isalpha():
break
option = option.lower()
Exercise 8.1.1
To plot our two functions 𝑓(𝑥) and 𝑔(𝑥) with Matplotlib, we need to create three lists: one for the 𝑥-
values, and one each for 𝑓(𝑥) and 𝑔(𝑥). We’ll populate these lists with a for-loop. The resulting
code:
xtab = []
fxtab = []
gxtab = []
plt.plot(xtab, fxtab)
plt.plot(xtab, gxtab)
plt.show()
179
When we inspect the curves in the plot window and zoom in on the intersection, we find that they
intersect at (1.054, 0.87).
Exercise 8.1.2
We’ll again plot an x-y graph, but our function is defined in polar coordinates. We therefore need to
convert these to Cartesian before storing them in the 𝑥 and 𝑦 lists. The resulting code:
xtab = []
ytab = []
plt.plot(xtab, ytab)
plt.show()
Exercise 8.1.3
After the previous exercises this shouldn’t be difficult: we already have two lists, which we plot against
each other:
plt.plot(time, co2)
plt.show()
Exercise 8.2.1
We have two graphs, 𝑦1 and 𝑦2 , that we’ll plot in two separate sub-plots. We’ll use a loop to create the
𝑥, 𝑦1 , and 𝑦2 lists, and we’ll create the stacked plots using the subplot() function.
y1tab = []
y2tab = []
xtab = []
for x in range(1000):
x = x / 100.0
xtab.append(x)
180 A. Answers to exercises
plt.subplot(211)
plt.plot(xtab, y1tab)
plt.subplot(212)
plt.plot(xtab, y2tab)
plt.show()
Exercise 8.2.2
We’ll again create a figure with two subplots. In this exercise we have two functions in polar coordinates,
that we have to convert to Cartesian before storing them in lists:
x1tab = []
y1tab = []
x2tab = []
y2tab = []
r1 = theta
r2 = theta**2
x1 = r1 * math.cos(theta)
y1 = r1 * math.sin(theta)
x1tab.append(x1)
y1tab.append(y1)
x2 = r2 * math.cos(theta)
y2 = r2 * math.sin(theta)
x2tab.append(x2)
y2tab.append(y2)
plt.subplot(121)
plt.plot(x1tab, y1tab)
plt.title(”r = theta”)
plt.xlabel(”x”)
plt.ylabel(”y”)
plt.subplot(122)
plt.plot(x2tab, y2tab)
plt.title(”r = theta**2”)
plt.xlabel(”x”)
plt.ylabel(”y”)
plt.show()
Exercise 8.2.3
181
Our data in this exercise couldn’t be simpler: three straight lines. We’ll plot them with three individual
plot commands:
plt.show()
Exercise 8.3.1
We can create interactive plots iteratively using plt.draw() inside a loop, after initialising interactive
plotting with plt.ion(). Because we draw interactively, we don’t necessarily need to store our data
in lists:
plt.ion()
plt.close()
Exercise 8.4.1
The first aerospace-themed exercise! We’ll make a 3D-plot using
mplot3d.Axes3D.plot_surface(). This function takes data coordinates in three 2D arrays. These
can be lists-of-lists, like [row1, row2, ..., rown], or, as we’ll find out in chapter ??, NumPy ar-
rays. When we consider that every row (so every nested list) corresponds to all values of 𝑥, for a given
value of 𝑦, then every row in our X array will contain all values of x, whereas every row in our Y array
will only contain the current value of y, repeated 𝑛 times. Finally, our Z array should contain Z values
𝑥2
corresponding to the equation 𝑧 = . To create these three nested lists we can use the following
1000
loop:
Zrow.append(x**2 / 1000)
Z.append(Zrow)
Unfortunately, the plot_surface() function doesn’t accept a nested list for Z. We have to convert
it to a NumPy array for it to work. Have a look at chapter ?? for more information on NumPy. Using
mplot3d, we can plot this data as follows:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
And just a sneak-preview of what this exercise would look like when we go all-NumPy:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
x = np.arange(0, 21, 1)
y = np.linspace(0, 8, 21)
X, Y = np.meshgrid(x, y)
Z = X**2 / 1000
Exercise 9.1.1
In this exercise we’ll simulate a falling ball, where we’ll compare a situation without, and with drag. Our
code starts with the required import statements, and initialisation of our variables:
We want to numerically integrate our equations until we reach a target height ℎ = 0. Since we don’t
know how many timesteps are needed for this we’ll use a while-loop:
# With drag
a2 = (-1.0 * F + D) / m
vy2 = vy2 + a2 * dt
y2 = y2 + vy2 * dt
ttab.append(t)
y1tab.append(y1)
y2tab.append(y2)
plt.plot(ttab, y1tab)
plt.plot(ttab, y2tab)
plt.show()
Exercise 9.2.1
In this exercise we make a two-dimensional simulation. As you’ve seen in the corresponding chapter,
the difference with a one-dimensional simulation is not big, so the steps in this exercise are the same as
in the previous one: Set the initial values, create the empty lists for later plotting, and simulate until the
desired end condition using a while-loop. In the first step we make this simulation without drag:
delta = 0.01
s = 0
h = 1.0
theta = 30.0 * math.pi / 180.0
vx = 100 * math.cos(theta)
vy = 100 * math.sin(theta)
g = 9.81
stab = []
htab = []
while h >= 0:
# Acceleration only dependent on gravity
184 A. Answers to exercises
ay = -1.0 * g
ax = 0
vy = vy + ay * delta
vx = vx + ax * delta
h = h + vy * delta
s = s + vx * delta
htab.append(h)
stab.append(s)
plt.plot(stab, htab)
plt.ylim(0, 140)
plt.show()
In the second step we add a constant drag. This adds two lines of initialisation, and changes the
calculation of 𝑎𝑥 and 𝑎𝑦 . Also theta needs to be updated now:
delta = 0.01
s = 0
h = 1.0
theta = 30.0 * math.pi / 180.0
vx = 100 * math.cos(theta)
vy = 100 * math.sin(theta)
g = 9.81
Fd = 80
m = 10
stab = []
htab = []
while h >= 0:
# Acceleration dependent on gravity
# and constant drag
ay = (-1 * m * g -1 * Fd * math.sin(theta)) / m
ax = (-1 * Fd * math.cos(theta)) / m
vy = vy + ay * delta
vx = vx + ax * delta
h = h + vy * delta
s = s + vx * delta
htab.append(h)
stab.append(s)
plt.plot(stab, htab)
plt.ylim(0, 140)
plt.show()
In the final step we acknowledge that drag depends on airspeed. For this we need a calculation of total
drag, and a decomposition of this value into x/y components:
185
delta = 0.01
s = 0
h = 1.0
theta = 30.0 * math.pi / 180.0
vx = 100 * math.cos(theta)
vy = 100 * math.sin(theta)
g = 9.81
CD = 0.47
rho = 1.225
R = 0.15
S = math.pi * R**2
m = 10
stab = []
htab = []
while h >= 0:
# Calculate the actual drag
V2 = (vx**2 + vy**2)
V = math.sqrt(V2)
D = 0.5 * CD * rho * V2 * S
# Decompose in x/y components,
# making use of the ratio between vx and vy
Dx = -D * vx / V
Dy = -D * vy / V
# Acceleration dependent on gravity
# and drag
ay = (-1 * m * g + Dy) / m
ax = Dx / m
vy = vy + ay * delta
vx = vx + ax * delta
h = h + vy * delta
s = s + vx * delta
htab.append(h)
stab.append(s)
plt.plot(stab, htab)
plt.ylim(0, 140)
plt.show()
Exercise 10.1.1
In this exercise we’ll plot a hexagon inside a circle, using NumPy. This can be done very easily with
numpy.linspace(), when we consider that a hexagon is just a very coarse circle:
plt.plot(x1, y1)
plt.plot(x2, y2)
plt.show()
Exercise 10.1.2
There are different ways to get the diagonal of an array. One way that you will already be familiar with
is to use a for-loop:
import numpy as np
def printdiag(arr):
# Use end=' ' to avoid the newline of print
print('The numbers on the diagonal are:', end=' ')
for i in range(len(arr)):
print(arr[i,i], end=' ')
# A final print for the newline
print()
However, if we look further in the documentation of NumPy we find that the array type already provides
a method to return its diagonal:
import numpy as np
Exercise 10.1.3
Multi-dimensional slicing is one of the aspects where NumPy arrays really shine compared to nested
lists. While it is still easy enough to get rows from a nested list, its impossible to directly slice columns.
For arrays this is possible. First the list approach:
As you can see, this is quite tedious, since a for-loop is needed here. Because NumPy arrays support
multi-dimensional slicing, getting the first column of an array is much easier:
import numpy as np
Exercise 10.2.1
To extend the calculation of the temperature vector to 32 km altitude, one layer needs to be added to
the logical multiplication expression. With the given temperature gradient, and the starting temperature
of 216.65 K, the temperature equation for this layer becomes: 𝑇(ℎ) = 216.65 + 1 ⋅ (ℎ − 20). The code
with one layer extra becomes:
import numpy as np
import matplotlib.pyplot as plt
temp = (h < 11) * to11 + (h >= 11) * to20 + (h >= 20) * (to32 - to20)
plt.plot(h, temp)
plt.show()
Exercise 10.2.2
We have to plot a discontinuous function that is equal to 𝑥 2 for 𝑥 < 1, and equal to 6 − 𝑥 for 𝑥 > 1. We
can again solve this with boolean masks:
import numpy as np
import matplotlib.pyplot as plt
plt.plot(x, fx)
plt.show()
Exercise 10.2.3
For the array given in the exercise, we want to retrieve the numbers: (1) not divisible by 3 (arr%3 != 0),
(2) divisible by 5 (arr%5 == 0), (3) divisible by 3 and 5 ((arr%3 == 0) * (arr%5 == 0)). Finally
we want to set all numbers that are divisible by three to 42. This can be done as follows:
import numpy as np
arr = np.array([3, 4, 6, 10, 24, 89, 45, 43, 46, 99, 100])
# Not divisible by 3:
print('Elements not divisible by 3:', arr[arr%3 != 0])
# Divisible by 5:
print('Elements divisible by 5:', arr[arr%5 == 0])
# Divisible by 3 and 5:
print('Elements divisible by 3 and 5:', arr[(arr%3 == 0) * (arr%5 ==
↪ 0)])
# Set elements that are divisible by 3 to 42
arr[arr%3 == 0] = 42
188 A. Answers to exercises
Exercise 10.4.1
To solve the linear system 𝐴𝑥 = 𝑏, we need to right-multiply with the inverse of A: 𝐴−1 𝐴𝑥 = 𝑥 = 𝐴−1 𝑏.
We can invert a matrix using numpy.linalg.inv():
import numpy as np
A = np.array([[2, -5, 6], [4, 3, 8], [5, -2, 9]])
b = np.array([[3, 5, -1]]).T # .T because we need a column vector
# Calculate x using the inverse of A
Ainv = np.linalg.inv(A)
x = Ainv.dot(b) # You can also use Ainv @ b
Exercise 10.4.2
We want to find the speed of a motorboat, from the time it takes to make a trip up- and downstream. If
the motorboat speed is 𝑢, and the river current speed is 𝑣, we know that:
3 ∶ 10 ⋅ (𝑢 − 𝑣) = 29.5[km]
2 ∶ 00 ⋅ (𝑢 + 𝑣) = 29.5[km]
or rewritten:
29.5
𝑢−𝑣 = [km/h]
3 ∶ 10
29.5
𝑢+𝑣 = [km/h]
2 ∶ 00
1 −1 𝑢 29.5/3.16667
This can be written as a linear system 𝐴𝑥 = 𝑏, where 𝐴 = [ ], 𝑥 = [ ], and 𝑏 = [ ].
1 1 𝑣 29.5/2.1
This linear system can be solved in the same way as the previous exercise:
import numpy as np
A = np.array([[1, -1], [1, 1]])
b = np.array([[29.5 / 3.16667, 29.5 / 2.1]]).T # .T because we need a
↪ column vector
# Calculate x using the inverse of A
Ainv = np.linalg.inv(A)
x = Ainv.dot(b) # You can also use Ainv @ b
# Print the results
print('The motorboat speed is', x[0], 'km/h')
print('The current speed is', x[1], 'km/h')
Exercise 10.4.3
We want to find a linear trend 𝑦 = 𝑏1 + 𝑏2 𝑥, which best fits the ten datapoints from the function from
the exercise. This means that the following needs to be minimised:
𝑦1 − (𝑏1 + 𝑏2 𝑥1 )
𝑦2 − (𝑏1 + 𝑏2 𝑥2 )
⋮
𝑦1 0 − (𝑏1 + 𝑏2 𝑥1 0)
This can be rewritten as 𝑌 = 𝑋𝑏, where 𝑏 = [𝑏1 , 𝑏2 ], 𝑌 is a 𝑛 ×1 column vector with all values of y, and 𝑋
is a 𝑛 ×2 matrix, with on its first column all ones, and on its second column all values of 𝑥 corresponding
to the values in vector 𝑌.
189
The linear least squares method involves finding the minimum of the squared differences: ||𝑌 − 𝑋𝑏||2 .
This can be done by setting the derivative to zero, and solving for 𝑏:
𝜕(||𝑌 − 𝑋𝑏||2 ) 𝜕(𝑌 𝑇 𝑌 − 𝑌 𝑇 𝑋𝑏 − 𝑏 𝑇 𝑋 𝑇 𝑌 + 𝑏 𝑇 𝑋 𝑇 𝑋𝑏)
= = −2𝑋 𝑇 𝑌 + 2𝑋 𝑇 𝑋𝑏 = 0
𝜕𝑏 𝜕𝑏
This is again a linear system that we can solve, where the solution is: 𝑏 = (𝑋 𝑇 𝑋)−1 𝑋 𝑇 𝑌. Note that 𝑋 𝑇 𝑋
becomes a 2 × 2 matrix, and 𝑋 𝑇 𝑌 a 2 × 1 column vector. In code this becomes:
import numpy as np
Y = y.reshape((len(y), 1))
Note that if you dive into the NumPy docs you’ll find that the linalg sub module has a built-in least
squares function. So you can also do the following:
import numpy as np
X = np.ones((len(x), 2))
X[:,1] = x
Y = y.reshape((len(y), 1))
print(np.linalg.lstsq(X, Y))
Exercise 12.9.1
In this exercise we’re building a game in four steps:
a) The basics of initialising PyGame are always the same: import the module, initialise it, and set
up the screen:
import pygame as pg
screen = pg.display.set_mode(reso)
pg.quit()
b) In the next step we add a picture of an airplane to the screen. This involves loading the image,
giving it an appropriate scale, and a position on the screen. The final step is to blit the image to
the screen:
c) In step 3 we add a while-loop to allow us to update the screen with time. In this loop we use
numerical integration to give the airplane a horizontal speed on the screen.
For the numerical integration we need some more initialisation:
The drawing code block needs to be changed to have all drawing inside a while-loop, together
with the numerical integration:
screen.fill(blue)
screen.blit(plane, planerect)
191
pg.display.flip()
d) In the final step we enable moving the airplane vertically over the screen using the up and down
keys. This involves processing the pygame events using pg.event.get():
After these four steps you should have a complete (and very simple) game, with an airplane moving over
the screen from left to right, where you can control its height on the screen with the arrow keys!
B
Jupyter: the interactive Python notebook
You can also install jupyter with pip install. With Jupyter you can edit so-called iPython Notebooks.
These are a blend between Python and LateX with an excel-like functionality. The result are cells with
Python code, with their output and documentation in-between the cells. Each cell can run individually,
but they all access the same namespace, so one Notebook is like one module. It is accessed in a
server-like way, so with the browser.
The iPy Notebook is very convenient user interface for Scientific Computing tasks. It allows you to edit
snippets of Python/NumPy code (“Cells”) which you run but also can still edit later. You can access all
variables between cells, as if you are in one program, but you can also see the output (including plots)
in between the code. The result is a blend of a graphical calculator, Python and a spreadsheet program
like Excel. You can run an individual cell or all cells and see how the output changes. You can also add
cells with raw text to create a living document. Each notebook page can be saved (“downloaded” as
it is called in this client-server set-up) as either a .py Python file or a .ipynb Python notebook file. The
Notebook page is both a scratchpad as well as a publication.
An example of how this looks is shown below. As you can see all functions from math, numpy scipy
and matplotlib.pyplot have already been imported:
193
194 B. Jupyter: the interactive Python notebook
C
Programming and Scientific Computing
in Python - Cheat sheet
This appendix gives a quick overview of the concepts, modules, types, functions, and methods de-
scribed in this reader. Each of the following sections covers one chapter in the reader.
195
196 C. Programming and Scientific Computing in Python - Cheat sheet
• Variable-range iteration:
while condition:
# Do something as long as
# condition is true
# [cst] of x in radians
math.sin(x)
math.cos(x)
math.tan(x)
# arc[cst] in radians
math.acos(x)
math.asin(x)
math.atan(x)
math.atan2(y, x)
# (x**2+y**2)**0.5
math.hypot(x,y)
math.degrees(x) # rad to deg
math.radians(x) # deg to rad
# Hyperbolic functions
math.sinh(x)
math.cosh(x)
math.tanh(x)
math.asinh(x)
math.acosh(x)
math.atanh(x)
• Easy management of open files. You can # Get the next 300 characters
avoid having to keep track of open files by ↪ from file
using the with statement: spartans = fin.read(300)
plt.plot(x, y) plt.show()
plt.show()
• Multiple plots in one figure
plt.subplot(211)
• Making it look good: plt.plot(t, x)
plt.subplot(212)
plt.subplot(t, v)
X, Y, Z = ...
im = plt.imshow(Z)
cset = plt.contour(Z,
↪ [levels])
plt.show()