Pyton for beginners
Zawartość:
Organizacja
- Ocena będzie wystawiana na podstawie ilości punktów z zaliczonych zadań.
- Zadania można oddawać na każdych zajęciach, wystarczy zademonstrować działanie programu oraz króciutko o nim opowiedzieć.
- Ćwiczenia z zestawu przypadającego na dane zajęcia można oddawać do
końca semestru ale …
- … jeżeli pod koniec semestru braknie czasu na zajęciach aby zadanie oddać to nie zostanie ono zalicone. W związku z tym proszę nie zwlekać z oddawaniem zadań.
- Skala ocen za punkty z zadań pojawi się około 10 tygodnia
- Na ostatnich zajęciach przeprowadzony jest egzamin, zagadnienia do zostaną rozesłane kilka tygodni wcześniej
- Ocena końcowa jest średnią ważoną oceny z zadań, projektu oraz
egzaminu:
- ocena z zadań: \(35\%\)
- ocena z projektu: \(35\%\)
- ocena z egzaminu: \(30\%\)
- Tematy projektów powinny być ustalone i skonsultowane z prowadzącym
do połowy semestru.
- Projekty można wykonywać w grupie. Maksymalny rozmiar grupy to \(3\) osoby.
Materiały dodatkowe
- some tutorials available online:
- exercises in python
- some interesting books, also available online:
- miscellaneous
- installing things
- see Appendix A of Automate the Boring Stuff with Python
- files and folders
- see chapter 10 of Automate the Boring Stuff with Python
- copying things
- paths, relative paths, etc.
- platform independent paths
- date and time
- “Never ever deal with time zones if you can help it.”
- see chapter 17 of Automate the Boring Stuff with Python
- geeks for geeks article
- manipulating images
- see chapter 19 of Automate the Boring Stuff with Python
- opencv
- spreadsheets
- see chapter 13 of Automate the Boring Stuff with Python
- running external programs from python, capturing output
- installing things
Zestawy zadań
Set 1
It’s \(3\) o’clock in the morning and you need to prepare some formula to feed your newborn baby. The instructions on the package are very specific: prepare a certain amount of water at a given temperature, add the correct amount of formula, mix and cool everything down before serving. In order to keep the baby safe from infections, all the water that you use needs to be pre-boiled. Here is your setup:
- You prepared a container of pre-boiled water cooled down to the temperature \(T_{\text{cool}}\).
- You prepared an empty, sterile feeding bottle.
- You have an electric kettle and measured that when the water comes out of the spout it has temperature \(T_{\text{hot}}\) (it’s slightly cooler then \(100^{\circ}C\)).
- You need to prepare \(V\) milliliters (or any other unit of volume) water at temperature \(T\) to mix up the formula for your little one. You do this by first pouring \(V_{\text{cool}}\) milliliters (or any other unit of volume) of the cooler water into the bottle and then filling the bottle up to \(V\) with the hot water from the kettle. How much cool water should you use?
The mathematics of this problem is very simple, see the APPENDIX. To make things more convenient implement the python function:
def calculateVcool(Tcool , Thot , V , T):
#...
return Vcoolthat returns the volume of the cooler water \(V_{\text{cool}}\) you need to pour into the bottle before pouring the remainder of the water from the kettle. This volume is calculated in such a way that when mixed the water has temperature \(T\).
Tip: If you find yourself in this scenario a infrared thermometer is a fantastic tool.
Turn the V, T, and Thot
arguments of function calculateVcool from exercise
A into optional arguments with realistic default values
for a \(3\) month old baby.
Write a python script that asks the user to input the arguments of
calculateVcool from A, runs the function
and prints the result.
Nobody has time to fiddle around with their laptop at \(3\) o’clock in the morning. Get the script from C to run on your phone (at your own risk :-) ).
Your significant other (SO) has the early morning shift with the baby and asked you to prepare a thermos with pre-boiled water. You have determined the temperature coefficient of the thermos (the mathematics of this is very simple, see the APPENDIX) and need to decide on the temperature of the water in the thermos so that in a couple of hours, when your SO needs to prepare the formula, the temperature of the water in the thermos is appropriate.
To help with this implement the python function:
def calculateTemperature(Tfinal , time , Troom , k ,
search = (0.0 , 100.0 , 1000)):
#...
return Twhere Tfinal is the desired temperature of the water
after time time, Troom is the ambient room
temperature and k is the temperature coefficient. The final
optional argument search determines the parameters of a
numerical simulation that will solve the problem. The returned value
T is the desired temperature of water in the thermos.
Use a simple numerical simulation to solve the problem. In this
simulation scan initial water temperature candidates in the range
search[0] \(\ldots\)
search[1]. The range is divided into search[2]
temperature values. For each temperature candidate, the final
temperature after time time is calculated (see
APPENDIX). Finally, out of all the temperature
candidates, the one whose final temperature is closest to the desired
Tfinal is returned as T. In your
implementation please use:
- a list comprehension,
- tuple unpacking,
- the functions and .
Tip: The exponent is available in the math library.
There is no need to install anything, it comes built in. Simply use
from math import exp
When mixing water to arrive at a given temperature, approximately (the notation is the same as in the exercises): \[ V_{\text{cool}} = V \frac{T_{\text{hot}} - T}{T_{\text{hot}} - T_{\text{cool}}} \] Does it matter if we use \(^{\circ}C\) or \(^{\circ}K\)? Is the formula any different if you prefer to pour the hot water first?
When water in a container is left in a room with ambient temperature \(T_{\text{room}}\) it will gradually, over time change it’s temperature to match the ambient temperature. This process can be approximately modeled by the formula: \[ \tau(t) = T_{\text{room}} - (T_{\text{room}} - \tau(0)) e^{-t/k} \] where \(\tau(0)\) is the initial temperature of water in the thermos and \(\tau(t)\) is the temperature in the thermos after time \(t\). The remaining notation is the same as in the exercises. Please note that a non-standard definition of the temperature coefficient \(k\) is used. The value of this coefficient can be worked out by measuring the temperature of the water in the thermos at two points in time and doing a little algebra. Does it matter if we use \(^{\circ}C\) or \(^{\circ}K\)? Is the formula any different if you prefer to pour the hot water first?
Set 2
Implement a function that takes an arbitrary number of arguments, say floating point numbers, and returns a dictionary containing:
- a list containing all the arguments
- the mean of the arguments
- the standard deviation of the arguments
Use this function in a script.
Implement a simple text based game. The gameplay and objective is completely up to you.
Please base your implementation on the following function:
def updateGameState(state)
# ...that updates the dictionary state in every iteration of
the main game loop:
while(!state["finished"]):
# ... At each iteration of the game loop print the available moves /
options available to the player and use the input function
to decide on how to proceed with the game play. Use if,
else, and elif statements to decide how the
game state will evolve. When the game is finished, change
the value of "finished" in state to
True. This will cause the main loop of the game to
terminate.
Add some elegant ASCI art to your
game. To do this please import the sys module and use the
sys.stdout.write method to implement the function:
def showGameState(state):
# ...that will print a beautiful graphical representation of the game state to “standard output”. Add this function to the main game loop. Please assume that we are working in a terminal that is \(80\) characters wide and \(40\) characters tall.
Tips:
- use two nested
forloops to sweep over all the \(80 \times 40\) “pixel” display we use in the game - use
ifand / or a dictionary to decide what character to put in each “pixel” - the new line character is
\n - this does not have to be very complicated, drawing a map with the location of the player is great but printing some basic information about the game statistics is also fine
Implement a “smart” function:
def smartFunction(a , b):
# ...
return cthat returns the result c = a + b. Since this operation
is very laborious and time consuming, the function memorizes previous
arguments a, b that were passed to it, it also
memorizes the result. This is all in the hope that when we pass those
arguments to the function again it won’t have to do all that work, it’ll
just recall the result.
In order to achieve this introduce a global variable
smartFunctionMemory that will contain a dictionary. The
keys in this dictionary are related to the arguments a,
b, and the values are related to the results
c.
What types of arguments can we pass into
smartFunction?
Implement the function:
def readState(path)
# ...
return statethat reads the state from a file located at path path.
The contents of this file is processed to obtain the saved state of a
previous game.
Implement the function:
def writeState(state , path)
# ...that writes the game state to a file located at path
path.
Add readState before the start of the game loop and
writeState after the loop to allow the user to save the
game progress.
Tip: Don’t worry we’ll discuss the details during class :-)
- simple
guessing game
- this is the example from class
- please have a look at the comments, in particluar they contain additiona information about reading and writing files
- game
of life
- see this Wikipedia article for more details
- run the game as usual
python ConowayGameOfLife.py- first press
cto change the state of chosen cells - next press
ito iterate - finally press
qto quit
- first press
- there are many comments
Set 3
Replace the repeated if, elif,
else statements from your game in Set 2
with match. If these statements don’t appear in the game,
please add some.
End your scripts from Set 1 with an infinite
while loop instead of a call similar to
input("Hit enter to exit."). Please use pass
in the infinite loop.
Import the time module and use the
time.time() function to measure the time the program spends
in the loop. If this time exceeds \(10\) seconds, ask the user if the program
should exit. If the program is supposed to wait reset the timer and run
the loop for another \(10\)
seconds.
Tip: use continue, and break.
Using the Jupyter notebook please investigate the logistic map.
Create a function that calculates the logistic map and draw the map
using the matplotlib library, we will discuss how this
library works during class. Make sure the notebook has a detailed
description of your approach, use literate
programming.
Re-implement the function from C to accept a keyword
argument drawFunction. If the value of this keyword
argument is not the default None then
drawFunction points to a function that draws the data and a
plot of the logistic map is produced automatically.
Test if this works using a lambda expression.
Can you use is to check if drawFunction has
the default value?
- Jupyter notebook from 24-10-2025 class.
Set 4
Implement a Reverse
Polish Notation calculator. Base your program on a stack implemented
as a python list. All operations of the calculator take the
stack, modify it and return the modified stack. For example adding two
numbers:
def add(stack):
x = stack.pop() # 1
y = stack.pop() # 2
stack.append(x + y) # 3
return stack # 4At 1 a number is popped from the end of the list
with pop resulting in a shorter stack. At
2 another number is popped from the stack. Finally in
3 the stack is modified by adding a number to the end
of the list with append. The new stack, shorter by one
number, is returned in 4.
Add more functions:
- multiply two numbers
- print the stack (does not modify the stack)
- print the last value from the stack
- using the
mathmodule add several more mathematical operations - print some help information (does not modify the stack)
- “quit” adds
Noneto the stack
Refer to the functions from a dictionary whose keys are python strings - the names of the functions. The values in the dictionaries are the functions themselves.
Create a while loop that checks if None is
in the stack using in, if it’s in the stack - exit. In the
loop ask the user to enter the command name, run the appropriate command
from the dictionary to modify the stack. If the command is not in the
dictionary keys, assume that the user entered a floating point number
and append it to the stack.
Make the code from A a little more bullet proof:
- make sure that the functions from A check if there are enough elements on the stack
- make sure that when the user enters a number to push to the stack -
it is a properly formatted floating point number (tip: use
tryandexcept, we will discuss this during class)
Add another module extra.py with additional functions.
Modify A to look inside this module for additional
functions (tip: use the __dict__ attribute, we will discuss
this during class).
Implement a simple class that iterates over the Fibonacci numbers. The constructor takes one argument - the number of Fibonacci numbers to iterate over. We will discuss the details during class.
Set 5
Implement two functions. The first one encodes the plaintext …
plaintext (string of characters):
def encode(key , plaintext):
# ...
# return ciphertextand the second one decodes the ciphertext … ciphertext
(string of characters):
def decode(key , ciphertext):
# ...
# return plaintextBoth functions take a string of characters, key, as the
first argument.
Use any cipher you like. We will discuss options during class. The extra point is for interesting solutions.
Rewrite the functions from A. Implement
encoder and decoder so that they can be
used:
myEncoder = encoder("secret_key")
myDecoder = decoder("secret_key")
plaintext = "this is the plaintext"
ciphertext = myEncoder(plaintext)
print(myDecoder(ciphertext) == plaintext) # trueUse two different methods:
encoderanddecoderare functions that return functionsmyEncoderandmyDecoderare objects of a class that has a__call__method
Using the argparse library implement a simple note taking application. The application has only one positional argument:
passwordwill be used as the key to encrypt and decrypt messages
and the following optional arguments:
--keywordsor-kwill be used to add single keywords, this option can be used multiple times--printor-pwill print all notes--printanyor-awill print only those notes that contain any of the keywords--printallor-owill print only those notes that contain all of the keywords--newor-nwill ask the user for the text of a new note with the appropriate keywords--deleteor-dwill delete a note with a given number
In your implementation:
- store the text of the note in encrypted form, use the functions from
the previous exercises for encrypting and decrypting, and
passwordas the key - use the
jsonmodule to store the notes in JSON format - store the notes in a single file located in the same directory as the script (we will discuss this during class)
- use a
setto store the keywords - apart from the keywords assign the creation date (using
datetime) to each note and a unique number - use formatted string literals when printing a note, make sure that the date, number, keywords, and note text are displayed in a clear way
- make sure that all the arguments have help information available
(using
argparse) - make sure that the program has a description information available
(using
argparse)
Extra point: replace the positional argument password
with an appropriate function from the getpass modle. Is
this safer? Why?
DISCLAMER: This note taking application is only as safe as our implmentation makes it. Use at your own risk!
Next time we will be talking about the numpy module. In
preparation please write a script that multiplies two \(1024 \times 1024\) matrices. Use python
lists and list comprehensions to implement the matrices. Measure how
much time the script takes to execute.
- some miscellaneous materials from class
- minimal
example of RPN calculator
- contains nested
try-catchblocks- this is not very elegant
- to exit in linux press CTRL + C
- contains nested
- some miscellaneous materials from 21 XI 2025
Set 6
Using the numpy library please implement the game of
life with periodic boundary conditions (the left half of the grid is
glued to the right, the top part of the grid is glued to the bottom …
essentially creating a torus). The implementation should be a single
script. The grid of cells is represented by a numpy array
with an integer dtype. When implementing a single iteration
of the game, please stick to the numpy library as strictly
as possible to boost performance. Test the performance using large grids
(1920 by 1080 and larger).
Tip: Use the numpy.roll function to obtain the state of
neighbouring cells. Visualization can be achieved using the
matplotlib.pyplot library functions, imshow
or matshow
function. We will cover the basic usage of matplotlib
during class.
Extend A to continuous values: the state of the cell can be dead (0), alive (1) or anything in between. There are many sensible ways of doing this. One way is “smooth life”, see the links:
Tip: The function convolve2d
from the scipy might be helpful. It works with
numpy arrays. Visualization can be achieved using the
matplotlib.pyplot library functions, imshow
or matshow
function. We will cover the basic usage of matplotlib
during class.
Add a simple interactive command line interface to A or B. The program should give the user at least the following options:
- save state of the game (the grid of cells) into a file with specified path
- load state of the game (the grid of cells) from a file with specified path
- run a specified amount of iterations
- quit
Be persistent, if the user types an incorrect value prompt him / her
again. To do this use try, except.
Wrap everything (A + C or B + C) into a class. Place the class definition in a separate module and use this module in a script.
Write a wrapper for the numpy array representing a grid
of cells in the game of life. Try to make this class immutable (is this
really possible in python?). Add the __hash__ and
__eq__ methods to the class.
… Obtaining the number of neighbours for the basic game of life …
Let’s assume that we start from random configuration of \(4 \times 4\) cells:
import numpy as np # 1
s = 4 # 2
state = np.random.randint(0 , high = 2 , size = (s , s)) # 3
print(state) # 4In 1 we import the numpy library, in
2 we set the size of the matrix containing cell states,
in 3 we draw a random distribution of cells (0 meaning
a dead cell 1 meaning a live cell, the integers are drawn from a uniform
distribution between \(0\) and
high - 1 \(= 1\)). Finally
4 prints our result:
[[0 0 0 0]
[0 1 1 0]
[1 1 0 1]
[0 1 0 1]]
To get a table whose values at a specific row and column correspond
the state if the eight neighbors (Up, Down , Right , Left , UpRight ,
DownLeft , UpLeft, DownRight) we first roll the arrays to
obtain:
U = np.roll(state , 1 , axis = 0)
D = np.roll(state , -1 , axis = 0)
R = np.roll(state , -1 , axis = 1)
L = np.roll(state , 1 , axis = 1)
UR = np.roll(np.roll(state , 1 , axis = 0) , -1 , axis = 1)
DL = np.roll(np.roll(state , -1 , axis = 0) , 1 , axis = 1)
UL = np.roll(np.roll(state , 1 , axis = 0) , 1 , axis = 1)
DR = np.roll(np.roll(state , -1 , axis = 0) , -1 , axis = 1)and then sum:
neighbors = U + D + R + L + UR + DL + UL + DR
print(neighbors)as a result, each cell has the following number of neighbors:
[[3 3 4 2]
[4 3 3 3]
[5 4 6 3]
[5 2 4 2]]
This should be correct if we assume periodic boundaries (the left edge is glued to the right edge and the top edge is glued to the bottom edge).
The next bit is tricky. There are many ways of implementing a single
iteration of the game. Since we are looking to eliminate regular python
loops to increase the speed of our program have a look at the following
functions np.ones, np.zeros,
np.where, np.logical_and,
np.logical_or and then have a close look the following
code:
allAlive = np.ones((s , s) , dtype = np.int64)
allDead = np.zeros((s , s) , dtype = np.int64)
np.where(np.logical_and(state == 0 , neighbors == 3) , allAlive , state)This can be easily extended to include the whole set of rules of the
game. Remember stick to numpy for performance.
… The same thing but this time with convolutions …
Let’s import the convolve2d function from the
scipy.signal library. Note that we are not importing the
whole package nut just one function:
from scipy.signal import convolve2dNext let’s try this:
k = np.array([[1 , 1 , 1] , [1 , 0 , 1] , [1 , 1 , 1]])
neighborsWithConvolve2D = convolve2d(state , k , mode = "same" , boundary="wrap")
print(neighborsWithConvolve2D)The result should be the same as neighbors:
[[3 3 4 2]
[4 3 3 3]
[5 4 6 3]
[5 2 4 2]]
The result is the same but the method is more general. We can look at
a wider neighborhood of a cell by modifying k.
… The user input is all wrong …
Tip: Saving a numpy array can be obtained by running
np.save (or numpy.save if we used just
import numpy). Loading a numpy array can be
obtained by running np.load. Both these functions raise
exceptions if they get a bad file path. This means that we can use them
inside try: and except: blocks. The function
int also raised exceptions when it’s string argument is not
a correctly formatted integer.
… Hiding class members …
A nice article on name mangling at geeksforgeeks.
… Calculating the hash of a numpy array …
To calculate the hash of the data that is contained inside a
numpy array a try
hash(a.data.tobytes()).
… Implementation using pygame …
See the pygame website for installation instructions. To implement the game of life, both the classic version and the smooth version, you can use the template. Please read the comments from the template carefully.
Set 7
Implement the smart function from exercise D in
Set 2. Use a function decorator. This decorator is a
class with a __call__ method. Objects of this class also
have a field that contains a dictionary with previous arguments and
return values.
Expand the decorator from the previous exercise to accept a function with an arbitrary number of arguments positional argument.
Expand the decorator from the previous exercise to accept a function that has an arbitrary number of positional and keyword arguments.
Define a class BadHash of mutable integer objects. Each
time a new instance of the object is created, increment a class
variable. Add with a __hash__ method to … CAREFULL THIS IS
A BIG MISTAKE! But we will go with it to explore the consequences. Use
this method with functions that use our decorator.
Define a class whose objects hold a numerical value. At minimum, the class should have the following methods apart from the constructor:
__add__,__sub__“magic” method to add and subtract objects of this class using the+and-operators__mul__,__truediv__“magic” methods to multiply and divide objects of this class using the*and/operators__neg__to get the negative number using the-operator__str__,__repr__to return the string representation of the number in objects of this class
In addition to performing numerical operations __add__,
__sub__, __mul__, __truediv__,
and __neg__ should appropriately increase the four class
counters that determine how many additions, multiplications, divisions
and negations were executed. Add the following class methods:
resetCountersresets counters that count the number of multiplications, additions, divisions and negationsgetCountersreturns a tuple with the counters of multiplications, additions, divisions and negations
Use this class to determine the computational complexity of matrix multiplication and calculating the application of a matrix to a vector.
- some miscellaneous code from class, please have a look at the comments
Set 8
In an era before electronic calculators, logarithms were king. Two usefull properties of the logarithm function: \[ \log{a b} = \log{a} + \log{b} \] \[ \log{a / b} = \log{a} - \log{b} \] make it possible to change multiplication and division into addition and subtraction which require much less effort. This observation lies at the heart two interesting old calculating methods: the slide rule, and tables of logarithms. We will be focusing on the latter.
Please read this section on fancier string formatting and create a string containing a table of logarithms:
-------------------
| x | Log(x) |
-------------------
| 1.00 | 0.0000 |
| 1.01 | 0.0043 |
| 1.02 | 0.0086 |
...
| 1.20 | 0.0792 |
...
| 2.00 | 0.3010 |
...
| 6.00 | 0.7782 |
...
| 10.0 | 1.0000 |
-------------------
the first column has numbers going from \(1\) to \(10\) (the step size is up to you). The second columns is a base \(10\) logarithm of the corresponding number in the first column.
How can we use this table in practice. Let’s say we want to multiply \(2\) by \(6\): \[ \log_{10}{2 \times 6} = \log_{10}{2} + \log_{10}{6} \approx 0.3010 + 0.7782 = 1.0792 = \log_{10}{x} \] where \(x\) is our approximate result (\(\approx 12\)). We can approximate the logarithms by looking at our table and addition is easy enough (a person equipped with only a book of logarithm tables and an abacus is a dangerous calculating machine). There is a problem though, if we look at the second column the value \(1.0792\) is out of range, how do we solve this? Simple, just subtract \(1\) (or divide by \(10\) depending on how you look at it): \[ \log_{10}(x / 10) = \log_{10}{x} - \log_{10}{10} = 1.0792 - 1.0 = 0.0792 \] The value \(0.0792\) is no longer out of range in the second column and corresponds to … \(1.20\) which is our apporximate (we are doing approximations here left and right, let’s not forget about this) result divided by \(10\). So to summarize logarithm tables, plus some extra tricks like dividing by ten or subtracting one, are a powerful calculating tool.
Save your table into a text file. Make sure that:
- the columns have consistent width
- one option is to round the numbers to a set precision
- other options are available in the tutorial
- the table has a header
- the extra points is for using block elements
instead of
|and+in drawing the frame of the table
Hint: logarithms are available in the math library. To
calculate the base \(10\) logarithm of
\(2.0\) you can use
math.log(2.0 , 10).
Change the formatting of your logarithm table to use \(\LaTeX\) syntax for tables. Make the table larger and split it into multiple separate tables. Each page should contain a single table. Add special values for \(\pi\), \(\e\) etc. Can we add more columns to perform calculations with square roots and trigonometric functions?
Save the string with your table into a file with a .tex
extension. Compile it with \(\LaTeX\).