Appendix B — Self Study Session - Basics

Important

This lecture consists of several self study sessions!

In these sections you are supposed to work on the given topics on your own and in your own time. We will try to provide helpful references for books and online material that can help you but you are not limited to these materials.

In order to get an idea on how much you understand of the material we provide exercises.

In this section we are mainly concerned with the basics of Python, nevertheless you are expected to use git and pdm to solve the problems at hand.

Once you are familiar with how Python handles these topics you will be able to answer questions like:

This topics are covered in the following resources (given in no particular order):

Exercises

Exercise B.1 (Indent is important in Python) Explain the outputs of the following code snippets, (compare Matthes 2023, chap. 4):

band_members = ["Peter", "Bjorn", "John"]

for member in band_members:
print(member)
  Cell In[1], line 4
    print(member)
    ^
IndentationError: expected an indented block after 'for' statement on line 3
band_members = ["Peter", "Bjorn", "John"]

for member in band_members:
    print(f"{member} played great in this song.")
print(f"I can not wait to hear {member} play in the next song.")
Peter played great in this song.
Bjorn played great in this song.
John played great in this song.
I can not wait to hear John play in the next song.
band_members = ["Peter", "Bjorn", "John"]

for member in band_members:
    print(f"{member} played great in this song.")
    print(f"I can not wait to hear {member} play in the next song.")

    print(f"I can not wait to hear all of you at the next gig.")
Peter played great in this song.
I can not wait to hear Peter play in the next song.
I can not wait to hear all of you at the next gig.
Bjorn played great in this song.
I can not wait to hear Bjorn play in the next song.
I can not wait to hear all of you at the next gig.
John played great in this song.
I can not wait to hear John play in the next song.
I can not wait to hear all of you at the next gig.
band_members = ["Peter", "Bjorn", "John"]

for member in band_members:
    print(f"{member} played great in this song.")
    print(f"I can not wait to hear {member} play in the next song.")

print(f"I can not wait to hear all of you at the next gig.")
Peter played great in this song.
I can not wait to hear Peter play in the next song.
Bjorn played great in this song.
I can not wait to hear Bjorn play in the next song.
John played great in this song.
I can not wait to hear John play in the next song.
I can not wait to hear all of you at the next gig.

Exercise B.2 (Lists, conditionals and loops) For two given lists list1 and list2 of integers with equal length define the following new lists:

  1. list_zip which combines the two lists to a single list like a zip. So start with the first element of list1, followed by the first element of list2, than the second elements until all elements of the lists are combined.
  2. list_odd which contains only the odd integers of list1, followed by the odd integers of list2.
  3. list_zip_reverse which combines the elements of the lists like for list_zip but starts at the last element of list1.

Example:

For list1 = [1, 2, 3, 4, 5] and list2 = [11, 12, 13, 14, 15], we get

  • list_zip = [1, 11, 2, 12, 3, 13, 4, 14, 5, 15]
  • list_odd = [1, 3, 5, 11, 13, 15]
  • list_zip_reverse = [5, 15, 4, 14, 3, 13, 2, 12, 1, 11]

Exercise B.3 (Fizz Buzz) Write a program that prints the integers from 1 to 100 (inclusive). If, however, the number is a multiple of three then print Fizz instead, and if the number is a multiple of five then print Buzz.

If multiple conditions hold true then all replacements should be printed, for example 15 should print FizzBuzz.

Exercise B.4 (Difference between assignment, copy and deep copy) Have a look at the module copy and the functions copy, deepcopy as well as the standard assignment operator =.

Run the following code and explain its output:

import copy

list1 = [[1, 2, 3], [4, 5, 6], ["a", "b", "c"], 2]
list2 = list1
list3 = copy.copy(list1)
list4 = copy.deepcopy(list1)

print("List # \tID\tEntries")
print("1\t", id(list1), "\t", list1)
print("2\t", id(list2), "\t", list2)
print("3\t", id(list3), "\t", list3)
print("4\t", id(list4), "\t", list4)

list1[3] = -2

print("List # \tID\tEntries")
print("1\t", id(list1), "\t", list1)
print("2\t", id(list2), "\t", list2)
print("3\t", id(list3), "\t", list3)
print("4\t", id(list4), "\t", list4)

list2[2][2] = 9

print("List # \tID\tEntries")
print("1\t", id(list1), "\t", list1)
print("2\t", id(list2), "\t", list2)
print("3\t", id(list3), "\t", list3)
print("4\t", id(list4), "\t", list4)

list1.append([0, 8, 15])

print("List # \tID\tEntries")
print("1\t", id(list1), "\t", list1)
print("2\t", id(list2), "\t", list2)
print("3\t", id(list3), "\t", list3)
print("4\t", id(list4), "\t", list4)
List #  ID  Entries
1    139935949860224     [[1, 2, 3], [4, 5, 6], ['a', 'b', 'c'], 2]
2    139935949860224     [[1, 2, 3], [4, 5, 6], ['a', 'b', 'c'], 2]
3    139935949859712     [[1, 2, 3], [4, 5, 6], ['a', 'b', 'c'], 2]
4    139935949859200     [[1, 2, 3], [4, 5, 6], ['a', 'b', 'c'], 2]
List #  ID  Entries
1    139935949860224     [[1, 2, 3], [4, 5, 6], ['a', 'b', 'c'], -2]
2    139935949860224     [[1, 2, 3], [4, 5, 6], ['a', 'b', 'c'], -2]
3    139935949859712     [[1, 2, 3], [4, 5, 6], ['a', 'b', 'c'], 2]
4    139935949859200     [[1, 2, 3], [4, 5, 6], ['a', 'b', 'c'], 2]
List #  ID  Entries
1    139935949860224     [[1, 2, 3], [4, 5, 6], ['a', 'b', 9], -2]
2    139935949860224     [[1, 2, 3], [4, 5, 6], ['a', 'b', 9], -2]
3    139935949859712     [[1, 2, 3], [4, 5, 6], ['a', 'b', 9], 2]
4    139935949859200     [[1, 2, 3], [4, 5, 6], ['a', 'b', 'c'], 2]
List #  ID  Entries
1    139935949860224     [[1, 2, 3], [4, 5, 6], ['a', 'b', 9], -2, [0, 8, 15]]
2    139935949860224     [[1, 2, 3], [4, 5, 6], ['a', 'b', 9], -2, [0, 8, 15]]
3    139935949859712     [[1, 2, 3], [4, 5, 6], ['a', 'b', 9], 2]
4    139935949859200     [[1, 2, 3], [4, 5, 6], ['a', 'b', 'c'], 2]

Hint: The function id returns the identity of an object. It is guaranteed to be unique and constant for every object as long as it exists in memory.

Exercise B.5 (Monte Carlo simulations with random numbers, functions, conditionals and loops) A circle with radius \(r\) has an area of \[A_{circle} = \pi r^2\] and the square that encases it \[A_{square} = 4 r^2.\]

The ratio between the area of the circle and the area of the square is \[ \frac{A_{circle}}{A_{square}} = \frac{\pi r^2}{4 r^2} = \frac{\pi}{4} \] and therefore we can define \(\pi\) as \[ \pi = 4\frac{A_{circle}}{A_{square}}. \] The same is true if we just take the first quadrant, so \(\frac14\) of the square as well as the circle. This simplification will make the code more compact and faster.

The algorithm therefore becomes:

  1. For a given number \(N\) of uniformly scattered points in the quadrant, determine if these points are in the circle (distance less than 1) or not. We call the number of points in the circle \(M\).
  2. Estimate \(\pi\) by computing \[ \pi \approx 4 \frac{M}{N}. \tag{B.1}\]

To write this in Python follow these steps:

  1. Search for a module that allows to generate random values in python.
  2. Define a function def in_unit_circle(N) that computes and returns \(M\) from the function input \(N\), which is a single positive integer. Hint: You can interpret two numbers between \(0\) and \(1\) as cartesian coordinates of a point and the squared sum of them will tell you the distance of this point from the origin.
  3. Define a function def estimate_pi(N) that computes and returns the estimation of \(\pi\) with Equation B.1.
  4. Search for a module that provides the exact value of \(\pi\) and write a function def get_accuracy(N) that returns the absolute difference between the above function and \(\pi\).
  5. Test your function with different values of \(N\). Hint: \(N\) needs to be quite large to have multiple digits of \(\pi\) correct.

Exercise B.6 (Type hinting) For the functions estimate_pi and in_unit_circle in Exercise B.5 provide type hints to suggest only integers are allowed.

How can you give a further hint that the integer is always positive?

Is the type enforced during runtime?

Exercise B.7 (Functions can have optional arguments) Let us loot at the function

def greet(person, greeting):
    print(f"{greeting} {person}!")


greet("Peter", "Hi")
Hi Peter!
  1. Make Hi the default value for greeting.

  2. Try calling the function as greet(person="Peter", greeting="Hi") and greet(greeting="Hi", person="Peter") and explain the result.

  3. Not all default values are a good choice, explain the following output

    def add_item(item, items=[]):
       items.append(item)
       return items
    
    print(add_item("apple"))
    print(add_item("banana")) 
    ['apple']
    ['apple', 'banana']
  4. Change the code sucht that the output is

    ['apple']
    ['banana']

Exercise B.8 (Functions can be arguments) Provide a version of the function estimate_pi(N) from Exercise B.5 that has a second argument that specifies the function to call e.g. in_unit_circle.

Provide type hints for the new function.

Note: We will come back to this example later where this will be of use.

Exercise B.9 (Recursion with Fibonacci) Create a function that computes the \(n\)th Fibonacci number with recursion, i.e. a function calling itself. Here is the sequence:

\[ x_0 = 0,\quad x_1 = 1,\quad x_n = x_{n-1} + x_{n-2},\quad n\geq 2. \] The first couple of elements are: \(0\), \(1\), \(1\), \(2\), \(3\), \(5\), \(8\), \(13\), \(21\), \(34\), \(55\), \(89\), \(144\), \(233\), \(377\), \(610\), \(\ldots\)

Exercise B.10 (Working with dictionaries) Compute the arabic number from a roman numeral, for positive integers from \(1\) to \(3999\). In order to do so use the dict as provided below, if you see fit you may change or add entries:

roman = {I:1, IV: 4, V: 5, IX: 9, X: 10, XL: 40, 
         L: 50, XC: 90, C: 100, CD: 400, D: 500,
         CM:900, M: 1000}

Examples

Roman Arabic
MMMDCCXXIV 3724
MXCIV 1094
VIII 8

Exercise B.11 (Loop with key, value pair)  

  1. Take the dictionary of Exercise B.10 and write a loop that walks through all keys and values such that both are extracted directly in the loop.

  2. We have seen the for loop for lists, sometimes it is helpful to still get the index of the list in addition to the value, write a code snippet that produces the following output for the list ["first", "second", "third"]

    index = 0, value = 'first'
    index = 1, value = 'second'
    index = 2, value = 'third'

Exercise B.12 (Set, List and Dictionary Comprehensions)  

  1. Transform a list of tuples into a list of numbers using (nested) list comprehension, i.e. the list flat should be created and filled in one call from the content of list_of_sets.

    list_of_sets = [(1, 2, 3), (4, 5, 6), (7, 8)]
    flat = [1, 2, 3, 4, 5, 6, 7, 8]
  2. Is it still working if we include a set with a single entry?

    list_of_sets = [(1, 2, 3), (4, 5, 6), (7, 8), (9)]
    flat = [1, 2, 3, 4, 5, 6, 7, 8, 9]
  3. What happens when we indicate that it is supposed to be a set clearly?

    list_of_sets = [(1, 2, 3), (4, 5, 6), (7, 8), (9,)]
    flat = [1, 2, 3, 4, 5, 6, 7, 8, 9]
  4. What happens if we have lists instead of sets?

    list_of_lists = [[1, 2, 3], [4, 5, 6], [7, 8], [9]]
    flat = [1, 2, 3, 4, 5, 6, 7, 8, 9]
  5. Split a sentence give as string into a list of words (ignore ,, . for now).

    sentence = "I spy with my little eye a tricky" \
               " list and dict comprehension coming up"
    words = ["I", "spy", "with", "my", "little", "eye",
             "a", "tricky", "list", "and", "dict",
             "comprehension", "coming", "up"]
  6. From this list of words, get a set (therefore unique) lengths of these words, i.e. spy -> 3. Hint: you might want to look into the function map.

  7. Combine everything to get from a sentence a dictionary with length of word as key and a set of words as value. Test it with a couple of different sentences.

  8. Can you reduce this into dictionary comprehension , i.e. create the dictionary from scratch in one go.