From f0cabfbf29c16789b767a509bc179a32967ceaba Mon Sep 17 00:00:00 2001 From: Felder Sabrina Date: Tue, 17 Feb 2026 11:23:31 +0100 Subject: [PATCH 01/37] exercises from work --- .../learn-debug-with-idle.py | 16 +++ ch08-conditional-logic/add-some-logic.py | 14 ++ ...llenge-simulate-a-coint-toss-experiment.py | 0 ch08-conditional-logic/coinf-flip.py | 21 +++ ch08-conditional-logic/compare-values.py | 21 +++ .../control-the-flow-review-exercise.py | 39 +++++ ch08-conditional-logic/control-the-flow.py | 136 ++++++++++++++++++ ch08-conditional-logic/dice.py | 10 ++ ch08-conditional-logic/factors.py | 13 ++ ch08-conditional-logic/review-exercises.py | 30 ++++ ch08-conditional-logic/unfair-coin-flip.py | 33 +++++ 11 files changed, 333 insertions(+) create mode 100644 ch07-fiding-and-fixing-code-bugs/learn-debug-with-idle.py create mode 100644 ch08-conditional-logic/add-some-logic.py create mode 100644 ch08-conditional-logic/challenge-simulate-a-coint-toss-experiment.py create mode 100644 ch08-conditional-logic/coinf-flip.py create mode 100644 ch08-conditional-logic/compare-values.py create mode 100644 ch08-conditional-logic/control-the-flow-review-exercise.py create mode 100644 ch08-conditional-logic/control-the-flow.py create mode 100644 ch08-conditional-logic/dice.py create mode 100644 ch08-conditional-logic/factors.py create mode 100644 ch08-conditional-logic/review-exercises.py create mode 100644 ch08-conditional-logic/unfair-coin-flip.py diff --git a/ch07-fiding-and-fixing-code-bugs/learn-debug-with-idle.py b/ch07-fiding-and-fixing-code-bugs/learn-debug-with-idle.py new file mode 100644 index 0000000..3424883 --- /dev/null +++ b/ch07-fiding-and-fixing-code-bugs/learn-debug-with-idle.py @@ -0,0 +1,16 @@ +for i in range(1,4): + j = i * 2 + print(f"i is {i} and j is {j}") + +# Buggy code +def add_underscores(word): + new_word = "_" + # using letter instead of i is considered more pythonic + for letter in word: + new_word = new_word + letter + "_" + #print(f"i = {i}; new_word = {new_word}") + return new_word + +phrase = "hello" +print("Call add_underscores(phrase) results in: ") +print(add_underscores(phrase)) diff --git a/ch08-conditional-logic/add-some-logic.py b/ch08-conditional-logic/add-some-logic.py new file mode 100644 index 0000000..f469c90 --- /dev/null +++ b/ch08-conditional-logic/add-some-logic.py @@ -0,0 +1,14 @@ +''' +1. Figure out what the result will be (True or False) when evaluating +the following expressions, then type them into the interactive win- +dow to check your answers: +• (1 <= 1) and (1 != 1) ==> False +• not (1 != 2) ==> False +• ("good" != "bad") or False ==> True +• ("good" != "Good") and not (1 == 1) ==> False +2. Add parentheses where necessary so that each of the following ex- +pressions evaluates to True: +• False == not True +• True and False == True and False +• not True and "A" == "B" ==> not (True and "A" == "B") +''' diff --git a/ch08-conditional-logic/challenge-simulate-a-coint-toss-experiment.py b/ch08-conditional-logic/challenge-simulate-a-coint-toss-experiment.py new file mode 100644 index 0000000..e69de29 diff --git a/ch08-conditional-logic/coinf-flip.py b/ch08-conditional-logic/coinf-flip.py new file mode 100644 index 0000000..02eb200 --- /dev/null +++ b/ch08-conditional-logic/coinf-flip.py @@ -0,0 +1,21 @@ +import random + +def coin_flip(): + """Randomly return 'heads' or 'tails'""" + if random.randint(0, 1) == 0: + return 'heads' + else: + return 'tails' + +# First initialize the taallies to 0 +heads_tally = 0 +tails_tally = 0 + +for trial in range(50_000): + if coin_flip() == 'heads': + heads_tally = heads_tally + 1 + else: + tails_tally = tails_tally +1 + +ratio = heads_tally / tails_tally +print(f"The ratio of head to tails is {ratio}") diff --git a/ch08-conditional-logic/compare-values.py b/ch08-conditional-logic/compare-values.py new file mode 100644 index 0000000..c388d75 --- /dev/null +++ b/ch08-conditional-logic/compare-values.py @@ -0,0 +1,21 @@ +''' +1. For each of the following conditional expressions, guess whether +they evaluate to True or False. Then type them into the interactive +window to check your answers: +• 1 <= 1 True +• 1 != 1 False +• 1 != 2 True +• "good" != "bad" True +• "good" != "Good" True +• 123 == "123" False + + +2. For each of the following expressions, fill in the blank (indicated by +__) with an appropriate Boolean comparator so that the expression +evaluates to True: +• 3 __ 4 +• 10 __ 5 +• "jack" __ "jill" +• 42 __ "42" +''' + diff --git a/ch08-conditional-logic/control-the-flow-review-exercise.py b/ch08-conditional-logic/control-the-flow-review-exercise.py new file mode 100644 index 0000000..955d11d --- /dev/null +++ b/ch08-conditional-logic/control-the-flow-review-exercise.py @@ -0,0 +1,39 @@ +''' +1. Write a program that prompts the user to enter a word using the +input() function and compares the length of the word to the num- +ber five. The program should display one of the following outputs, +depending on the length of the user’s input: +• "Your input is less than 5 characters long" +• "Your input is greater than 5 characters long" +• "Your input is 5 characters long" +''' + +goal = 5 +user_input = input("Enter a word: ") +input_len = len(user_input) + + +if input_len < goal: + print(f"Your input is less than {goal} characters long") +elif input_len > goal: + print(f"Your input is greater than {goal} characters long") +else: + print(f"Your input is {goal} characters long") + + +''' +2. Write a program that displays "I'm thinking of a number between 1 +and 10. Guess which one." Then use input() to get a number from +the user. If the user inputs the number 3, then the program should +display "You win!" For any other input, the program should display +"You lose." +''' + +num = 3 +print("\nI'm thinking of a number between 1 and 10. Guess which one.") +user_guess = int(input("What's your guess? ")) + +if user_guess == num: + print("You win!") +else: + print("You lose.") diff --git a/ch08-conditional-logic/control-the-flow.py b/ch08-conditional-logic/control-the-flow.py new file mode 100644 index 0000000..f35d6fc --- /dev/null +++ b/ch08-conditional-logic/control-the-flow.py @@ -0,0 +1,136 @@ +div_line = 50 * '#' + +print(f"\n{div_line}\nThe Start of sample 1\n{div_line}\n") + +if 2 + 2 == 4: + print("2 and 2 is 4") + +if 2 + 2 == 5: + print("Is this the mirror universe?") + + + + +# selective code execution on condition +# if keyword +print(f"\n{div_line}\nThe Start of 'if' sample\n{div_line}\n") + +grade = 40 + +if grade >= 70: + print("You passed the class!") + +if grade < 70: + print("You did not pass the class :( ") + +print("Thank you for attending.") + +# if - else keywords +print(f"\n{div_line}\nThe Start of 'if - else' sample\n{div_line}\n") + +if grade >= 70: + print("You passed the class!") +else: + print("You did not pass the class :( ") + +print("Thank you for attending.") + +# elif keyword +print(f"\n{div_line}\nThe Start of 'elif' sample\n{div_line}\n") + +grade = 85 + +if grade >= 90: + print("You passed the class with an A.") +elif grade >= 80: + print("You passed the class with an B.") +elif grade >= 70: + print("You passed the class with a C.") +else: + print("You did not pass the class :( ") + +print("Thank you for attending") + + +# nested if statements +print(f"\n{div_line}\nThe Start of nested if statements sample\n{div_line}\n") + +sport = input("Enter a sport: ") +p1_score = int(input("Enter player 1 score: ")) +p2_score = int(input("Enter player 2 score: ")) + +# 1 basketball + +if sport.lower() == "basketball": + if p1_score == p2_score: + print("The game is a draw.") + elif p1_score > p2_score: + print("Player 1 wins.") + else: + print("Player 2 wins.") + +# 2 golf +elif sport.lower() == "golf": + if p1_score == p2_score: + print("The game is a draw.") + elif p1_score < p2_score: + print("Player 1 wins.") + else: + print("Player 2 wins.") + +# 2 other sports +else: + print("Unknown sport") + + +# refactor to reduce complexity + +print(f"\n{div_line}\nThe Start of refactored nested if statements sample\n{div_line}\n") + +sport = input("Enter a sport: ") +p1_score = int(input("Enter player 1 score: ")) +p2_score = int(input("Enter player 2 score: ")) + +# same score is a draw regardless of the sport +if p1_score == p2_score: + print("The game is a draw.") + +elif sport.lower() == "basketball": + if p1_score > p2_score: + print("Player 1 wins.") + else: + print("Player 2 wins.") + +# 2 golf +elif sport.lower() == "golf": + if p1_score < p2_score: + print("Player 1 wins.") + else: + print("Player 2 wins.") + +# 2 other sports +else: + print("Unknown sport") + +# refactor to use compount conditional expressions: +print(f"\n{div_line}\nThe Start of refactored code using compound conditional\n" + + f"expressions sample\n{div_line}\n") + +sport = input("Enter a sport: ") +p1_score = int(input("Enter player 1 score: ")) +p2_score = int(input("Enter player 2 score: ")) + +sport = sport.lower() + +if p1_score == p2_score: + print("The game is a draw") +elif (sport == "basketball") or (sport == "golf"): + p1_wins_bball = (sport == "basketball") and (p1_score > p2_score) + p1_wins_golf = (sport == "golf") and (p1_score < p2_score) + p1_wins = p1_wins_bball or p1_wins_golf + if p1_wins: + print("Player 1 wins.") + else: + print("Player 2 wins.") +else: + print("Unknown sport") diff --git a/ch08-conditional-logic/dice.py b/ch08-conditional-logic/dice.py new file mode 100644 index 0000000..7474e08 --- /dev/null +++ b/ch08-conditional-logic/dice.py @@ -0,0 +1,10 @@ +import random + +def dice(): + ''' Throw a dice and return the result''' + result = random.randint(1, 6) + print(result) + return result + +for num in range(0, 10): + dice() diff --git a/ch08-conditional-logic/factors.py b/ch08-conditional-logic/factors.py new file mode 100644 index 0000000..408c57a --- /dev/null +++ b/ch08-conditional-logic/factors.py @@ -0,0 +1,13 @@ +''' +Write a program called factors.py that asks the user to input a posi- +tive integer and then prints out the factors of that number. +''' + +user_input = int(input("Enter a positive integer: ")) + +for num in range(1, user_input + 1): + if user_input % num == 0: + print(f"{num} is a factor of {user_input}") + + +# did it! the wording in the sample solution is a bit different diff --git a/ch08-conditional-logic/review-exercises.py b/ch08-conditional-logic/review-exercises.py new file mode 100644 index 0000000..127a1b7 --- /dev/null +++ b/ch08-conditional-logic/review-exercises.py @@ -0,0 +1,30 @@ +''' +Docstring for ch08-conditional-logic.review-exercises + +1. Write a function called roll() that uses randint() to simulate rolling +a fair die by returning a random integer between 1 and 6. + +2. Write a program that simulates ten thousand rolls of a fair die and +displays the average number rolled. +''' + +# exercise 1 + +import random + +def roll(): + ''' randomly returns an integer between 1 and 6, inclusive ''' + return random.randint(1,6) + +dice = roll() +print(dice) + +# exercise 2 + +sum_of_rolls = 0 + +for trial in range(10_000): + sum_of_rolls += roll() + +average = sum_of_rolls / 10000 +print(f"The average number rolled is {average}") \ No newline at end of file diff --git a/ch08-conditional-logic/unfair-coin-flip.py b/ch08-conditional-logic/unfair-coin-flip.py new file mode 100644 index 0000000..0b55499 --- /dev/null +++ b/ch08-conditional-logic/unfair-coin-flip.py @@ -0,0 +1,33 @@ +import random + +# For example, unfair_coin_flip(.7) has a 70 percent chance of returning 'tails' +def unfair_coin_flip(probability_of_tails): + ''' Randomly returns 'heads' or 'tails' with a given probability 0.x of returning 'tails' ''' + if random.random() < probability_of_tails: + return 'tails' + else: + return 'heads' + +heads_tally = 0 +tails_tally = 0 + +for trial in range(10_000): + if unfair_coin_flip(.7) == "heads": + heads_tally = heads_tally + 1 + else: + tails_tally += 1 + +ratio = heads_tally / tails_tally +print(f"The ratio of heads to tails is {ratio}") + +heads_tally = 0 +tails_tally = 0 + +for trial in range(10_000): + if unfair_coin_flip(.4) == "heads": + heads_tally += 1 + else: + tails_tally += 1 + +ratio = heads_tally / tails_tally +print(f"The ratio of heads to tails is {ratio}") From 7425bb30c881284cb89bcec8d196e3520088f26f Mon Sep 17 00:00:00 2001 From: Felder Sabrina Date: Tue, 17 Feb 2026 15:48:41 +0100 Subject: [PATCH 02/37] ch08-challenge2 --- .../simulate-an-election.py | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 ch08-conditional-logic/simulate-an-election.py diff --git a/ch08-conditional-logic/simulate-an-election.py b/ch08-conditional-logic/simulate-an-election.py new file mode 100644 index 0000000..63ac3ae --- /dev/null +++ b/ch08-conditional-logic/simulate-an-election.py @@ -0,0 +1,44 @@ +import random + +''' +With some help from the random module and a little conditional logic, +you can simulate an election between two candidates. +Suppose two candidates, Candidate A and Candidate B, are running +for mayor in a city with three voting regions. The most recent polls +show that Candidate A has the following chances for winning in each +region: +• Region 1: 87 percent chance of winning +• Region 2: 65 percent chance of winning +• Region 3: 17 percent chance of winning +Write a program that simulates the election ten thousand times and +prints the percentage of times in which Candidate A wins. +To keep things simple, assume that a candidate wins the election if +they win in at least two of the three regions +''' + +region1_chance = 0.87 +region2_chance = 0.65 +region3_chance = 0.17 + +simulation_runs = 10_000 +candidate_a_win_count = 0 + +def election(chance_candidate_a_wins): + """ Randomly returns 'True' or 'False' depending on the result """ + if random.random() <= chance_candidate_a_wins: + return True + else: + return False + +for election_run in range(simulation_runs): + region1_result = election(region1_chance) + region2_result = election(region2_chance) + region3_result = election(region3_chance) + + if (region1_result and region2_result) or (region1_result and region3_result) or (region2_result and region3_result): + candidate_a_win_count +=1 + +candidate_a_win_quote = candidate_a_win_count / simulation_runs * 100 + +print(f"Candidate A wins in {candidate_a_win_quote: .2f} percent of simulations") + From 0103a107d7f812a07417dbd5c2ab448aa010d720 Mon Sep 17 00:00:00 2001 From: Felder Sabrina Date: Tue, 17 Feb 2026 15:48:52 +0100 Subject: [PATCH 03/37] ch09-tuple --- ch09-lists-tuples-and-dictionaries/tuple.py | 27 +++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 ch09-lists-tuples-and-dictionaries/tuple.py diff --git a/ch09-lists-tuples-and-dictionaries/tuple.py b/ch09-lists-tuples-and-dictionaries/tuple.py new file mode 100644 index 0000000..20e080a --- /dev/null +++ b/ch09-lists-tuples-and-dictionaries/tuple.py @@ -0,0 +1,27 @@ +# Tuple literal +# is a tuple that is written out explicitly as a comma-separated list of values +# sourrounded by parantheses. + +my_first_tuple = (1, 2, 3) +print(type(my_first_tuple)) + +mixed_tuple = (1, 2.0, "three") +print(type(mixed_tuple)) + +empty_tuple = () +print(type(empty_tuple)) + +# create tuple with exactly one element +x = (1) +print(type(x)) + +# add a comma after the only element +x = (1,) +print(type(x)) + +# Built-in tuple() +# you can use the built-in tuple() to create a tuple from another sequence type, such as a string: +print(tuple("Python")) + +# test +print(tuple(range(1, 5))) \ No newline at end of file From 5957f55b1f2f5ee83239d1e693af76be764e591d Mon Sep 17 00:00:00 2001 From: Felder Sabrina Date: Mon, 23 Feb 2026 10:03:14 +0100 Subject: [PATCH 04/37] progressing tuples, lists --- .../.ipynb_checkpoints/tuple-checkpoint.py | 49 ++++++++ .../basic-list-operations.py | 23 ++++ .../lists-are.py | 51 ++++++++ ch09-lists-tuples-and-dictionaries/tuple.py | 113 +++++++++++++++++- 4 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 ch09-lists-tuples-and-dictionaries/.ipynb_checkpoints/tuple-checkpoint.py create mode 100644 ch09-lists-tuples-and-dictionaries/basic-list-operations.py create mode 100644 ch09-lists-tuples-and-dictionaries/lists-are.py diff --git a/ch09-lists-tuples-and-dictionaries/.ipynb_checkpoints/tuple-checkpoint.py b/ch09-lists-tuples-and-dictionaries/.ipynb_checkpoints/tuple-checkpoint.py new file mode 100644 index 0000000..178c17d --- /dev/null +++ b/ch09-lists-tuples-and-dictionaries/.ipynb_checkpoints/tuple-checkpoint.py @@ -0,0 +1,49 @@ +# Tuple literal +# is a tuple that is written out explicitly as a comma-separated list of values +# sourrounded by parantheses. + +my_first_tuple = (1, 2, 3) +print(type(my_first_tuple)) + +mixed_tuple = (1, 2.0, "three") +print(type(mixed_tuple)) + +empty_tuple = () +print(type(empty_tuple)) + +# create tuple with exactly one element +x = (1) +print(type(x)) + +# add a comma after the only element +x = (1,) +print(type(x)) + +# Built-in tuple() +# you can use the built-in tuple() to create a tuple from another sequence type, such as a string: +print(tuple("Python")) + +# test +print(tuple(range(1, 5))) + +vowels = ("a", "e", "i", "o", "u") + +for vowel in vowels: + print(vowel.upper()) + +# Tuple unpacking +# third but less common way of creating a tuple + +coordinates = 4.21, 9.29 +print(type(coordinates)) +print(coordinates) + +x, y = coordinates +print(f"x = {x}") +print(f"y = {y}") + +# multiple variable assignemnts in a single line +name, age, occupation = "Adisa", 34, "programmer" +print(name) +print(age) +print(occupation) \ No newline at end of file diff --git a/ch09-lists-tuples-and-dictionaries/basic-list-operations.py b/ch09-lists-tuples-and-dictionaries/basic-list-operations.py new file mode 100644 index 0000000..f852093 --- /dev/null +++ b/ch09-lists-tuples-and-dictionaries/basic-list-operations.py @@ -0,0 +1,23 @@ +# indexing and slicing +numbers = [1, 2, 3, 4] +print(f"The original numbers list: {numbers}") +print("Next the output of numbers[1]:") +print(numbers[1]) + +new_numbers = numbers[1:3] +print(f"The new list from the original list using slice notation 'numbers[1:3]'") +print(new_numbers) + +# like in tuples we can use 'in' to check for the existence of list elements +# Check existence of an element +print("Test for 'Bob' in numbers") +print("Bob" in numbers) + +# personal test how to handle numbers within in check +print(3 in numbers) + +# iterate lists, print only even numbers +print("Lists are iterable. Print only the even number from the list") +for number in numbers: + if number % 2 == 0: + print(number) \ No newline at end of file diff --git a/ch09-lists-tuples-and-dictionaries/lists-are.py b/ch09-lists-tuples-and-dictionaries/lists-are.py new file mode 100644 index 0000000..09032e1 --- /dev/null +++ b/ch09-lists-tuples-and-dictionaries/lists-are.py @@ -0,0 +1,51 @@ +# Starting with lists, the first mutable squence we encounter + +colors = ["red", "yellow", "green", "blue"] +print(type(colors)) + +print(colors) + +# a list can consist of elements of different datatypes +sample_list = [ 1, 2.0, "three"] +print(type(sample_list)) +print(sample_list) + +# create a list using the built-in list() +# create a list from a tuple + +print(list((1, 2, 4,))) + +# create a list from a string +print(list("Python")) + +# create a list from a string using split() +groceries = "eggs, milk, cheese, chocolate" +grocery_list = groceries.split(", ") +print(grocery_list) + +# The string argument passed to .split() is called the separator +# by changing the separator, you can split strings into lists in +# numerous ways: + +# split string on semicolon +print("a;b;c".split(";")) + +# split string on spaces +print("The quick brown fox".split(" ")) + +# split string on multiple characters +# .split() always returns a list whose length is one more than the number +# of separators contained in the string. The separator "ba" appears twice, +# so the list returned by split() has three elements. +print("abbaabba".split("ba")) + +# it the separator is not contained in the string at all, then .split() +# returns a list with the string as its only element +print("abbaabba".split("c")) + +""" +In all, you’ve seen three ways to create a list: +1. A list literal +2. The built-in list() +3. The string .split() method +""" diff --git a/ch09-lists-tuples-and-dictionaries/tuple.py b/ch09-lists-tuples-and-dictionaries/tuple.py index 20e080a..d24e7e4 100644 --- a/ch09-lists-tuples-and-dictionaries/tuple.py +++ b/ch09-lists-tuples-and-dictionaries/tuple.py @@ -24,4 +24,115 @@ print(tuple("Python")) # test -print(tuple(range(1, 5))) \ No newline at end of file +print(tuple(range(1, 5))) + +vowels = ("a", "e", "i", "o", "u") + +for vowel in vowels: + print(vowel.upper()) + +# Tuple unpacking +# third but less common way of creating a tuple + +coordinates = 4.21, 9.29 +print(type(coordinates)) +print(coordinates) + +x, y = coordinates +print(f"x = {x}") +print(f"y = {y}") + +# multiple variable assignemnts in a single line +# NOTE: Assigning more than two or three variables this way can make it +# difficult to tell which value is assigned to which variable name! +name, age, occupation = "Adisa", 34, "programmer" +print(name) +print(age) +print(occupation) + +# The number of names and the number of values must be equal + +try: + a, b, c, d = 1, 2, 3 +except ValueError: + print("ValueError exception occured: ", ValueError.__doc__) + +try: + a, b, c = 1, 2, 3, 4 +except ValueError: + print("ValueError exception occured: ", ValueError.__doc__) + +# checking the existence of values with 'in' + +o_result = "o" in vowels +print("o in vowels?", o_result) + +x_result = "x" in vowels +print("x in vowels?", x_result) + +# Returning multiple values from a function + +def adder_substractor(num1, num2): + ''' Returns a tuple in which the first element is the sum of the two numbers + and the second element is the difference between the two numbers''' + return (num1 + num2, num1 - num2) + +a = 3 +b = 2 + +print(adder_substractor(a, b)) + +a = 4 +b = 8 +print(adder_substractor(a, b)) + +a = 0 +print(adder_substractor(a, b)) + +# review exercises + +""" +1. Create a tuple literal named cardinal_numbers that holds the strings +"first", "second", and "third", in that order. +2. Using index notation and print(), display the string at index 1 in +cardinal_numbers. +3. In a single line of code, unpack the values in cardinal_numbers into +three new strings named position1, position2, and position3. Then +print each value on a separate line. +4. Using tuple() and a string literal, create a tuple called my_name that +contains the letters of your name. +5. Check whether the character "x" is in my_name using the in keyword. +6. Create a new tuple containing all but the first letter in my_name using +slice notation. +""" + +# 1 +cardinal_numbers = ("first", "second", "third") + +# 2 +print(cardinal_numbers[1]) + +# 3 +position1, position2, position3 = cardinal_numbers + +print(position1) +print(position2) +print(position3) + +# 4 +my_name = tuple("Sabrina") + +# 5 +contains_x = "x" in my_name +print("x in my_name?") +print(contains_x) + +# or short +print("x" in my_name) + +#6 +sliced_tuple = my_name[1:] +print(sliced_tuple) + +# or shorthand +print(my_name[1:]) \ No newline at end of file From dae6da4af46ecfbbd770e15726fb95eded83d5e0 Mon Sep 17 00:00:00 2001 From: Felder Sabrina Date: Mon, 2 Mar 2026 15:34:38 +0100 Subject: [PATCH 05/37] added exercism exercises --- exercism.org/armstrong_number.py | 18 +++++++ exercism.org/collatz_conjecture.py | 39 +++++++++++++++ exercism.org/meltdown-mitigation.py | 78 +++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 exercism.org/armstrong_number.py create mode 100644 exercism.org/collatz_conjecture.py create mode 100644 exercism.org/meltdown-mitigation.py diff --git a/exercism.org/armstrong_number.py b/exercism.org/armstrong_number.py new file mode 100644 index 0000000..d20bda4 --- /dev/null +++ b/exercism.org/armstrong_number.py @@ -0,0 +1,18 @@ +def is_armstrong_number(number): + original_number = number + numbers = [] + + while number > 0: + num = number % 10 + numbers.insert(0, num) + number = number // 10 + + numbers_count = len(numbers) + numbers_sum = 0 + + for member in numbers: + numbers_sum += member ** numbers_count + + return numbers_sum == original_number + +print(is_armstrong_number(5)) diff --git a/exercism.org/collatz_conjecture.py b/exercism.org/collatz_conjecture.py new file mode 100644 index 0000000..ca11659 --- /dev/null +++ b/exercism.org/collatz_conjecture.py @@ -0,0 +1,39 @@ +""" Module to check for collatz conjecture """ + +def steps(number): + """ Calculate how many steps it takes to reach 1 using Collatz Conjecture """ + if number < 0: + raise ValueError("Only positive integers are allowed.") + return + if number == 0: + raise ValueError("0 is an invalid input.") + return + + current_value = number + count = 0 + + while current_value > 1: + if current_value % 2 == 0: + current_value = current_value / 2 + else: + current_value = current_value * 3 + 1 + count += 1 + + return count + +print(steps(2)) +print(steps(21)) +print(steps(52)) + +try: + result = steps(-2) + print(result) +except ValueError: + print("ValueError occured") + +try: + print(steps(0)) +except: + print("Except hit") + +print("Done") diff --git a/exercism.org/meltdown-mitigation.py b/exercism.org/meltdown-mitigation.py new file mode 100644 index 0000000..abeca4c --- /dev/null +++ b/exercism.org/meltdown-mitigation.py @@ -0,0 +1,78 @@ +"""Functions to prevent a nuclear meltdown.""" + + +def is_criticality_balanced(temperature, neutrons_emitted): + """Verify criticality is balanced. + + :param temperature: int or float - temperature value in kelvin. + :param neutrons_emitted: int or float - number of neutrons emitted per second. + :return: bool - is criticality balanced? + + A reactor is said to be balanced in criticality if it satisfies the following conditions: + - The temperature is less than 800 K. + - The number of neutrons emitted per second is greater than 500. + - The product of temperature and neutrons emitted per second is less than 500000. + """ + + balanced_temp = temperature < 800 + balanced_neutrons = neutrons_emitted > 500 + balanced_product = temperature * neutrons_emitted < 500_000 + + return balanced_temp and balanced_neutrons and balanced_product + + +def reactor_efficiency(voltage, current, theoretical_max_power): + """Assess reactor efficiency zone. + + :param voltage: int or float - voltage value. + :param current: int or float - current value. + :param theoretical_max_power: int or float - power that corresponds to a 100% efficiency. + :return: str - one of ('green', 'orange', 'red', or 'black'). + + Efficiency can be grouped into 4 bands: + + 1. green -> efficiency of 80% or more, + 2. orange -> efficiency of less than 80% but at least 60%, + 3. red -> efficiency below 60%, but still 30% or more, + 4. black -> less than 30% efficient. + + The percentage value is calculated as + (generated power/ theoretical max power)*100 + where generated power = voltage * current + """ + + generated_power = voltage * current + + percentage = (generated_power / theoretical_max_power) * 100 + + if percentage >= 80: + return 'green' + if 80 > percentage >= 60: + return 'orange' + if 60 > percentage >= 30: + return 'red' + return 'black' + + +def fail_safe(temperature, neutrons_produced_per_second, threshold): + """Assess and return status code for the reactor. + + :param temperature: int or float - value of the temperature in kelvin. + :param neutrons_produced_per_second: int or float - neutron flux. + :param threshold: int or float - threshold for category. + :return: str - one of ('LOW', 'NORMAL', 'DANGER'). + + 1. 'LOW' -> `temperature * neutrons per second` < 90% of `threshold` + 2. 'NORMAL' -> `temperature * neutrons per second` +/- 10% of `threshold` + 3. 'DANGER' -> `temperature * neutrons per second` is not in the above-stated ranges + """ + + current_status = (temperature * neutrons_produced_per_second) + + if status < 90: + return 'LOW' + if threshold * 1.1 > status > threshold * 0.9: + return 'NORMAL' + else: + return 'DANGER' + From fcb1a253cc390e351907cd670a920c6e768fc1e1 Mon Sep 17 00:00:00 2001 From: Felder Sabrina Date: Mon, 2 Mar 2026 15:35:18 +0100 Subject: [PATCH 06/37] progress with list methods --- .../list-methods.py | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 ch09-lists-tuples-and-dictionaries/list-methods.py diff --git a/ch09-lists-tuples-and-dictionaries/list-methods.py b/ch09-lists-tuples-and-dictionaries/list-methods.py new file mode 100644 index 0000000..48053f8 --- /dev/null +++ b/ch09-lists-tuples-and-dictionaries/list-methods.py @@ -0,0 +1,30 @@ +# list.insert() takes an index i and a value x and inserts the value x at +# index i in the list: + +colors = ["red", "yellow", "green", "blue"] +print("Original list") +print(colors) + +# insert "orange" into the second position +colors.insert(1, "orange") +print('After colors.insert(1, "orange")') +print(colors) + +# if value for index parameter of .insert() is larger than the greates index +# in the list, then the value is inserted at the end of the list: +colors.insert(10, "violet") +print("after colors.insert(10, 'violet')") +print(colors) + + +# You can also use negative indices with .insert() +# i = -1 inserts the value x to the currently last index. +# which results in moving the existing last value to the right. staying last +colors.insert(-1, "indigo") +print('After colors.insert(-1, "indigo")') +print(colors) + + +# Remove value from specified index list.pop() takes one parameter, an index i +# the value that is removed is returned by the method +color = colors.pop(3) From 8bea49016932723a34bc113fc893c18a8a7d0cf9 Mon Sep 17 00:00:00 2001 From: Felder Sabrina Date: Tue, 3 Mar 2026 11:00:21 +0100 Subject: [PATCH 07/37] extend try expect with else and finally --- ch08-conditional-logic/6-recover-from-errors.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ch08-conditional-logic/6-recover-from-errors.py b/ch08-conditional-logic/6-recover-from-errors.py index 250144b..e2f3b05 100644 --- a/ch08-conditional-logic/6-recover-from-errors.py +++ b/ch08-conditional-logic/6-recover-from-errors.py @@ -12,6 +12,10 @@ break except ValueError: print("try again") + # else: + # print("All went well.") + finally: + print("Done.") # Exercise 2 From 11155772278f77d4e4c553d7e16bd35f0883c9d3 Mon Sep 17 00:00:00 2001 From: Felder Sabrina Date: Tue, 3 Mar 2026 11:00:42 +0100 Subject: [PATCH 08/37] exercism bob and perfect_number --- exercism.org/bob.py | 16 ++++++++++++++++ exercism.org/perfect_number.py | 27 +++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 exercism.org/bob.py create mode 100644 exercism.org/perfect_number.py diff --git a/exercism.org/bob.py b/exercism.org/bob.py new file mode 100644 index 0000000..b9b9028 --- /dev/null +++ b/exercism.org/bob.py @@ -0,0 +1,16 @@ +""" Talking to a teenager simulator """ +def response(hey_bob): + """ Talking to Bob returns one of five possible answers """ + + input_minus_whitespace = str.strip(hey_bob) + + if len(input_minus_whitespace) == 0: + return "Fine. Be that way!" + if input_minus_whitespace.endswith('?') and input_minus_whitespace.isupper(): + return "Calm down, I know what I'm doing!" + if input_minus_whitespace.endswith('?'): + return "Sure." + if input_minus_whitespace.isupper(): + return "Whoa, chill out!" + return "Whatever." + diff --git a/exercism.org/perfect_number.py b/exercism.org/perfect_number.py new file mode 100644 index 0000000..2b9f1d2 --- /dev/null +++ b/exercism.org/perfect_number.py @@ -0,0 +1,27 @@ +""" Module to check numbers based on Nicomachus' classification """ + +def classify(number): + """ A perfect number equals the sum of its positive divisors. + + :param number: int a positive integer + :return: str the classification of the input integer + """ + if number <= 0: + raise ValueError("Classification is only possible for positive integers.") + + aliquot_sum = 0 + + for num in range(1, number): + if number % num == 0: + aliquot_sum += num + + if aliquot_sum > number: + return "abundant" + if aliquot_sum < number: + return "deficient" + return "perfect" + +print(classify(6)) +print(classify(12)) +print(classify(24)) +print(classify(8)) \ No newline at end of file From e10c025bb771fa2ab9cf359de3770ac61e51209d Mon Sep 17 00:00:00 2001 From: Felder Sabrina Date: Wed, 4 Mar 2026 09:49:10 +0100 Subject: [PATCH 09/37] finish ch09 --- .../ch09-review-exercises.py | 47 ++++++++++++++++++ .../list-methods.py | 49 +++++++++++++++++++ .../list-of-numbers.py | 31 ++++++++++++ 3 files changed, 127 insertions(+) create mode 100644 ch09-lists-tuples-and-dictionaries/ch09-review-exercises.py create mode 100644 ch09-lists-tuples-and-dictionaries/list-of-numbers.py diff --git a/ch09-lists-tuples-and-dictionaries/ch09-review-exercises.py b/ch09-lists-tuples-and-dictionaries/ch09-review-exercises.py new file mode 100644 index 0000000..f8c54fb --- /dev/null +++ b/ch09-lists-tuples-and-dictionaries/ch09-review-exercises.py @@ -0,0 +1,47 @@ +""" +1. Create a list named food with two elements, "rice" and "beans". +""" +food = ["rice", "beans"] +print(food) + +""" +2. Append the string "broccoli" to food using .append(). +""" +food.append("broccoli") +print(food) + +""" +3. Add the strings "bread" and "pizza" to food using .extend(). +""" +food.extend(["bread", "pizza"]) +print(food) + +""" +4. Print the first two items in the food list using print() and slice no- +tation. +""" +print(food[:2]) + +""" +5. Print the last item in food using print() and index notation. +""" +print(food[-1]) + +""" +6. Create a list called breakfast from the string "eggs, fruit, orange +juice" using the string .split() method. +""" +str_breakfast = "eggs, fruit, orange juice" +breakfast = str_breakfast.split(",") + +""" +7. Verify that breakfast has three items using len(). +""" +print(len(breakfast)) + +""" +8. Create a new list called lengths using a list comprehension that con- +tains the lengths of each string in the breakfast list. +""" +lengths = [len(value) for value in breakfast] +print(lengths) diff --git a/ch09-lists-tuples-and-dictionaries/list-methods.py b/ch09-lists-tuples-and-dictionaries/list-methods.py index 48053f8..2af3c34 100644 --- a/ch09-lists-tuples-and-dictionaries/list-methods.py +++ b/ch09-lists-tuples-and-dictionaries/list-methods.py @@ -28,3 +28,52 @@ # Remove value from specified index list.pop() takes one parameter, an index i # the value that is removed is returned by the method color = colors.pop(3) +print("colors.pop(3) returned and removed: " + (color)) + +print("colors List after using colors.pop(3)") +print(colors) + +# remove the last object from the list with pop(-1) +# .pop() without a value removes the last item in the list: +color = colors.pop(-1) +print("colors.pop(-1) returned and removed: " + (color)) + +print("colors List after using colors.pop(-1)") +print(colors) + +# list.append() +# calling .appen() increases the length of the list by one and inserts the value into the final slot. +colors.append("pink") +print(colors) + +# alternative way to add 'pink' to the end of the list +colors.pop() +print(colors) + +colors.insert(len(colors), "pink") +print(colors) + +# list.extend() method is used to add several new elements to the end of a list +colors.extend(["violet", "grey", "amber"]) +print(colors) + +# excurse to list-of-numbers.py + +# List comprehensions +print("Create a tuple of numbers:") +my_numbers = (1, 2, 3, 4, 5) +print(my_numbers) +print("use list comprehensions") +print("squares = [num**2 for num in my_numbers]") +squares = [num**2 for num in my_numbers] +print("Result:") +print(squares) + +# list comprehensions are commonly used to convert values in a list to a different type +print("\nList Comprehensions") +print("From string input to floats using list comprehension") +str_numbers = ["1.5", "2.3", "5.25"] +print(str_numbers) +print("float_numbers = [float(value) for value in str_numbers]") +float_numbers = [float(value) for value in str_numbers] +print(float_numbers) \ No newline at end of file diff --git a/ch09-lists-tuples-and-dictionaries/list-of-numbers.py b/ch09-lists-tuples-and-dictionaries/list-of-numbers.py new file mode 100644 index 0000000..e46988d --- /dev/null +++ b/ch09-lists-tuples-and-dictionaries/list-of-numbers.py @@ -0,0 +1,31 @@ +# List of numbers + +nums = [1, 2, 3, 4, 5] +print("nums = [1, 2, 3, 4, 5]") + +total = 0 +for number in nums: + total += number + +print(total) + +# a much more succinct was of doing this in Python: +print("sum([1, 2, 3, 4, 5])") +print(sum([1, 2, 3, 4, 5])) +print("sum(nums)") +print(sum(nums)) + +# two other useful built-in functions for working with numers are +# min() and max() +print("min(nums):") +print(min(nums)) +print("max(nums):") +print(max(nums)) + +# sum(), min(), and max() also work with tuples: +nums = (1, 2, 3, 4, 5) +print("sum((1, 2, 3, 4, 5))") +print(sum((1, 2, 3, 4, 5))) +print(min(nums)) +print("max(nums):") +print(max(nums)) From 14fd1c44a216b6946992df74226da6a6973c7c61 Mon Sep 17 00:00:00 2001 From: Felder Sabrina Date: Wed, 4 Mar 2026 15:58:54 +0100 Subject: [PATCH 10/37] exercism triangle --- exercism.org/triangle.py | 60 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 exercism.org/triangle.py diff --git a/exercism.org/triangle.py b/exercism.org/triangle.py new file mode 100644 index 0000000..18f4a04 --- /dev/null +++ b/exercism.org/triangle.py @@ -0,0 +1,60 @@ +""" Triangle module """ +def equilateral(sides): + """ Check if a triangle is equilateral and returns 'True' or 'False' """ + if not sides_len_okay(sides): + return False + + side1, side2, side3 = sides + + if side1 == side2 == side3: + return True + + return False + +def isosceles(sides): + """ Check if a triangle is isosceles and returns 'True' or 'False' """ + if not sides_len_okay(sides): + return False + + if not is_triangle(sides): + return False + + side1, side2, side3 = sides + + if side1 == side2 or side1 == side3 or side2 == side3: + return True + return False + +def scalene(sides): + """ Check if a triangle is scalene and returns 'True' or 'False' """ + if not sides_len_okay(sides): + return False + + if not is_triangle(sides): + return False + + side1, side2, side3 = sides + + if side1 == side2 == side3: + return False + if side1 == side2 or side1 == side3 or side2 == side3: + return False + return True + +def sides_len_okay(sides): + """ Check input sides to be longer than '0' """ + return all(side > 0 for side in sides) # complex but shorter than for with if nested + +def is_triangle(sides): + """ Check if input can be a triangle """ + side1, side2, side3 = sides + + conditions_met = 0 + + if side1 + side2 >= side3: + conditions_met += 1 + if side2 + side3 >= side1: + conditions_met += 1 + if side1 + side3 >= side2: + conditions_met += 1 + return conditions_met == 3 \ No newline at end of file From 87484e0c85aa2d428702f72edfdc76d4e06ce42a Mon Sep 17 00:00:00 2001 From: Felder Sabrina Date: Wed, 4 Mar 2026 15:59:04 +0100 Subject: [PATCH 11/37] ongoing pig-latin --- exercism.org/pig-latin.py | 75 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 exercism.org/pig-latin.py diff --git a/exercism.org/pig-latin.py b/exercism.org/pig-latin.py new file mode 100644 index 0000000..48c3c3e --- /dev/null +++ b/exercism.org/pig-latin.py @@ -0,0 +1,75 @@ +""" +Rule 1 + +If a word begins with a vowel, or starts with "xr" or "yt", add an "ay" sound to the end of the word. + +For example: + + "apple" -> "appleay" (starts with vowel) + "xray" -> "xrayay" (starts with "xr") + "yttria" -> "yttriaay" (starts with "yt") + +Rule 2 + +If a word begins with one or more consonants, first move those consonants to the end of the word and then add an "ay" sound to the end of the word. + +For example: + + "pig" -> "igp" -> "igpay" (starts with single consonant) + "chair" -> "airch" -> "airchay" (starts with multiple consonants) + "thrush" -> "ushthr" -> "ushthray" (starts with multiple consonants) + +Rule 3 + +If a word starts with zero or more consonants followed by "qu", first move those consonants (if any) and the "qu" part to the end of the word, and then add an "ay" sound to the end of the word. + +For example: + + "quick" -> "ickqu" -> "ickquay" (starts with "qu", no preceding consonants) + "square" -> "aresqu" -> "aresquay" (starts with one consonant followed by "qu") + +Rule 4 + +If a word starts with one or more consonants followed by "y", first move the consonants preceding the "y"to the end of the word, and then add an "ay" sound to the end of the word. + +Some examples: + + "my" -> "ym" -> "ymay" (starts with single consonant followed by "y") + "rhythm" -> "ythmrh" -> "ythmrhay" (starts with multiple consonants followed by "y") + +""" + +""" Pig latin module """ +def translate(text): + """ Translate input to pig latin text """ + vowels = ("a", "e", "i", "o", "u") + rule1 = ("a", "e", "i", "o", "u", "xr", "yt") + + text_as_list = text.split(" ") + translated_words = [] + + for word in text_as_list: + if str(word).startswith(rule1): + translated_words.append(word + "ay") + continue + + not_vowel = True + translated_word = word + + for char in translated_word: + if char in vowels: + not_vowel = False + break + + + + translated_words.append() + + result = "" + for translation in translated_words: + result = result + " " + translation + + return result + + +print(translate("apple xray yttria")) \ No newline at end of file From cc23fbf9bbbad68ddc9a1ef13f292209ba880517 Mon Sep 17 00:00:00 2001 From: Felder Sabrina Date: Thu, 5 Mar 2026 12:05:48 +0100 Subject: [PATCH 12/37] exercism pig-latin without warnings --- exercism.org/pig-latin.py | 56 +++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/exercism.org/pig-latin.py b/exercism.org/pig-latin.py index 48c3c3e..95d7da4 100644 --- a/exercism.org/pig-latin.py +++ b/exercism.org/pig-latin.py @@ -49,27 +49,55 @@ def translate(text): translated_words = [] for word in text_as_list: - if str(word).startswith(rule1): - translated_words.append(word + "ay") - continue - - not_vowel = True translated_word = word - + consonants = "" + for char in translated_word: - if char in vowels: - not_vowel = False + if char in vowels: break + consonants = consonants + char + + if word.startswith(rule1): + translated_words.append(word) + elif "qu" in word: + position_of_qu = word.find("qu") + if position_of_qu >= 2: + slice_of_word = word[:len(consonants)] + rest_of_word = word[len(consonants):] + if rest_of_word.startswith(vowels): + translated_word = rest_of_word + slice_of_word + translated_words.append(translated_word) + elif word.find("qu") >= 0: + translated_word = word[2 + position_of_qu:] + word[: 2 + position_of_qu] + translated_words.append(translated_word) + else: + if "y" in consonants: + position_of_y = consonants.find("y") + if position_of_y == 0: + translated_word = word[position_of_y + 1:] + word[:position_of_y + 1] + translated_words.append(translated_word) + continue + translated_word = word[position_of_y:] + word[:position_of_y] + translated_words.append(translated_word) + continue + translated_word = word[len(consonants):] + consonants + translated_words.append(translated_word) - - translated_words.append() - result = "" + for translation in translated_words: - result = result + " " + translation + result = result + " " + translation + "ay" - return result + return result.strip() -print(translate("apple xray yttria")) \ No newline at end of file +print(translate("apple")) +print(translate("queen")) +print(translate("square")) +print(translate("pig")) +print(translate("chair")) +print(translate("my")) +print(translate("rhythm")) +print(translate("liquid")) +print(translate("yellow")) \ No newline at end of file From 15ba95fddf27364983c43889752ebf7680f457a1 Mon Sep 17 00:00:00 2001 From: Felder Sabrina Date: Thu, 5 Mar 2026 14:12:45 +0100 Subject: [PATCH 13/37] concepts unlocked lists, string methods --- exercism.org/little-sisters-vocab.py | 64 ++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 exercism.org/little-sisters-vocab.py diff --git a/exercism.org/little-sisters-vocab.py b/exercism.org/little-sisters-vocab.py new file mode 100644 index 0000000..ff11839 --- /dev/null +++ b/exercism.org/little-sisters-vocab.py @@ -0,0 +1,64 @@ +"""Functions for creating, transforming, and adding prefixes to strings.""" + + +def add_prefix_un(word): + """Take the given word and add the 'un' prefix. + + :param word: str - containing the root word. + :return: str - of root word prepended with 'un'. + """ + return "un" + word + + +def make_word_groups(vocab_words): + """Transform a list containing a prefix and words into a string with the prefix followed by the words with prefix prepended. + + :param vocab_words: list - of vocabulary words with prefix in first index. + :return: str - of prefix followed by vocabulary words with + prefix applied. + + This function takes a `vocab_words` list and returns a string + with the prefix and the words with prefix applied, separated + by ' :: '. + + For example: list('en', 'close', 'joy', 'lighten'), + produces the following string: 'en :: enclose :: enjoy :: enlighten'. + """ + separator = " :: " + vocab_words[0] + + return separator.join(vocab_words) + + +def remove_suffix_ness(word): + """Remove the suffix from the word while keeping spelling in mind. + + :param word: str - of word to remove suffix from. + :return: str - of word with suffix removed & spelling adjusted. + + For example: "heaviness" becomes "heavy", but "sadness" becomes "sad". + """ + if word[:-4].endswith("i"): + return word[:-5] + "y" + return word[:-4] + + +def adjective_to_verb(sentence, index): + """Change the adjective within the sentence to a verb. + + :param sentence: str - that uses the word in sentence. + :param index: int - index of the word to remove and transform. + :return: str - word that changes the extracted adjective to a verb. + + For example, ("It got dark as the sun set.", 2) becomes "darken". + """ + + words = sentence.split() + word = words[index] + if not word.isalpha(): + word = word[:-1] + return word + "en" + +print(adjective_to_verb("It got dark. as the sun set.", 2)) + +print(remove_suffix_ness("heaviness")) +print(remove_suffix_ness("sadness")) \ No newline at end of file From 7131bf4ec92219b49b1fc234be3d0363c571923f Mon Sep 17 00:00:00 2001 From: Felder Sabrina Date: Fri, 6 Mar 2026 12:41:40 +0100 Subject: [PATCH 14/37] real-pyton progress nested lists and tuples --- ...esting-copying-sorting-tuples-and-lists.py | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 ch09-lists-tuples-and-dictionaries/3-nesting-copying-sorting-tuples-and-lists.py diff --git a/ch09-lists-tuples-and-dictionaries/3-nesting-copying-sorting-tuples-and-lists.py b/ch09-lists-tuples-and-dictionaries/3-nesting-copying-sorting-tuples-and-lists.py new file mode 100644 index 0000000..a987737 --- /dev/null +++ b/ch09-lists-tuples-and-dictionaries/3-nesting-copying-sorting-tuples-and-lists.py @@ -0,0 +1,41 @@ +# Nesting +two_by_two = [[1, 2], [3, 4]] + +# two_by_two has length 2 +print(len(two_by_two)) + +# both elements of two_by_two are lists +print(two_by_two[0]) +print(two_by_two[1]) + +print("two_by_two[1]") +print(two_by_two[1]) + +print("two_by_two[1][0]") +print(two_by_two[1][0]) + +# Copying + +animals = ["lion", "tiger", "frumious Bandersnatch"] +# shallow copy, copies the reference to the same object. +# A variable name is really just a reference to a specific location in computer memory. +large_cats = animals +large_cats.append("Tigger") + +print("animals after large_cats = animals and large_cats.append('Tigger')") +print(animals) + +# to get an independent kopy of the animals list, you can use slice notation to return a new list with +# the same values +animals = ["lion", "tiger", "frumious Bandersnatch"] +large_cats = animals[:] +large_cats.append("leopard") + +print("large_cats created with slice notation animals[:] and after appending a new animal") +print(large_cats) + +print("Animals to show it is not affected by changes to large_cats") +print(animals) + + +# Sorting \ No newline at end of file From f474146307493be07ea63a3fb40494827e457d24 Mon Sep 17 00:00:00 2001 From: Felder Sabrina Date: Fri, 6 Mar 2026 12:41:57 +0100 Subject: [PATCH 15/37] exercism exercise work in progess --- exercism.org/line-up.py | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 exercism.org/line-up.py diff --git a/exercism.org/line-up.py b/exercism.org/line-up.py new file mode 100644 index 0000000..3fce3fc --- /dev/null +++ b/exercism.org/line-up.py @@ -0,0 +1,5 @@ +""" Module to inform customers about their number in line of customers """ +def line_up(name, number): + """ Return a string telling customer rank in line """ + + pass \ No newline at end of file From 0f19b842ba9b2327c64a0bea50583a394dbd40e6 Mon Sep 17 00:00:00 2001 From: Felder Sabrina Date: Mon, 9 Mar 2026 11:29:45 +0100 Subject: [PATCH 16/37] added week-3 of open.hpi course --- ..._3_unit_1_tuples_notebook-checkpoint.ipynb | 279 +++++++ ...t_2_dictionaries_notebook-checkpoint.ipynb | 759 ++++++++++++++++++ ...unit_3_whentouse_notebook-checkpoint.ipynb | 298 +++++++ ..._unit_4_safedict_notebook-checkpoint.ipynb | 133 +++ ..._unit_5_impfunct_notebook-checkpoint.ipynb | 116 +++ ..._3_unit_6_while_exercises-checkpoint.ipynb | 51 ++ ...k_3_unit_6_while_notebook-checkpoint.ipynb | 386 +++++++++ .../week_3_additional_exercises.ipynb | 173 ++++ ...week_3_additional_exercises_solution.ipynb | 244 ++++++ .../week_3_unit_1_tuples_notebook.ipynb | 448 +++++++++++ .../week_3_unit_2_dictionaries_notebook.ipynb | 759 ++++++++++++++++++ .../week_3_unit_3_whentouse_notebook.ipynb | 298 +++++++ .../week_3_unit_4_safedict_notebook.ipynb | 133 +++ .../week_3_unit_5_impfunct_notebook.ipynb | 116 +++ .../week_3_unit_6_while_notebook.ipynb | 386 +++++++++ open-hpi/week-3/week-3-unit-1-tuples.pdf | Bin 0 -> 1142856 bytes .../week-3/week-3-unit-2-dictionaries.pdf | Bin 0 -> 1184918 bytes open-hpi/week-3/week-3-unit-3-whentouse.pdf | Bin 0 -> 748785 bytes open-hpi/week-3/week-3-unit-4-safedict.pdf | Bin 0 -> 960688 bytes open-hpi/week-3/week-3-unit-5-impfunct.pdf | Bin 0 -> 753499 bytes open-hpi/week-3/week-3-unit-6-while.pdf | Bin 0 -> 819699 bytes 21 files changed, 4579 insertions(+) create mode 100644 open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_1_tuples_notebook-checkpoint.ipynb create mode 100644 open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_2_dictionaries_notebook-checkpoint.ipynb create mode 100644 open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_3_whentouse_notebook-checkpoint.ipynb create mode 100644 open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_4_safedict_notebook-checkpoint.ipynb create mode 100644 open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_5_impfunct_notebook-checkpoint.ipynb create mode 100644 open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_6_while_exercises-checkpoint.ipynb create mode 100644 open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_6_while_notebook-checkpoint.ipynb create mode 100644 open-hpi/week-3/notebooks-week-3/week_3_additional_exercises.ipynb create mode 100644 open-hpi/week-3/notebooks-week-3/week_3_additional_exercises_solution.ipynb create mode 100644 open-hpi/week-3/notebooks-week-3/week_3_unit_1_tuples_notebook.ipynb create mode 100644 open-hpi/week-3/notebooks-week-3/week_3_unit_2_dictionaries_notebook.ipynb create mode 100644 open-hpi/week-3/notebooks-week-3/week_3_unit_3_whentouse_notebook.ipynb create mode 100644 open-hpi/week-3/notebooks-week-3/week_3_unit_4_safedict_notebook.ipynb create mode 100644 open-hpi/week-3/notebooks-week-3/week_3_unit_5_impfunct_notebook.ipynb create mode 100644 open-hpi/week-3/notebooks-week-3/week_3_unit_6_while_notebook.ipynb create mode 100644 open-hpi/week-3/week-3-unit-1-tuples.pdf create mode 100644 open-hpi/week-3/week-3-unit-2-dictionaries.pdf create mode 100644 open-hpi/week-3/week-3-unit-3-whentouse.pdf create mode 100644 open-hpi/week-3/week-3-unit-4-safedict.pdf create mode 100644 open-hpi/week-3/week-3-unit-5-impfunct.pdf create mode 100644 open-hpi/week-3/week-3-unit-6-while.pdf diff --git a/open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_1_tuples_notebook-checkpoint.ipynb b/open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_1_tuples_notebook-checkpoint.ipynb new file mode 100644 index 0000000..f416c6a --- /dev/null +++ b/open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_1_tuples_notebook-checkpoint.ipynb @@ -0,0 +1,279 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# What are [tuples](https://docs.python.org/3/library/stdtypes.html?#tuples)?\n", + "\n", + "## Definition\n", + "\n", + "A `tuple` is a complex data type, similar to lists, but with a key difference:\n", + "tuples are immutable, while lists are mutable. This means that once a tuple is\n", + "created, its elements cannot be changed.\n", + "\n", + "A tuple consists of multiple elements enclosed in parentheses: `()`. The\n", + "elements are separated by commas. Just like with lists and simple data types,\n", + "tuples can be assigned to variables. Additionally, tuples can store other tuples\n", + "or lists as elements.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "address = (52066, \"Aachen\", \"Eupener Str. 70\", \"0241-6009-12345\")\n", + "print(address)\n", + "student = (\"Peter\", \"Meier\", 123456, \"SAP Basics\", \"pm12345s@university.edu\", address)\n", + "print(student)\n", + "tup1 = (12312, \"absbsb\", [1, 2, 3, 4], (\"a\", \"b\", \"c\"), \"end\")\n", + "print(tup1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Tuples cannot be modified\n", + "\n", + "The key difference between tuples and lists is that lists can be modified, while\n", + "tuples cannot — they are immutable. This means you cannot add or remove elements\n", + "in a tuple or change the value of an existing element. While you can assign a\n", + "new tuple to a variable, there are no methods like `tuple.append()` or\n", + "`tuple.remove()` to alter tuples directly.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tup = (1, 2, \"a\")\n", + "print(tup[2])\n", + "tup[2] = \"b\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tup = (1, 2, 3)\n", + "print(tup)\n", + "tup = (\"a\", \"b\", \"c\", \"d\")\n", + "print(tup)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## What are tuples used for?\n", + "\n", + "Tuples are quite similar to lists. In this unit, we will not delve deeply into\n", + "data modeling; that will be covered in Unit 3. However, here is a simple rule of\n", + "thumb for choosing between lists and tuples:\n", + "\n", + "- Use lists for collections of similar items.\n", + "- Use tuples for items with many attributes.\n", + "\n", + "Consider the following examples where tuples are appropriate:\n", + "\n", + "- `address = (zip_code, city, street, house_number)`\n", + "- `position = (x_coordinate, y_coordinate)`\n", + "- `date = (year, month, day)`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Basic operations on tuples\n", + "### Creating a Tuple Interactively\n", + "\n", + "You can use the `input()` function to read values and assemble them into a\n", + "tuple.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "name = input(\"Please enter name: \")\n", + "first_name = input(\"Please enter first name: \")\n", + "phone = input(\"Please enter phone number: \")\n", + "age = input(\"Please enter age (integer): \")\n", + "\n", + "employee = (name, first_name, phone, age)\n", + "\n", + "print(employee)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Using an index with tuples\n", + "\n", + "Just like lists, tuples use an index that is represented with square brackets\n", + "`[]`. The index starts at 0. You can also use negative indices, similar to\n", + "lists. \n", + "\n", + "**Important:** Even though you use square brackets to access elements,\n", + "it remains a tuple, not a list.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "address = (52066, \"Aachen\", \"Eupener Str. 70\", \"0241-6009-12345\")\n", + "\n", + "student = (\n", + " \"Peter\",\n", + " \"Parker\",\n", + " 123456,\n", + " \"Python for Beginners\",\n", + " \"pp12345s@university.edu\",\n", + " address,\n", + ")\n", + "\n", + "print(address[0])\n", + "print(student[1])\n", + "print(student[5])\n", + "print(student[5][2])\n", + "print(student[-1])\n", + "print(address[-3])\n", + "print(address[2])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Once again: Tuples are immutable\n", + "\n", + "Attempting to change a single element of a tuple by using its index will result\n", + "in an error." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "address = (52066, \"Aachen\", \"Eupener Str. 70\", \"0241-6009-12345\")\n", + "address[2] = \"Goethestraße 1\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Slicing operator and functions & methods\n", + "\n", + "The slicing operator, familiar from lists, also works with tuples to create a\n", + "sub-tuple. You can use indices to access not only a single element, but an\n", + "entire range. Some functions and methods work with tuples as well, as shown in\n", + "the table below:\n", + "\n", + "| Function / Method | Return Value |\n", + "| ----------------- | ---------------------------------------------------------------------------------------- |\n", + "| `len(tuple)` | Number of elements in the tuple |\n", + "| `tuple.count(x)` | Number of times element *x* appears in the tuple |\n", + "| `tuple.index(x)` | Index of the first occurrence of *x*. If *x* does not exist, an error will occur |\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "numbers = (1, 2, \"trois\", \"four\", \"V\", 6)\n", + "print(numbers[2:4])\n", + "print(len(numbers))\n", + "print(numbers.count(1))\n", + "print(numbers.index(\"V\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Looping through tuples\n", + "\n", + "Similar to lists, a `for` loop can be used to access each element of a tuple." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "address = (52066, \"Aachen\", \"Eupener Str. 70\", \"0241-6009-12345\")\n", + "\n", + "for address_part in address:\n", + " print(address_part)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Conversion of tuples\n", + "\n", + "There are functions, like `int()`, to convert data types — such as from a string\n", + "to an integer. Similar conversions exist between lists and tuples: `list()`\n", + "converts the argument into a list, and `tuple()` converts it into a tuple.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "l = [1, 2, \"a\", 2.3]\n", + "t = tuple(l)\n", + "print(t)\n", + "l = list(t)\n", + "print(l)" + ] + } + ], + "metadata": { + "interpreter": { + "hash": "ac59ebe37160ed0dfa835113d9b8498d9f09ceb179beaac4002f036b9467c963" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.2" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_2_dictionaries_notebook-checkpoint.ipynb b/open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_2_dictionaries_notebook-checkpoint.ipynb new file mode 100644 index 0000000..5f08147 --- /dev/null +++ b/open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_2_dictionaries_notebook-checkpoint.ipynb @@ -0,0 +1,759 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# What are dictionaries?\n", + "\n", + "In both lists and tuples, elements are accessed using an index. This method has a\n", + "drawback: to access a specific element, you need to know its exact index, which \n", + "can be challenging when elements are added or removed. In such cases, the indices \n", + "of subsequent elements change, making programming cumbersome due to constant \n", + "index tracking.\n", + "\n", + "## Accessing Elements by Name\n", + "\n", + "Dictionaries offer a solution to this problem. Like lists and tuples, dictionaries \n", + "can store multiple elements. The key difference is that elements in a dictionary \n", + "are accessed by a name rather than an index, using a key-value format.\n", + "\n", + "For example, consider a telephone book. A telephone book lists numerous phone \n", + "numbers, each associated with a name:\n", + "\n", + "| Name | Telephone Number |\n", + "| :----------- | ---------------: |\n", + "| P. McCartney | 123 456 |\n", + "| J. Lennon | 987 654 321 |\n", + "| G. Harrison | 11 342 555 |\n", + "| R. Starr | 777 888 32 |\n", + "\n", + "In a telephone book, you don't seek the third element; instead, you look for \n", + "the number corresponding to a specific name, like *G. Harrison*. Essentially, you \n", + "want the number using the name directly. Accessing an element by name is what dictionaries are good for. Instead of \"names,\" we call them \"keys\" in dictionary terminology.\n", + "\n", + "## Using Dictionaries\n", + "\n", + "A dictionary is a collection of key-value pairs. Dictionaries are enclosed in \n", + "curly braces `{}`. Inside the braces, key-value pairs are separated by commas, \n", + "and each key-value pair is of the form: `key: value`. A dictionary looks like \n", + "this: `{key1: value1, key2: value2, ..., keyN: valueN}`. Here's an example:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'P. McCartney': 123456, 'J. Lennon': 987654321, 'G. Harrison': 11342555, 'R. Starr': 77788832}\n" + ] + } + ], + "source": [ + "phone_book = {\n", + " \"P. McCartney\": 123456,\n", + " \"J. Lennon\": 987654321,\n", + " \"G. Harrison\": 11342555,\n", + " \"R. Starr\": 77788832,\n", + "}\n", + "print(phone_book)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can access individual elements of `phone_book` using the key (person's name). \n", + "To do this, place the key in square brackets `[ ]` following the dictionary's \n", + "name." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "11342555\n" + ] + } + ], + "source": [ + "print(phone_book[\"G. Harrison\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Access by index, however, is not possible, as shown below:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "ename": "KeyError", + "evalue": "2", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mKeyError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[43mphone_book\u001b[49m\u001b[43m[\u001b[49m\u001b[32;43m2\u001b[39;49m\u001b[43m]\u001b[49m)\n", + "\u001b[31mKeyError\u001b[39m: 2" + ] + } + ], + "source": [ + "print(phone_book[2])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Attempting to use an index with a dictionary will lead to an error, as dictionaries\n", + "do not have this index-based access. Instead, they rely on keys, and accessing a \n", + "non-existing key results in a key error.\n", + "\n", + "Here's another example demonstrating this point:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "ename": "KeyError", + "evalue": "'P. Best'", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mKeyError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[4]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[43mphone_book\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mP. Best\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m)\n", + "\u001b[31mKeyError\u001b[39m: 'P. Best'" + ] + } + ], + "source": [ + "print(phone_book[\"P. Best\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Adding key-value pairs to a dictionary\n", + "\n", + "You can easily add new key-value pairs to a dictionary using the following syntax: \n", + "`dictionary[key] = value`. In the example below, additional phone numbers are \n", + "added to our telephone book:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'P. McCartney': 123456, 'J. Lennon': 987654321, 'G. Harrison': 11342555, 'R. Starr': 77788832, 'Y. Ono': 5463333, 'B. Epstein': 9998777, 'B. Geröllheimer': 9998777}\n" + ] + } + ], + "source": [ + "phone_book[\"Y. Ono\"] = 5463333\n", + "phone_book[\"B. Geröllheimer\"] = 9998777\n", + "print(phone_book)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Replacing the value for an existing key\n", + "\n", + "If you assign a new value to an already existing key in a dictionary, the value \n", + "is overwritten. This feature is useful for updating or changing entries but be \n", + "careful not to overwrite the wrong entries in your dictionary!" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'P. McCartney': 654321, 'J. Lennon': 987654321, 'G. Harrison': 11342555, 'R. Starr': 77788832, 'Y. Ono': 5463333, 'B. Epstein': 9998777, 'B. Geröllheimer': 9998777}\n" + ] + } + ], + "source": [ + "phone_book[\"P. McCartney\"] = 654321\n", + "print(phone_book)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### More about keys and values\n", + "\n", + "In our telephone book example, all keys were strings and all values were integers. \n", + "However, dictionaries are not limited to these data types. *Values* can be of any \n", + "data type, including lists, tuples, and even other dictionaries. *Keys* can also \n", + "be nearly any data type (more on the requirements for keys below). \n", + "Within a dictionary, data \n", + "types can be mixed, as demonstrated in the example below:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{123: 'blabla', True: 123.456, 'key': False, (123, 'abc'): 1000, 34.5: [0, 1, 1, 2, 3, 5]}\n" + ] + } + ], + "source": [ + "stupid_dict = {\n", + " 123: \"blabla\",\n", + " True: 123.456,\n", + " \"key\": False,\n", + " (123, \"abc\"): 1000,\n", + " 34.5: [0, 1, 1, 2, 3, 5],\n", + "}\n", + "print(stupid_dict)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The key in a dictionary is immutable\n", + "\n", + "In contrast to values, keys in a dictionary must be immutable.[1](#fn1) Acceptable key \n", + "types include `integers`, `strings`, and `tuples`, but not lists, since lists \n", + "can be modified. Consider these two similar examples:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{('Paul', 'McCartney'): 123456}\n" + ] + } + ], + "source": [ + "new_phone_book = {(\"Paul\", \"McCartney\"): 123456}\n", + "print(new_phone_book)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "unhashable type: 'list'", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mTypeError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[10]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m new_phone_book = {[\u001b[33m\"\u001b[39m\u001b[33mPaul\u001b[39m\u001b[33m\"\u001b[39m, \u001b[33m\"\u001b[39m\u001b[33mMcCartney\u001b[39m\u001b[33m\"\u001b[39m]: \u001b[32m123456\u001b[39m}\n\u001b[32m 2\u001b[39m \u001b[38;5;28mprint\u001b[39m(new_phone_book)\n", + "\u001b[31mTypeError\u001b[39m: unhashable type: 'list'" + ] + } + ], + "source": [ + "new_phone_book = {[\"Paul\", \"McCartney\"]: 123456}\n", + "print(new_phone_book)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Deleting an element\n", + "You can delete elements from a dictionary using the `del` keyword. Review the \n", + "example below. What happens if you run this cell twice? Why does the error occur?" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'J. Lennon': 987654321, 'G. Harrison': 11342555, 'R. Starr': 77788832, 'Y. Ono': 5463333, 'B. Epstein': 9998777, 'B. Geröllheimer': 9998777}\n" + ] + } + ], + "source": [ + "del phone_book[\"P. McCartney\"]\n", + "print(phone_book)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Iterating over a dictionary with a `for` loop\n", + "\n", + "Just like lists and tuples, dictionaries can be iterated over using a `for` loop. \n", + "The syntax is as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "J. Lennon 987654321\n", + "G. Harrison 11342555\n", + "R. Starr 77788832\n", + "Y. Ono 5463333\n", + "B. Epstein 9998777\n", + "B. Geröllheimer 9998777\n" + ] + } + ], + "source": [ + "for name in phone_book:\n", + " print(name, phone_book[name])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Usage of functions and methods with dictionaries\n", + "\n", + "Several functions and methods can be used to manage dictionaries.\n", + "\n", + "### General functions and methods\n", + "\n", + "| Function/Method | Return Value |\n", + "| --------------- | ------------------------------------------- |\n", + "| `len()` | Number of key-value pairs in the dictionary |\n", + "| `.keys()` | All keys of a dictionary |\n", + "| `.values()` | All values of a dictionary |\n", + "| `.items()` | All `key:value` pairs as tuples |\n", + "\n", + "> **NOTE:** The `.keys()` method returns a `dict_keys` data type. You can convert \n", + "this to a list with the `list()` function.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "6\n" + ] + } + ], + "source": [ + "print(len(phone_book))" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dict_keys(['J. Lennon', 'G. Harrison', 'R. Starr', 'Y. Ono', 'B. Epstein', 'B. Geröllheimer'])\n", + "\n", + "['J. Lennon', 'G. Harrison', 'R. Starr', 'Y. Ono', 'B. Epstein', 'B. Geröllheimer']\n" + ] + } + ], + "source": [ + "print(phone_book.keys())\n", + "print(type(phone_book.keys()))\n", + "print(list(phone_book.keys()))" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[987654321, 11342555, 77788832, 5463333, 9998777, 9998777]\n" + ] + } + ], + "source": [ + "print(list(phone_book.values()))" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dict_items([('J. Lennon', 987654321), ('G. Harrison', 11342555), ('R. Starr', 77788832), ('Y. Ono', 5463333), ('B. Epstein', 9998777), ('B. Geröllheimer', 9998777)])\n", + "{'J. Lennon': 987654321, 'G. Harrison': 11342555, 'R. Starr': 77788832, 'Y. Ono': 5463333, 'B. Epstein': 9998777, 'B. Geröllheimer': 9998777}\n", + "{'J. Lennon': 987654321, 'G. Harrison': 11342555, 'R. Starr': 77788832, 'Y. Ono': 5463333, 'B. Epstein': 9998777}\n" + ] + } + ], + "source": [ + "print(phone_book.items())\n", + "print(phone_book)\n", + "del phone_book[\"B. Geröllheimer\"]\n", + "print(phone_book)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Typical applications of dictionaries\n", + "\n", + "Below are two scenarios where dictionaries are particularly useful.\n", + "\n", + "### Example 1: Student data\n", + "\n", + "In the telephone book example, the key-value pairs were *name - telephone number*. \n", + "In this example, a dictionary represents a student's data, which includes name, \n", + "first-name, ID, e-mail, city, etc.\n", + "\n", + "Here, the keys and values vary significantly. Each key describes a different \n", + "attribute of the same student. The advantage is you don't need to remember the \n", + "indices of various attributes. Unlike tuples, attributes can be added or \n", + "removed as needed.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'name': 'McCartney', 'firstname': 'Paul', 'subject': 'Music', 'email': 'paul@beatles.co.uk', 'city': 'Liverpool'}\n", + "McCartney\n", + "Liverpool\n" + ] + } + ], + "source": [ + "student = {\n", + " \"name\": \"McCartney\",\n", + " \"firstname\": \"Paul\",\n", + " \"subject\": \"Music\",\n", + " \"email\": \"paul@beatles.co.uk\",\n", + " \"city\": \"Liverpool\",\n", + "}\n", + "print(student)\n", + "print(student[\"name\"])\n", + "print(student[\"city\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example 2: Students and student IDs\n", + "\n", + "When data has a unique identifier (ID), it can serve as the dictionary key. For \n", + "example, each student can have a student ID as a key:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{12345: 'Paul McCartney', 23456: 'John Lennon', 34567: 'George Harrison', 45678: 'Ringo Starr'}\n", + "George Harrison\n" + ] + } + ], + "source": [ + "students = {\n", + " 12345: \"Paul McCartney\",\n", + " 23456: \"John Lennon\",\n", + " 34567: \"George Harrison\",\n", + " 45678: \"Ringo Starr\",\n", + "}\n", + "print(students)\n", + "print(students[34567])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exercise 1: Create a dictionary\n", + "\n", + "Create a dictionary with the English colors *red, green, blue, yellow* as keys \n", + "and their German translations *rot, grün, blau, gelb* as values. " + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'red': 'rot', 'green': 'grün', 'blue': 'blau', 'yellow': 'gelb'}\n" + ] + } + ], + "source": [ + "# Your code here\n", + "color_dict = {\"red\": \"rot\", \"green\": \"grün\", \"blue\": \"blau\", \"yellow\": \"gelb\"}\n", + "print(color_dict)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exercise 2: Translate\n", + "\n", + "Now, use the translation dictionary from the last exercise. Using `input()`, \n", + "ask for a color and provide its translation as output.\n", + "\n", + "Example input:\n", + "\n", + "```\n", + "Which color should be translated? red\n", + "```\n", + "\n", + "\n", + "Example output:\n", + "\n", + "```\n", + "The German word for red is rot.\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "Which color should be translated? red\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The German word for red is rot\n", + "The German word for red is rot\n" + ] + } + ], + "source": [ + "# Your code here\n", + "#print(color_dict.keys())\n", + "color_to_translate = input(\"Which color should be translated? \")\n", + "\n", + "print(\"The German word for \" + color_to_translate + \" is \" + color_dict[color_to_translate])\n", + "print(\"The German word for\", color_to_translate, \"is\", color_dict[color_to_translate])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Footnote\n", + "[1](#fn1-back) Actually, the key must be hashable in a dictionary. While \n", + "all immutable Python 🐍 data types are hashable, not all hashable types are immutable. \n", + "Further discussion is beyond the scope of this course." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Unit 2: Exercise\n", + "## Anweisungen:\n", + "\n", + "Now it's your turn. Click the button below to open CodeOcean and implement a solution for the given exercise. If you want, you can also solve it in a Jupyter Notebook first and copy the solution to CodeOcean afterwards.\n", + "\n", + "One of the nice features of Python is that it supports Unicode. Therefore it is possible to use emojis just like other characters in strings. In this exercise you will use this feature to build an emoji translator.\n", + "\n", + "Below is a dictionary that maps English terms to Emojis (broken into multiple lines for better readability).\n", + "\n", + "{\n", + "\"happy\": \"😃\", \n", + "\"heart\": \"😍\", \n", + "\"rotfl\": \"🤣\", \n", + "\"smile\": \"😊\", \n", + "\"crying\": \"😭\", \n", + "\"kiss\": \"😘\", \n", + "\"clap\": \"👏\", \n", + "\"grin\": \"😁\", \n", + "\"fire\": \"🔥\", \n", + "\"broken\": \"💔\", \n", + "\"think\": \"🤔\", \n", + "\"excited\": \"🤩\", \n", + "\"boring\": \"🙄\", \n", + "\"winking\": \"😉\", \n", + "\"ok\": \"👌\", \n", + "\"hug\": \"🤗\", \n", + "\"cool\": \"😎\", \n", + "\"angry\": \"😠\", \n", + "\"python\": \"🐍\" \n", + "} \n", + "\n", + "Use this dictionary to build a program that:\n", + "\n", + " Reads a sentence from the user.\n", + " Replaces all the words in the sentence with the corresponding Emoji.\n", + "\n", + "Below is an example execution of the program:\n", + "\n", + "Please enter a sentence: I'm so excited to learn python \n", + "I'm so 🤩 to learn 🐍\n" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": {}, + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "Please enter a sentence: If you are happy\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " If you are 😃\n" + ] + } + ], + "source": [ + "emoji_dict = { \"happy\": \"😃\", \"heart\": \"😍\", \"rotfl\": \"🤣\", \"smile\": \"😊\", \"crying\": \"😭\", \"kiss\": \"😘\", \"clap\": \"👏\", \"grin\": \"😁\", \"fire\": \"🔥\", \"broken\": \"💔\",\n", + "\"think\": \"🤔\", \"excited\": \"🤩\", \"boring\": \"🙄\", \"winking\": \"😉\", \"ok\": \"👌\", \"hug\": \"🤗\", \"cool\": \"😎\", \"angry\": \"😠\", \"python\": \"🐍\"} \n", + "\n", + "user_input = input(\"Please enter a sentence: \")\n", + "\n", + "word_list = user_input.split()\n", + "dict_keys = emoji_dict.keys()\n", + "\n", + "result = \"\"\n", + "\n", + "for word in word_list:\n", + " if word in dict_keys:\n", + " emoji = emoji_dict[word]\n", + " word = emoji\n", + " result += \" \" + word \n", + "\n", + "result.strip()\n", + "#result = [result = result + item for item in word_list]\n", + "print(result)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "file_extension": ".py", + "interpreter": { + "hash": "ac59ebe37160ed0dfa835113d9b8498d9f09ceb179beaac4002f036b9467c963" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.12" + }, + "mimetype": "text/x-python", + "name": "python", + "npconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": 3 + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_3_whentouse_notebook-checkpoint.ipynb b/open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_3_whentouse_notebook-checkpoint.ipynb new file mode 100644 index 0000000..023c226 --- /dev/null +++ b/open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_3_whentouse_notebook-checkpoint.ipynb @@ -0,0 +1,298 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# When to use lists, tuples and dictionaries\n", + "\n", + "So far, we have introduced three complex data types: `list`, `tuple`, and `dictionary`. \n", + "In principle, these data types are interchangeable.[1](#fn1)\n", + "\n", + "For instance, a list can be simulated with a dictionary by using the index of a \n", + "list item as the dictionary key. However, this approach can complicate certain \n", + "operations. For example, if you delete an item from a list, all subsequent indices \n", + "decrease by one. Simulating this behavior with a dictionary requires complex \n", + "operations. Similarly, a tuple can also be mimicked with a list, but using a list \n", + "sacrifices the immutability that is intrinsic to a tuple. So, why not opt for \n", + "lists initially if they offer more possibilities? What advantages do different \n", + "complex data types provide, and when should each type be used?\n", + "\n", + "The following suggestions are based on personal experiences and programming styles. \n", + "These recommendations should not be taken as the definitive truth but merely as \n", + "guidance. These recommendations may not be shared universally by other programmers,\n", + "and that's perfectly fine.[2](#fn2)\n", + "\n", + "## Lists should be used for many objects\n", + "\n", + "A list can contain items of different data types, such as `[23, \"abc\", True, (\"a\", \"b\", \"c\")]`. \n", + "However, handling lists with varying data types can be challenging. If all elements \n", + "share the same data type, managing the list becomes easier. Knowing the data type \n", + "of list elements allows for consistent use of functions and methods. For example, \n", + "consider the phone number list from a previous notebook. Iterating through this \n", + "list ensures you're processing phone numbers, typically integers. Limiting lists \n", + "to a single data type can simplify programming.\n", + "\n", + "In the example below, the list `numbers` contains only integers. The `for` loop \n", + "takes advantage of this knowledge and divides each value by 2 using integer division \n", + "`//`. If the list contained other types, like strings or lists, this operation \n", + "would result in an error.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "numbers = [2, 4, 8, 16, 32, 64]\n", + "for i in numbers:\n", + " print(i // 2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Tuples should be used for complex objects\n", + "\n", + "As mentioned in the first notebook of this week, tuples are best suited for complex \n", + "objects requiring multiple attributes. Take students as an example: each student \n", + "might be described by a name, first name, email, student ID, course of study, \n", + "address, etc. These attributes are consistent across all students, making tuples \n", + "ideal for representing each student.\n", + "\n", + "If your program manages multiple students, you can store each student tuple in a \n", + "list. This combination of lists and tuples—a list of tuples—is effective for \n", + "handling multiple complex objects of the same type.\n", + "\n", + "The list-of-tuples structure works well when accessing data from CSV files (CSV \n", + "stands for comma-separated values), a common data format." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "student_1 = (\"Dylan\", \"Bob\", 334455, \"Philosophy\")\n", + "student_2 = (\"Cobain\", \"Kurt\", 987654, \"Mechanical Engineering\")\n", + "student_3 = (\"Winehouse\", \"Amy\", 123321, \"Medicine\")\n", + "list_of_students = [student_1, student_2, student_3]\n", + "\n", + "for s in list_of_students:\n", + " print(s[3])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## If there is an ID, use a dictionary instead of a list\n", + "\n", + "When objects have a unique ID, a dictionary may be more suitable. This allows you \n", + "to access objects directly via their IDs rather than searching through them.\n", + "\n", + "Consider the example of students: each student has a unique student ID. Operations \n", + "with students often rely on their IDs rather than names to avoid confusion—especially \n", + "among students with identical names. Unlike names, student IDs are unique. \n", + "Therefore, for student data, a dictionary of tuples can be advantageous.\n", + "\n", + "A dictionary of tuples is also a good option for data retrieved from relational \n", + "databases. These databases typically consist of records with consistent structures \n", + "and unique IDs. The ID can serve as the dictionary key, and the rest of the record, \n", + "in tuple form, becomes the dictionary value.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "student_1 = (\"Dylan\", \"Bob\", \"Philosophy\")\n", + "student_2 = (\"Cobain\", \"Kurt\", \"Mechanical Engineering\")\n", + "student_3 = (\"Winehouse\", \"Amy\", \"Medicine\")\n", + "\n", + "dict_of_students = {}\n", + "dict_of_students[334455] = student_1\n", + "dict_of_students[987654] = student_2\n", + "dict_of_students[123321] = student_3\n", + "\n", + "for student_ID in dict_of_students:\n", + " print(student_ID, dict_of_students[student_ID][2])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## If complex objects differ in their attributes, use a dictionary instead of the tuple\n", + "\n", + "Unlike tuples, dictionaries provide greater flexibility in describing objects. \n", + "Consider again the example of students: students from different departments may \n", + "take different classes and receive marks in distinct modules. In such scenarios, \n", + "dictionaries can replace tuples. If no ID is present, a list of dictionaries is \n", + "viable; if an ID is available, a dictionary of dictionaries is feasible.\n", + "\n", + "Even more complex combinations are possible. For instance, students typically share \n", + "attributes like name, first name, email, etc., making tuples appropriate. However, \n", + "students enroll in different courses, and their modules and achievements vary. \n", + "Here, a dictionary for these attributes is preferable. A combination of tuples \n", + "and dictionaries is possible: student data is stored in a tuple, while one tuple \n", + "element, containing a student's marks, is a dictionary. You end up with a list \n", + "of tuples, where one element of the tuple is a dictionary. Complex, but effective.\n", + "\n", + "There are many options, but choosing a well-suited data structure often makes \n", + "your life as a programmer easier. So, carefully consider your data structure choices." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s1 = (\"Dylan\", \"Bob\", 334455, \"Philosophy\", {\"Logic\": \"A\", \"Ethics\": \"B\"})\n", + "s2 = (\"Cobain\", \"Kurt\", 987654, \"Mechanical Engineering\", {\"Math\": \"B\"})\n", + "s3 = (\"Winehouse\", \"Amy\", 123321, \"Medicine\", {\"Math\": \"B\", \"Chemistry\": \"C\"})\n", + "\n", + "l_of_students = [s1, s2, s3]\n", + "for s in l_of_students:\n", + " print(s)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exercise 1: List of tuples\n", + "\n", + "In the cell below, a list of students is provided. Each student is defined within \n", + "a tuple. Implement `input()` statements to gather information for a new student. \n", + "Combine the data into a tuple and append the tuple to the `list_of_students`. \n", + "Finally, print the entire list using a `for` loop that iterates over the list.\n", + "\n", + "Example Input:\n", + "```\n", + " Name: Weasley\n", + " Firstname: Ginney\n", + " ...\n", + "```\n", + "\n", + "Example Output:\n", + "```\n", + " (\"Potter\", \"Harry\", 477264, \"harry@hogwarts.wiz\", \"Defence Against the Dark Arts\")\n", + " (\"Weasley\", \"Ron\", 490134, \"ron@hogwarts.wiz\", \"Care of Magical Creatures\")\n", + " ...\n", + "``` " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list_of_students = [\n", + " (\"Potter\", \"Harry\", 477264, \"harry@hogwarts.wiz\", \"Defence Against the Dark Arts\"),\n", + " (\"Weasley\", \"Ron\", 490134, \"ron@hogwarts.wiz\", \"Care of Magical Creatures\"),\n", + " (\"Granger\", \"Hermione\", 471617, \"hermione@hogwarts.wiz\", \"Alchemy\"),\n", + " (\"Creevey\", \"Colin\", 432646, \"colin@hogwarts.wiz\", \"Music\"),\n", + " (\"Finnigan\", \"Seamus\", 481989, \"seamus@hogwarts.wiz\", \"Ancient Studies\"),\n", + " (\"Abbott\", \"Hannah\", 488962, \"hannah@hogwarts.wiz\", \"Apparition\"),\n", + " (\"Parkinson\", \"Pansy\", 482103, \"pansy@hogwarts.wiz\", \"Dark Arts\"),\n", + " (\"Malfoy\", \"Draco\", 492010, \"draco@hogwarts.wiz\", \"Defence Against the Dark Arts\"),\n", + " (\"Thomas\", \"Dean\", 447924, \"dean.thomas@hogwarts.wiz\", \"Divination\"),\n", + "]\n", + "\n", + "#Your code here" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exercise 2: Transform the above list into a dictionary\n", + "\n", + "Convert the `list_of_students` from the previous exercise into a dictionary of \n", + "tuples. Each student remains stored as a `tuple`, but use the third element — the \n", + "ID — as the dictionary key. To do this, create a new tuple for each student that \n", + "includes all original elements except the ID. This new tuple becomes the value, \n", + "while the ID becomes the key in the resulting dictionary." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "#Your code here" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exercise 3: Transform the dictionary of tuples into a dictionary of dictionaries\n", + "\n", + "Using the result of the previous exercise, iterate over the dictionary with a \n", + "`for` loop. The dictionary value is always a `tuple`. Transform this `tuple` into \n", + "a dictionary. Choose suitable names for each entry." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#Your code here" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Footnote\n", + "[1](#fn1-back) Of course using a list instead of a dictionary or vice versa might have severe\n", + "implications on the performance and memory usage of your program. However, a detailed discussion of the advantages and\n", + "disadvantages of the list, tuples and dictionaries is beyond the scope of this course. \n", + "\n", + "[2](#fn2-back) Stephan and Christian had quite a few discussions about these recommendations — and they're still not in complete agreement 😉. However, after learning about [Haskell](https://en.wikipedia.org/wiki/Haskell), Christian now mostly supports Stephan's recommendations." + ] + } + ], + "metadata": { + "file_extension": ".py", + "interpreter": { + "hash": "ac59ebe37160ed0dfa835113d9b8498d9f09ceb179beaac4002f036b9467c963" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.2" + }, + "mimetype": "text/x-python", + "name": "python", + "npconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": 3 + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_4_safedict_notebook-checkpoint.ipynb b/open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_4_safedict_notebook-checkpoint.ipynb new file mode 100644 index 0000000..7073135 --- /dev/null +++ b/open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_4_safedict_notebook-checkpoint.ipynb @@ -0,0 +1,133 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Safe dictionary access\n", + "\n", + "When you try to access a key that does not exist in a dictionary, you will\n", + "encounter a `KeyError`. This is demonstrated in the following example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dict = {1234: \"Peter\", 2345: \"Jane\"}\n", + "print(dict[1234])\n", + "print(dict[3456])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Safe access to a dictionary using `in`\n", + "\n", + "To prevent your program from being interrupted by errors, it is a good idea to\n", + "catch these potential errors. Using the keyword `in`, you can check if a key\n", + "exists in the dictionary, and thus avoid the error entirely. The expression\n", + "`key in dict` will return `True` if the key exists in the dictionary, and\n", + "`False` otherwise. You can use this within an `if` statement to handle the\n", + "situation gracefully." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dict_of_students = {\n", + " 477264: (\"Potter\", \"Harry\", \"harry@hogwarts.wiz\", \"Defence Against the Dark Arts\"),\n", + " 490134: (\"Weasley\", \"Ron\", \"ron@hogwarts.wiz\", \"Care of Magical Creatures\"),\n", + " 471617: (\"Granger\", \"Hermione\", \"hermione@hogwarts.wiz\", \"Alchemy\"),\n", + " 432646: (\"Creevey\", \"Colin\", \"colin@hogwarts.wiz\", \"Music\"),\n", + " 481989: (\"Finnigan\", \"Seamus\", \"seamus@hogwarts.wiz\", \"Ancient Studies\"),\n", + " 488962: (\"Abbott\", \"Hannah\", \"hannah@hogwarts.wiz\", \"Apparition\"),\n", + " 482103: (\"Parkinson\", \"Pansy\", \"pansy@hogwarts.wiz\", \"Dark Arts\"),\n", + " 492010: (\"Malfoy\", \"Draco\", \"draco@hogwarts.wiz\", \"Defence Against the Dark Arts\"),\n", + " 447924: (\"Thomas\", \"Dean\", \"dean.thomas@hogwarts.wiz\", \"Divination\"),\n", + "}\n", + "\n", + "matrnr = int(input(\"Please enter matriculation number: \"))\n", + "if matrnr in dict_of_students:\n", + " print(\"The student you are looking for is:\", dict_of_students[matrnr])\n", + "else:\n", + " print(\"A student with this matriculation number does not exist\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Alternative: Using [dict.get()](https://docs.python.org/3/library/stdtypes.html#dict.get) to access values safely\n", + "\n", + "Another way to avoid errors is by using the `get()` method to retrieve the\n", + "value associated with a given key. This method can take two arguments:\n", + "\n", + "`dict.get(key, default)`\n", + "\n", + "The first argument is the `key` you want to retrieve the value for (such as the\n", + "matriculation number in the above example). The second, **optional** argument,\n", + "`default`, specifies the return value if the `key` is not found in the\n", + "dictionary. If you do not provide the `default` argument, the method will \n", + "return `None` by default, and no error will be raised, so your program will \n", + "continue to run smoothly. Below is an example of how to implement this \n", + "technique:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "input_num = int(input(\"Please enter matriculation number:\"))\n", + "student = dict_of_students.get(\n", + " input_num, \"no matching student found for this matriculation number\"\n", + ")\n", + "\n", + "print(\n", + " \"For the matriculation number\",\n", + " input_num,\n", + " \"the dictionary returned the following result:\",\n", + " student,\n", + ")" + ] + } + ], + "metadata": { + "file_extension": ".py", + "interpreter": { + "hash": "ac59ebe37160ed0dfa835113d9b8498d9f09ceb179beaac4002f036b9467c963" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.2" + }, + "mimetype": "text/x-python", + "name": "python", + "npconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": 3 + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_5_impfunct_notebook-checkpoint.ipynb b/open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_5_impfunct_notebook-checkpoint.ipynb new file mode 100644 index 0000000..4a56feb --- /dev/null +++ b/open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_5_impfunct_notebook-checkpoint.ipynb @@ -0,0 +1,116 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Important functions and methods for complex data types\n", + "\n", + "This unit serves as a recap to summarize the key functions and methods for \n", + "[Lists](https://docs.python.org/3/library/stdtypes.html#lists), \n", + "[Tuples](https://docs.python.org/3/library/stdtypes.html#tuples), and \n", + "[Dictionaries](https://docs.python.org/3/library/stdtypes.html#mapping-types-dict).\n", + "\n", + "The tables below provide an overview of various methods and functions, along with \n", + "an indication of whether they are applicable to the data types mentioned above:\n", + "\n", + "| **Method** | **Description** | **List** | **Tuple** | **Dictionary** |\n", + "| :----------------- | :---------------------------------------- | :------: | :-------: | :------------: |\n", + "| `.sort()` | Sorts the object | X | | |\n", + "| `.append(x)` | Appends *x* to the end of the object | X | | |\n", + "| `.pop(i)` | Removes item at index *i* from the object | X | | X |\n", + "| `.insert(i, x)` | Inserts *x* at index *i* | X | | |\n", + "| `.remove(x)` | Removes the first occurrence of *x* | X | | |\n", + "| `.count(x)` | Counts the number of occurrences of *x* | X | X | |\n", + "| `.index(x)` | Returns the index of *x* | X | X | |\n", + "| `.keys()` | Returns all `keys` of the dictionary | | | X |\n", + "| `.values()` | Returns all `values` of the dictionary | | | X |\n", + "| `.items()` | Returns all `items` of the dictionary | | | X |\n", + "\n", + "\n", + "\n", + "| **Function** | **Description** | **List** | **Tuple** | **Dictionary** |\n", + "| :----------------- | :---------------------------------------- | :------: | :-------: | :------------: |\n", + "| `del object[item]` | Deletes *item* from *object* | X | | X |\n", + "| `len(object)` | Returns the length of *object* | X | X | X |\n", + "| `min(object)` | Returns the smallest item in *object* | X | X | [1](#fn1) |\n", + "| `max(object)` | Returns the largest item in *object* | X | X | [1](#fn1) |\n", + "| `sorted(object)` | Returns a sorted list of the items in *object* | X | X | [1](#fn1) |\n", + "\n", + "It should be evident why the first five **methods** apply only to lists — they modify \n", + "the object, and since tuples are immutable, these methods are unavailable for them. \n", + "Regarding dictionaries, sorting is unnecessary because values are accessed using a \n", + "`key`, not an index. Inserting values into a dictionary uses different syntax, and \n", + "removing the first occurrence of a value doesn't make sense because dictionaries \n", + "can have any order, and items are accessed using keys. \n", + "The last set of **methods** is specific to dictionaries, as lists and tuples do not \n", + "have keys and values. Therefore, these methods work only for dictionaries.\n", + "\n", + "The `del` keyword operates only on mutable data types and not on tuples. \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example\n", + "\n", + "The methods mentioned above can be used to identify which letter occurs most often \n", + "in a text. Note that this example is solely to demonstrate the use of these \n", + "methods. A dictionary would provide a more efficient solution." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "text = \"lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\"\n", + "letters = \"abcdefghijklmnopqrstuvwxyz\"\n", + "\n", + "result = []\n", + "for l in letters:\n", + " result.append(text.count(l))\n", + "\n", + "max_count = max(result)\n", + "max_index = result.index(max_count)\n", + "\n", + "print(letters[max_index])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Footnote\n", + "[1](#fn1-back) In general, the last three functions are also applicable to dictionaries. However, \n", + "it is more common to use these functions with the values or keys of a dictionary." + ] + } + ], + "metadata": { + "interpreter": { + "hash": "fb768b9b3ecf961ba38b0c7e836f7f85a23c08c1797458e7c652470f2ae90c9c" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.2" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_6_while_exercises-checkpoint.ipynb b/open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_6_while_exercises-checkpoint.ipynb new file mode 100644 index 0000000..fb8381b --- /dev/null +++ b/open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_6_while_exercises-checkpoint.ipynb @@ -0,0 +1,51 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exercise: Enter courses\n", + "\n", + "A course includes several attributes: course number, course name, lecturer, and \n", + "semester. These values are combined into a tuple and appended to an initially \n", + "empty list. Your task is to create a program that allows the user to input any \n", + "number of new courses using the `input()` function. When prompting for the \n", + "course number, if the user enters an empty value (simply pressing Enter), the \n", + "program should terminate the loop and display the list of courses.\n", + "\n", + "**Additional Requirement**: When displaying the courses, iterate over the list \n", + "and output each tuple individually. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#Your code here" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.2" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_6_while_notebook-checkpoint.ipynb b/open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_6_while_notebook-checkpoint.ipynb new file mode 100644 index 0000000..a8f5cb1 --- /dev/null +++ b/open-hpi/week-3/notebooks-week-3/.ipynb_checkpoints/week_3_unit_6_while_notebook-checkpoint.ipynb @@ -0,0 +1,386 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# The [while](https://docs.python.org/3/reference/compound_stmts.html#while) loop\n", + "\n", + "You are already familiar with the `for` loop. There is another loop in Python, the\n", + "`while` loop. The `while` loop offers more flexibility, but can be a bit more\n", + "complex to use.\n", + "\n", + "The `for` loop works well when you have a sequence to iterate over, like a list or\n", + "a range. However, there are scenarios where you need to repeat an operation multiple\n", + "times without a predefined sequence:\n", + "- At an ATM, the system prompts for a PIN until it is entered correctly.\n", + "- A user must enter a value that can be converted into a number.\n", + "- A user should keep answering questions until none remain. In such cases, a `while`\n", + " loop is a better choice than using a `for` loop with \"tricks\".\n", + "\n", + "## Syntax of the `while` loop\n", + "\n", + "The simplest form of a `while` loop is structured as follows:\n", + "\n", + "```python\n", + "while condition:\n", + " statement1\n", + " statement2\n", + " ...\n", + " statementN\n", + "```\n", + "\n", + "The loop begins with the keyword `while`, followed by a condition and a colon, much\n", + "like an `if` statement. The subsequent block of code should be indented, similar to\n", + "other control structures. When the loop's indented code is done executing, the program\n", + "resumes execution with the non-indented code following the loop.\n", + "\n", + "## How a `while` loop works\n", + "\n", + "To start, the loop checks the condition specified after `while`. If the condition is\n", + "`True`, the loop's body is executed. Upon reaching the end of the loop body, the\n", + "program returns to the `while` condition and checks it again. \n", + "The loop continues running as long as the condition remains `True`. Once the condition\n", + "becomes `False`, the loop ends and the program continues with the code following the\n", + "loop. \n", + "If the condition is `False` the first time the `while` loop is encountered, the loop's\n", + "body is skipped entirely and the program continues immediately after the loop.\n", + "\n", + "## Example 1: Using `input()` until a number is entered\n", + "\n", + "Consider this problem: You need to enter a number using `input()`. For calculations,\n", + "the entered string must first be converted to a number, such as an integer using\n", + "`int()`. If the user enters non-numeric input, the conversion will cause an error.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "number = input(\"Please enter a number: \")\n", + "number = int(number)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To avoid this, we can use a `while` loop. The loop's condition checks if the input is\n", + "numeric using the `.isdecimal()` method. Until a valid number is entered, the loop can\n", + "keep requesting input. Once a number is entered, conversion occurs **after** the loop." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# The loop runs until a suitable input is available\n", + "number = \"x\"\n", + "while not (number.isdecimal()):\n", + " number = input(\"Please enter number: \")\n", + "\n", + "number = int(number)\n", + "print(number)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "An *imperfection* in the loop is visible above: To check the condition initially, the\n", + "variable `number` must be declared first but with a value that is not a number.\n", + "Setting `number = \"x\"` is solely to initialize it with something besides an integer.\n", + "\n", + "Alternatively, this variant is possible:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# The loop runs until a suitable input is available\n", + "number = input(\"Please enter number: \")\n", + "while not (number.isdecimal()):\n", + " number = input(\"Please enter number: \")\n", + "\n", + "number = int(number)\n", + "print(number)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Although the statement in line two is less \"meaningless,\" there is now a\n", + "duplicated `input` statements. This is not the most elegant programming style. \n", + "Question: Why can the `int(number)` conversion occur only after the loop?\n", + "\n", + "## Example 2: Check PIN\n", + "\n", + "In this example, we have a secret PIN. The user must enter the correct PIN to proceed\n", + "beyond the `while` loop. The program structure is similar to the previous example.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "secret_pin = 1234\n", + "\n", + "pin = int(input(\"Please enter PIN: \"))\n", + "\n", + "while pin != secret_pin:\n", + " print(\"The PIN was wrong.\")\n", + " pin = int(input(\"Please enter PIN: \"))\n", + "\n", + "print(pin, \"is correct\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In real life, attempts at entering a PIN are usually limited. ATMs typically allow \n", + "only three tries before blocking. How can we represent this in our loop?\n", + "\n", + "We need a condition that checks both if the PIN is correct and if attempts exceed \n", + "three. Here's how this can be implemented:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "secret_pin = 1234\n", + "\n", + "pin = int(input(\"Please enter PIN: \"))\n", + "attempts = 1\n", + "\n", + "while (pin != secret_pin) and (attempts < 3):\n", + " print(\"The PIN was wrong.\")\n", + " pin = int(input(\"Please enter PIN: \"))\n", + " attempts += 1\n", + "\n", + "if pin == secret_pin:\n", + " print(pin, \"is correct\")\n", + "else:\n", + " print(\"You entered the wrong PIN three times, your card will be confiscated.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This program also has *imperfections*. A second variable `attempts` is required, \n", + "initialized before the loop and incremented inside it. After the loop, it's not \n", + "directly clear why it ended — was it a correct PIN or exceeded attempts? An `if` \n", + "statement is needed to clarify this." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 3: Simple counter\n", + "\n", + "In the previous example, a counter (`attempts`) was used, illustrating the basic act \n", + "of incrementing variables. Here's how you can count from 1 to 10 with a `while` loop:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Simple counter\n", + "# The following program is to count from 1 - 10\n", + "i = 1\n", + "while i <= 10:\n", + " print(i)\n", + " i += 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This solution appears straightforward. However, subtle details can \n", + "alter program behavior:\n", + "- Should `i` start at 1 or 0?\n", + "- Should the condition be `i <= 10` or `i < 10`?\n", + "- Should the comparison value be 10 or 11?\n", + "- Should the increment (`i += 1`) occur before or after `print()` in the loop body?\n", + "\n", + "Each small variation changes how the program behaves. Therefore, when applying `while`\n", + "loops, pay attention to boundary conditions. Experiment by modifying the above program\n", + "with these questions in mind and predict its output.\n", + "\n", + "## A Classic error: The infinite loop\n", + "\n", + "A common mistake is an infinite loop: a loop that never stops because its condition \n", + "always remains `True`. For instance, in the previous example, if the increment is \n", + "forgotten, the loop becomes infinite. \n", + "Note: To halt an infinite loop, press the Stop button at the top.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# The following program is to count from 1 - 10\n", + "i = 1\n", + "while i <= 10:\n", + " print(i)\n", + " # Because of the commented out (forgotten) increment, the loop runs endlessly.\n", + " # i += 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Using a `for` loop is simpler and more robust as it automatically checks boundaries. \n", + "Use a `for` loop when possible; it is simpler and thus reduces potential errors.\n", + "\n", + "# Example 4: Guessing a random number\n", + "\n", + "A `while` loop is well-suited for implementing a number guessing game.\n", + "In this game, a random number between 1 and 100 is generated. Unlike the previous PIN example, the secret number \n", + "is unknown to the user. After each guess, the program provides a hint indicating whether the secret number is higher \n", + "or lower than the user's guess. This process continues until the user correctly guesses the number.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "\n", + "secret_number = random.randint(1, 100)\n", + "\n", + "guessed_number = int(input(\"Please guess a number: \"))\n", + "\n", + "while guessed_number != secret_number:\n", + " if guessed_number < secret_number:\n", + " print(\"The number\", guessed_number, \"was too small.\")\n", + " else:\n", + " print(\"The number\", guessed_number, \"was too big.\")\n", + "\n", + " guessed_number = int(input(\"Please guess a number: \"))\n", + "\n", + "print(\"Correct!\", guessed_number, \"was the number you were looking for.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Exiting the loop with a `break`\n", + "\n", + "A while loop can be exited early using the break statement. When break is executed, the loop stops immediately — even if the loop condition is still true. After a break, the condition is no longer checked. Therefore, break is typically used inside an if statement within the loop.\n", + "\n", + "Using break can simplify certain programming tasks. For example, suppose we want to enter data for multiple students, where each student has a matriculation number, last name, and first name. Since we don’t know in advance how many students will be entered, a while loop is a good choice.\n", + "\n", + "We want the loop to stop when the user presses the return key without entering a matriculation number. This can be implemented cleanly using break, as shown below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list_of_students = []\n", + "\n", + "while True:\n", + " matnr = input(\"Enter matriculation number: \")\n", + " if matnr == \"\":\n", + " break\n", + " name = input(\"Enter name: \")\n", + " firstname = input(\"Enter first name: \")\n", + " list_of_students.append((matnr, name, firstname))\n", + "\n", + "print(list_of_students)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`while True:` sets up an infinite loop since `True` is always true. The loop only exits \n", + "via `break`. After the first `input()`, a condition checks whether to exit. If so, \n", + "the `break` ends the loop and prevents further `input()` calls." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exercise: Calculating value growth\n", + "\n", + "The value of a property increases by *p* percent every year. Write a program that calculates the value of the property\n", + "for each year until the value has doubled. Use `input()` to ask for the percentage and the initial value. \n", + "\n", + "Example input:\n", + "```\n", + "What is the property's value? 10000 \n", + "By what percentage does it increase yearly? 5\n", + "```\n", + "\n", + "Example output:\n", + "```\n", + "Year 0 - 10000.0 \n", + "Year 1 - 10500.0 \n", + "Year 2 - 11025.0\n", + "Year 3 - 11576.25\n", + "...\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#Your code here" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.2" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/open-hpi/week-3/notebooks-week-3/week_3_additional_exercises.ipynb b/open-hpi/week-3/notebooks-week-3/week_3_additional_exercises.ipynb new file mode 100644 index 0000000..8e9745f --- /dev/null +++ b/open-hpi/week-3/notebooks-week-3/week_3_additional_exercises.ipynb @@ -0,0 +1,173 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Week 3 - Additional Exercises\n", + "## Tuples, Dictionaries" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 1: Create Tuple\n", + "The Guardian publishes the current [football tables](https://www.theguardian.com/football/tables) of the major European football leagues ⚽️. (If you like to do this task with your own league, you can of course choose any other webpage hosting your team, your sports, your league 🙂).\n", + "\n", + "For each of the first three teams of your favourite league create a tuple consisting of *Name of the Team, Position, Games Played* and *Points*. Use three variables `team1`, `team2`, `team3` to store these tuples. Using a `for` loop iterate over one team and print the individual elements." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# First line could be\n", + "# team1 = (\"Man City\", 1, 31, 74)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 2: Add these tuples to a list\n", + "Create a list called `league` and add each team to this list. Iterate over this list and output each team." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 3: Using `input()` add three more teams to the league\n", + "Three teams are not enough! Add three more teams to your league. Use a `for` loop iterating over `range(3)`. Within the `for` loop use `input()` to get the required values (name, position, games played, points). Do not forget to cast the input, if a certain data type is preferred. At the end of the loop create a tuple and append it to your `league`. Once the enlarged `league` is built up, again print all the teams. (If you run the program several times, you can really increase the number of teams in your league.)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 4: Print out the position of a team\n", + "Using `input()` ask for a name of a team. If the team is in the list, output the current position of this team. If the team is not in the list, then output, that the team does not play in your league." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 5: Convert the list into a dictionary\n", + "As you have seen in the last exercise, it is necessary to loop over the list to find a certain team. Maybe, we should convert the *list of tuples* into a *dictionary of tuples* named `new_league`. Take the name of each team as the key and the other values as a (new) tuple which is the value assigned to that key. Finally, iterate over the dictionary and print each key-value-pair." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 6: Print out the position of a team\n", + "Redo exercise 4. Of course you should use the dictionary `new_league` to get the data." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 7: Add the goal difference\n", + "You realize, that to position two teams with same number of points the *goal difference (GD)* is required. Add the goal difference to the tuple containing the team values. As a tuple is unmutable, you have to first deconstruct the tuple, then add the goal difference and finally construct a new tuple. You can get the goal difference again using `input()`. Finally print the values of all teams of `new_league`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 8: Convert the tuples into dictionaries\n", + "As you have experienced in the above exercise, adding new information to the teams is quite cumbersome as a tuple is immutable. That means all values from the old tuple have to be picked and placed into a new tuple with the additional values. To make live easier in the future, the tuples should next be changed into a dictionary. Iterate over the dictionary `new_league`. For each team create a new dictionary `team_info`. For the keys use descriptive names like `\"points\"` or `\"goal difference\"`. Assign the value from the tuples as value to these keys. Assign the dictionary `team_info` as the value to the team in the dictionary of the teams. Finally, loop over the dictionary and display the team information." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 9: Add Points/Game\n", + "As another indicator per team, the average number of points per game should be calculated and added as info to each team. Loop over `new_league`, calculate this new indicator and add it to the team values. Finally, for each team print the name and the points per game." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/open-hpi/week-3/notebooks-week-3/week_3_additional_exercises_solution.ipynb b/open-hpi/week-3/notebooks-week-3/week_3_additional_exercises_solution.ipynb new file mode 100644 index 0000000..024d504 --- /dev/null +++ b/open-hpi/week-3/notebooks-week-3/week_3_additional_exercises_solution.ipynb @@ -0,0 +1,244 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Week 3 - Additional Exercises\n", + "## Tuples, Dictionaries" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 1: Create Tuple\n", + "The Guardian publishes the current [football tables](https://www.theguardian.com/football/tables) of the major European football leagues ⚽️. (If you like to do this task with your own league, you can of course choose any other webpage hosting your team, your sports, your league 🙂).\n", + "\n", + "For each of the first three teams of your favourite league create a tuple consisting of *Name of the Team, Position, Games Played* and *Points*. Use three variables `team1`, `team2`, `team3` to store these tuples. Using a `for` loop iterate over one team and print the individual elements." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "team1 = (\"Man City\", 1, 31, 74)\n", + "team2 = (\"Liverpool\", 2, 31, 73)\n", + "team3 = (\"Chelsea\", 3, 30, 62)\n", + "for element in team2:\n", + " print(element)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 2: Add these tuples to a list\n", + "Create a list called `league` and add each team to this list. Iterate over this list and output each team." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "league = [team1, team2, team3]\n", + "for team in league:\n", + " print(team)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 3: Using `input()` add three more teams to the league\n", + "Three teams are not enough! Add three more teams to your league. Use a `for` loop iterating over `range(3)`. Within the `for` loop use `input()` to get the required values (name, position, games played, points). Do not forget to cast the input, if a certain data type is preferred. At the end of the loop create a tuple and append it to your `league`. Once the enlarged `league` is built up, again print all the teams. (If you run the program several times, you can really increase the number of teams in your league.)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for i in range(3):\n", + " name = input(\"Name of team: \")\n", + " position = int(input(\"Position of team: \"))\n", + " games_played = int(input(\"Games played: \"))\n", + " points = int(input(\"Points achieved so far: \"))\n", + " team = (name, position, games_played, points)\n", + " league.append(team)\n", + " \n", + "for team in league:\n", + " print(team)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 4: Print out the position of a team\n", + "Using `input()` ask for a name of a team. If the team is in the list, output the current position of this team. If the team is not in the list, then output, that the team does not play in your league." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "name = input(\"From which team do you want to have the position: \")\n", + "\n", + "found = False\n", + "for team in league:\n", + " if team[0] == name:\n", + " print(team[0], \"is currently on position\", team[1])\n", + " found = True\n", + "if not found:\n", + " print(name, \"does not play in the league\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 5: Convert the list into a dictionary\n", + "As you have seen in the last exercise, it is necessary to loop over the list to find a certain team. Maybe, we should convert the *list of tuples* into a *dictionary of tuples* named `new_league`. Take the name of each team as the key and the other values as a (new) tuple which is the value assigned to that key. Finally, iterate over the dictionary and print each key-value-pair." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "new_league = {}\n", + "\n", + "for team in league:\n", + " values = (team[1], team [2], team[3])\n", + " new_league[team[0]] = values\n", + " \n", + "for team, values in new_league.items():\n", + " print(team, values)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 6: Print out the position of a team\n", + "Redo exercise 4. Of course you should use the dictionary `new_league` to get the data." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "team = input(\"From which team do you want to have the position: \")\n", + "\n", + "if team in new_league:\n", + " print(team, \"is currently on position\", new_league[team][0])\n", + "else:\n", + " print(team, \"does not play in the league\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 7: Add the goal difference\n", + "You realize, that to position two teams with same number of points the *goal difference (GD)* is required. Add the goal difference to the tuple containing the team values. As a tuple is unmutable, you have to first deconstruct the tuple, then add the goal difference and finally construct a new tuple. You can get the goal difference again using `input()`. Finally print the values of all teams of `new_league`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for team, values in new_league.items():\n", + " question = \"What is the goal difference for \" + team + \"? \"\n", + " goal_difference = int(input(question))\n", + " new_values = (values[0], values[1], values[2], goal_difference)\n", + " new_league[team] = new_values\n", + " \n", + "for team, values in new_league.items():\n", + " print(team, values)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 8: Convert the tuples into dictionaries\n", + "As you have experienced in the above exercise, adding new information to the teams is quite cumbersome as a tuple is immutable. That means all values from the old tuple have to be picked and placed into a new tuple with the additional values. To make live easier in the future, the tuples should next be changed into a dictionary. Iterate over the dictionary `new_league`. For each team create a new dictionary `team_info`. For the keys use descriptive names like `\"points\"` or `\"goal difference\"`. Assign the value from the tuples as value to these keys. Assign the dictionary `team_info` as the value to the team in the dictionary of the teams. Finally, loop over the dictionary and display the team information." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for key, values in new_league.items():\n", + " team_info = {}\n", + " team_info[\"position\"] = values[0]\n", + " team_info[\"games played\"] = values[1]\n", + " team_info[\"points\"] = values[2]\n", + " team_info[\"goal difference\"] = values[3]\n", + " new_league[key] = team_info\n", + " \n", + "for key, values in new_league.items():\n", + " print(key, values)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 9: Add Points/Game\n", + "As another indicator per team, the average number of points per game should be calculated and added as info to each team. Loop over `new_league`, calculate this new indicator and add it to the team values. Finally, for each team print the name and the points per game." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for team, values in new_league.items():\n", + " points_per_game = values[\"points\"] / values[\"games played\"]\n", + " values[\"points per game\"] = points_per_game\n", + "\n", + "for team, values in new_league.items():\n", + " print(team, values[\"points per game\"])" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/open-hpi/week-3/notebooks-week-3/week_3_unit_1_tuples_notebook.ipynb b/open-hpi/week-3/notebooks-week-3/week_3_unit_1_tuples_notebook.ipynb new file mode 100644 index 0000000..dca09d5 --- /dev/null +++ b/open-hpi/week-3/notebooks-week-3/week_3_unit_1_tuples_notebook.ipynb @@ -0,0 +1,448 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# What are [tuples](https://docs.python.org/3/library/stdtypes.html?#tuples)?\n", + "\n", + "## Definition\n", + "\n", + "A `tuple` is a complex data type, similar to lists, but with a key difference:\n", + "tuples are immutable, while lists are mutable. This means that once a tuple is\n", + "created, its elements cannot be changed.\n", + "\n", + "A tuple consists of multiple elements enclosed in parentheses: `()`. The\n", + "elements are separated by commas. Just like with lists and simple data types,\n", + "tuples can be assigned to variables. Additionally, tuples can store other tuples\n", + "or lists as elements.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(52066, 'Aachen', 'Eupener Str. 70', '0241-6009-12345')\n", + "('Peter', 'Meier', 123456, 'SAP Basics', 'pm12345s@university.edu', (52066, 'Aachen', 'Eupener Str. 70', '0241-6009-12345'))\n", + "(12312, 'absbsb', [1, 2, 3, 4], ('a', 'b', 'c'), 'end')\n" + ] + } + ], + "source": [ + "address = (52066, \"Aachen\", \"Eupener Str. 70\", \"0241-6009-12345\")\n", + "print(address)\n", + "student = (\"Peter\", \"Meier\", 123456, \"SAP Basics\", \"pm12345s@university.edu\", address)\n", + "print(student)\n", + "tup1 = (12312, \"absbsb\", [1, 2, 3, 4], (\"a\", \"b\", \"c\"), \"end\")\n", + "print(tup1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Tuples cannot be modified\n", + "\n", + "The key difference between tuples and lists is that lists can be modified, while\n", + "tuples cannot — they are immutable. This means you cannot add or remove elements\n", + "in a tuple or change the value of an existing element. While you can assign a\n", + "new tuple to a variable, there are no methods like `tuple.append()` or\n", + "`tuple.remove()` to alter tuples directly.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a\n" + ] + }, + { + "ename": "TypeError", + "evalue": "'tuple' object does not support item assignment", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mTypeError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[5]\u001b[39m\u001b[32m, line 3\u001b[39m\n\u001b[32m 1\u001b[39m tup = (\u001b[32m1\u001b[39m, \u001b[32m2\u001b[39m, \u001b[33m\"\u001b[39m\u001b[33ma\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 2\u001b[39m \u001b[38;5;28mprint\u001b[39m(tup[\u001b[32m2\u001b[39m])\n\u001b[32m----> \u001b[39m\u001b[32m3\u001b[39m \u001b[43mtup\u001b[49m\u001b[43m[\u001b[49m\u001b[32;43m2\u001b[39;49m\u001b[43m]\u001b[49m = \u001b[33m\"\u001b[39m\u001b[33mb\u001b[39m\u001b[33m\"\u001b[39m\n", + "\u001b[31mTypeError\u001b[39m: 'tuple' object does not support item assignment" + ] + } + ], + "source": [ + "tup = (1, 2, \"a\")\n", + "print(tup[2])\n", + "tup[2] = \"b\"" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(1, 2, 3)\n", + "('a', 'b', 'c', 'd')\n" + ] + } + ], + "source": [ + "tup = (1, 2, 3)\n", + "print(tup)\n", + "tup = (\"a\", \"b\", \"c\", \"d\")\n", + "print(tup)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## What are tuples used for?\n", + "\n", + "Tuples are quite similar to lists. In this unit, we will not delve deeply into\n", + "data modeling; that will be covered in Unit 3. However, here is a simple rule of\n", + "thumb for choosing between lists and tuples:\n", + "\n", + "- Use lists for collections of similar items.\n", + "- Use tuples for items with many attributes.\n", + "\n", + "Consider the following examples where tuples are appropriate:\n", + "\n", + "- `address = (zip_code, city, street, house_number)`\n", + "- `position = (x_coordinate, y_coordinate)`\n", + "- `date = (year, month, day)`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Basic operations on tuples\n", + "### Creating a Tuple Interactively\n", + "\n", + "You can use the `input()` function to read values and assemble them into a\n", + "tuple.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "Please enter name: Sixpack\n", + "Please enter first name: Joe\n", + "Please enter phone number: 555-4457\n", + "Please enter age (integer): 38\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "('Sixpack', 'Joe', '555-4457', '38')\n" + ] + } + ], + "source": [ + "name = input(\"Please enter name: \")\n", + "first_name = input(\"Please enter first name: \")\n", + "phone = input(\"Please enter phone number: \")\n", + "age = input(\"Please enter age (integer): \")\n", + "\n", + "employee = (name, first_name, phone, age)\n", + "\n", + "print(employee)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Using an index with tuples\n", + "\n", + "Just like lists, tuples use an index that is represented with square brackets\n", + "`[]`. The index starts at 0. You can also use negative indices, similar to\n", + "lists. \n", + "\n", + "**Important:** Even though you use square brackets to access elements,\n", + "it remains a tuple, not a list.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "52066\n", + "Parker\n", + "(52066, 'Aachen', 'Eupener Str. 70', '0241-6009-12345')\n", + "Eupener Str. 70\n", + "(52066, 'Aachen', 'Eupener Str. 70', '0241-6009-12345')\n", + "Aachen\n", + "Eupener Str. 70\n" + ] + } + ], + "source": [ + "address = (52066, \"Aachen\", \"Eupener Str. 70\", \"0241-6009-12345\")\n", + "\n", + "student = (\n", + " \"Peter\",\n", + " \"Parker\",\n", + " 123456,\n", + " \"Python for Beginners\",\n", + " \"pp12345s@university.edu\",\n", + " address,\n", + ")\n", + "\n", + "print(address[0])\n", + "print(student[1])\n", + "print(student[5])\n", + "print(student[5][2])\n", + "print(student[-1])\n", + "print(address[-3])\n", + "print(address[2])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Once again: Tuples are immutable\n", + "\n", + "Attempting to change a single element of a tuple by using its index will result\n", + "in an error." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "'tuple' object does not support item assignment", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mTypeError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[8]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m address = (\u001b[32m52066\u001b[39m, \u001b[33m\"\u001b[39m\u001b[33mAachen\u001b[39m\u001b[33m\"\u001b[39m, \u001b[33m\"\u001b[39m\u001b[33mEupener Str. 70\u001b[39m\u001b[33m\"\u001b[39m, \u001b[33m\"\u001b[39m\u001b[33m0241-6009-12345\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m \u001b[43maddress\u001b[49m\u001b[43m[\u001b[49m\u001b[32;43m2\u001b[39;49m\u001b[43m]\u001b[49m = \u001b[33m\"\u001b[39m\u001b[33mGoethestraße 1\u001b[39m\u001b[33m\"\u001b[39m\n", + "\u001b[31mTypeError\u001b[39m: 'tuple' object does not support item assignment" + ] + } + ], + "source": [ + "address = (52066, \"Aachen\", \"Eupener Str. 70\", \"0241-6009-12345\")\n", + "address[2] = \"Goethestraße 1\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Slicing operator and functions & methods\n", + "\n", + "The slicing operator, familiar from lists, also works with tuples to create a\n", + "sub-tuple. You can use indices to access not only a single element, but an\n", + "entire range. Some functions and methods work with tuples as well, as shown in\n", + "the table below:\n", + "\n", + "| Function / Method | Return Value |\n", + "| ----------------- | ---------------------------------------------------------------------------------------- |\n", + "| `len(tuple)` | Number of elements in the tuple |\n", + "| `tuple.count(x)` | Number of times element *x* appears in the tuple |\n", + "| `tuple.index(x)` | Index of the first occurrence of *x*. If *x* does not exist, an error will occur |\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "('trois', 'four')\n", + "6\n", + "1\n", + "4\n" + ] + } + ], + "source": [ + "numbers = (1, 2, \"trois\", \"four\", \"V\", 6)\n", + "print(numbers[2:4])\n", + "print(len(numbers))\n", + "print(numbers.count(1))\n", + "print(numbers.index(\"V\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Looping through tuples\n", + "\n", + "Similar to lists, a `for` loop can be used to access each element of a tuple." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "52066\n", + "Aachen\n", + "Eupener Str. 70\n", + "0241-6009-12345\n" + ] + } + ], + "source": [ + "address = (52066, \"Aachen\", \"Eupener Str. 70\", \"0241-6009-12345\")\n", + "\n", + "for address_part in address:\n", + " print(address_part)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Conversion of tuples\n", + "\n", + "There are functions, like `int()`, to convert data types — such as from a string\n", + "to an integer. Similar conversions exist between lists and tuples: `list()`\n", + "converts the argument into a list, and `tuple()` converts it into a tuple.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(1, 2, 'a', 2.3)\n", + "[1, 2, 'a', 2.3]\n" + ] + } + ], + "source": [ + "l = [1, 2, \"a\", 2.3]\n", + "t = tuple(l)\n", + "print(t)\n", + "l = list(t)\n", + "print(l)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Unit 1: Exercise\n", + "## Anweisungen:\n", + "\n", + "Now it's your turn. Click the button below to open CodeOcean and implement a solution for the given exercise. If you want, you can also solve it in a Jupyter Notebook first and copy the solution to CodeOcean afterwards. \n", + "\n", + "Write a program that asks the user for given name, surname and field of study for a student. Store this data in a tuple and print the tuple. Below is an example execution of the program. \n", + "\n", + "Please enter the given name of the student: Harry \n", + "Please enter the surname of the student: Potter \n", + "Please enter the filed of study of the student: Defence Against the Dark Arts \n", + "('Harry', 'Potter', 'Defence Against the Dark Arts')\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "Please enter the given name of the studend: Harry\n", + "Please enter the surname of the studend: Potter\n", + "Please enter the field of study of the student: Defence Against the Dark Arts\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "('Harry', 'Potter', 'Defence Against the Dark Arts')\n" + ] + } + ], + "source": [ + "given_name = input(\"Please enter the given name of the studend: \")\n", + "surname = input(\"Please enter the surname of the studend: \")\n", + "field_of_study = input(\"Please enter the field of study of the student: \")\n", + "\n", + "student = (given_name, surname, field_of_study)\n", + "print(student)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "interpreter": { + "hash": "ac59ebe37160ed0dfa835113d9b8498d9f09ceb179beaac4002f036b9467c963" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.12" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/open-hpi/week-3/notebooks-week-3/week_3_unit_2_dictionaries_notebook.ipynb b/open-hpi/week-3/notebooks-week-3/week_3_unit_2_dictionaries_notebook.ipynb new file mode 100644 index 0000000..5f08147 --- /dev/null +++ b/open-hpi/week-3/notebooks-week-3/week_3_unit_2_dictionaries_notebook.ipynb @@ -0,0 +1,759 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# What are dictionaries?\n", + "\n", + "In both lists and tuples, elements are accessed using an index. This method has a\n", + "drawback: to access a specific element, you need to know its exact index, which \n", + "can be challenging when elements are added or removed. In such cases, the indices \n", + "of subsequent elements change, making programming cumbersome due to constant \n", + "index tracking.\n", + "\n", + "## Accessing Elements by Name\n", + "\n", + "Dictionaries offer a solution to this problem. Like lists and tuples, dictionaries \n", + "can store multiple elements. The key difference is that elements in a dictionary \n", + "are accessed by a name rather than an index, using a key-value format.\n", + "\n", + "For example, consider a telephone book. A telephone book lists numerous phone \n", + "numbers, each associated with a name:\n", + "\n", + "| Name | Telephone Number |\n", + "| :----------- | ---------------: |\n", + "| P. McCartney | 123 456 |\n", + "| J. Lennon | 987 654 321 |\n", + "| G. Harrison | 11 342 555 |\n", + "| R. Starr | 777 888 32 |\n", + "\n", + "In a telephone book, you don't seek the third element; instead, you look for \n", + "the number corresponding to a specific name, like *G. Harrison*. Essentially, you \n", + "want the number using the name directly. Accessing an element by name is what dictionaries are good for. Instead of \"names,\" we call them \"keys\" in dictionary terminology.\n", + "\n", + "## Using Dictionaries\n", + "\n", + "A dictionary is a collection of key-value pairs. Dictionaries are enclosed in \n", + "curly braces `{}`. Inside the braces, key-value pairs are separated by commas, \n", + "and each key-value pair is of the form: `key: value`. A dictionary looks like \n", + "this: `{key1: value1, key2: value2, ..., keyN: valueN}`. Here's an example:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'P. McCartney': 123456, 'J. Lennon': 987654321, 'G. Harrison': 11342555, 'R. Starr': 77788832}\n" + ] + } + ], + "source": [ + "phone_book = {\n", + " \"P. McCartney\": 123456,\n", + " \"J. Lennon\": 987654321,\n", + " \"G. Harrison\": 11342555,\n", + " \"R. Starr\": 77788832,\n", + "}\n", + "print(phone_book)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can access individual elements of `phone_book` using the key (person's name). \n", + "To do this, place the key in square brackets `[ ]` following the dictionary's \n", + "name." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "11342555\n" + ] + } + ], + "source": [ + "print(phone_book[\"G. Harrison\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Access by index, however, is not possible, as shown below:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "ename": "KeyError", + "evalue": "2", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mKeyError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[43mphone_book\u001b[49m\u001b[43m[\u001b[49m\u001b[32;43m2\u001b[39;49m\u001b[43m]\u001b[49m)\n", + "\u001b[31mKeyError\u001b[39m: 2" + ] + } + ], + "source": [ + "print(phone_book[2])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Attempting to use an index with a dictionary will lead to an error, as dictionaries\n", + "do not have this index-based access. Instead, they rely on keys, and accessing a \n", + "non-existing key results in a key error.\n", + "\n", + "Here's another example demonstrating this point:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "ename": "KeyError", + "evalue": "'P. Best'", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mKeyError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[4]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[43mphone_book\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mP. Best\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m)\n", + "\u001b[31mKeyError\u001b[39m: 'P. Best'" + ] + } + ], + "source": [ + "print(phone_book[\"P. Best\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Adding key-value pairs to a dictionary\n", + "\n", + "You can easily add new key-value pairs to a dictionary using the following syntax: \n", + "`dictionary[key] = value`. In the example below, additional phone numbers are \n", + "added to our telephone book:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'P. McCartney': 123456, 'J. Lennon': 987654321, 'G. Harrison': 11342555, 'R. Starr': 77788832, 'Y. Ono': 5463333, 'B. Epstein': 9998777, 'B. Geröllheimer': 9998777}\n" + ] + } + ], + "source": [ + "phone_book[\"Y. Ono\"] = 5463333\n", + "phone_book[\"B. Geröllheimer\"] = 9998777\n", + "print(phone_book)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Replacing the value for an existing key\n", + "\n", + "If you assign a new value to an already existing key in a dictionary, the value \n", + "is overwritten. This feature is useful for updating or changing entries but be \n", + "careful not to overwrite the wrong entries in your dictionary!" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'P. McCartney': 654321, 'J. Lennon': 987654321, 'G. Harrison': 11342555, 'R. Starr': 77788832, 'Y. Ono': 5463333, 'B. Epstein': 9998777, 'B. Geröllheimer': 9998777}\n" + ] + } + ], + "source": [ + "phone_book[\"P. McCartney\"] = 654321\n", + "print(phone_book)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### More about keys and values\n", + "\n", + "In our telephone book example, all keys were strings and all values were integers. \n", + "However, dictionaries are not limited to these data types. *Values* can be of any \n", + "data type, including lists, tuples, and even other dictionaries. *Keys* can also \n", + "be nearly any data type (more on the requirements for keys below). \n", + "Within a dictionary, data \n", + "types can be mixed, as demonstrated in the example below:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{123: 'blabla', True: 123.456, 'key': False, (123, 'abc'): 1000, 34.5: [0, 1, 1, 2, 3, 5]}\n" + ] + } + ], + "source": [ + "stupid_dict = {\n", + " 123: \"blabla\",\n", + " True: 123.456,\n", + " \"key\": False,\n", + " (123, \"abc\"): 1000,\n", + " 34.5: [0, 1, 1, 2, 3, 5],\n", + "}\n", + "print(stupid_dict)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The key in a dictionary is immutable\n", + "\n", + "In contrast to values, keys in a dictionary must be immutable.[1](#fn1) Acceptable key \n", + "types include `integers`, `strings`, and `tuples`, but not lists, since lists \n", + "can be modified. Consider these two similar examples:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{('Paul', 'McCartney'): 123456}\n" + ] + } + ], + "source": [ + "new_phone_book = {(\"Paul\", \"McCartney\"): 123456}\n", + "print(new_phone_book)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "unhashable type: 'list'", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mTypeError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[10]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m new_phone_book = {[\u001b[33m\"\u001b[39m\u001b[33mPaul\u001b[39m\u001b[33m\"\u001b[39m, \u001b[33m\"\u001b[39m\u001b[33mMcCartney\u001b[39m\u001b[33m\"\u001b[39m]: \u001b[32m123456\u001b[39m}\n\u001b[32m 2\u001b[39m \u001b[38;5;28mprint\u001b[39m(new_phone_book)\n", + "\u001b[31mTypeError\u001b[39m: unhashable type: 'list'" + ] + } + ], + "source": [ + "new_phone_book = {[\"Paul\", \"McCartney\"]: 123456}\n", + "print(new_phone_book)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Deleting an element\n", + "You can delete elements from a dictionary using the `del` keyword. Review the \n", + "example below. What happens if you run this cell twice? Why does the error occur?" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'J. Lennon': 987654321, 'G. Harrison': 11342555, 'R. Starr': 77788832, 'Y. Ono': 5463333, 'B. Epstein': 9998777, 'B. Geröllheimer': 9998777}\n" + ] + } + ], + "source": [ + "del phone_book[\"P. McCartney\"]\n", + "print(phone_book)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Iterating over a dictionary with a `for` loop\n", + "\n", + "Just like lists and tuples, dictionaries can be iterated over using a `for` loop. \n", + "The syntax is as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "J. Lennon 987654321\n", + "G. Harrison 11342555\n", + "R. Starr 77788832\n", + "Y. Ono 5463333\n", + "B. Epstein 9998777\n", + "B. Geröllheimer 9998777\n" + ] + } + ], + "source": [ + "for name in phone_book:\n", + " print(name, phone_book[name])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Usage of functions and methods with dictionaries\n", + "\n", + "Several functions and methods can be used to manage dictionaries.\n", + "\n", + "### General functions and methods\n", + "\n", + "| Function/Method | Return Value |\n", + "| --------------- | ------------------------------------------- |\n", + "| `len()` | Number of key-value pairs in the dictionary |\n", + "| `.keys()` | All keys of a dictionary |\n", + "| `.values()` | All values of a dictionary |\n", + "| `.items()` | All `key:value` pairs as tuples |\n", + "\n", + "> **NOTE:** The `.keys()` method returns a `dict_keys` data type. You can convert \n", + "this to a list with the `list()` function.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "6\n" + ] + } + ], + "source": [ + "print(len(phone_book))" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dict_keys(['J. Lennon', 'G. Harrison', 'R. Starr', 'Y. Ono', 'B. Epstein', 'B. Geröllheimer'])\n", + "\n", + "['J. Lennon', 'G. Harrison', 'R. Starr', 'Y. Ono', 'B. Epstein', 'B. Geröllheimer']\n" + ] + } + ], + "source": [ + "print(phone_book.keys())\n", + "print(type(phone_book.keys()))\n", + "print(list(phone_book.keys()))" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[987654321, 11342555, 77788832, 5463333, 9998777, 9998777]\n" + ] + } + ], + "source": [ + "print(list(phone_book.values()))" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dict_items([('J. Lennon', 987654321), ('G. Harrison', 11342555), ('R. Starr', 77788832), ('Y. Ono', 5463333), ('B. Epstein', 9998777), ('B. Geröllheimer', 9998777)])\n", + "{'J. Lennon': 987654321, 'G. Harrison': 11342555, 'R. Starr': 77788832, 'Y. Ono': 5463333, 'B. Epstein': 9998777, 'B. Geröllheimer': 9998777}\n", + "{'J. Lennon': 987654321, 'G. Harrison': 11342555, 'R. Starr': 77788832, 'Y. Ono': 5463333, 'B. Epstein': 9998777}\n" + ] + } + ], + "source": [ + "print(phone_book.items())\n", + "print(phone_book)\n", + "del phone_book[\"B. Geröllheimer\"]\n", + "print(phone_book)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Typical applications of dictionaries\n", + "\n", + "Below are two scenarios where dictionaries are particularly useful.\n", + "\n", + "### Example 1: Student data\n", + "\n", + "In the telephone book example, the key-value pairs were *name - telephone number*. \n", + "In this example, a dictionary represents a student's data, which includes name, \n", + "first-name, ID, e-mail, city, etc.\n", + "\n", + "Here, the keys and values vary significantly. Each key describes a different \n", + "attribute of the same student. The advantage is you don't need to remember the \n", + "indices of various attributes. Unlike tuples, attributes can be added or \n", + "removed as needed.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'name': 'McCartney', 'firstname': 'Paul', 'subject': 'Music', 'email': 'paul@beatles.co.uk', 'city': 'Liverpool'}\n", + "McCartney\n", + "Liverpool\n" + ] + } + ], + "source": [ + "student = {\n", + " \"name\": \"McCartney\",\n", + " \"firstname\": \"Paul\",\n", + " \"subject\": \"Music\",\n", + " \"email\": \"paul@beatles.co.uk\",\n", + " \"city\": \"Liverpool\",\n", + "}\n", + "print(student)\n", + "print(student[\"name\"])\n", + "print(student[\"city\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example 2: Students and student IDs\n", + "\n", + "When data has a unique identifier (ID), it can serve as the dictionary key. For \n", + "example, each student can have a student ID as a key:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{12345: 'Paul McCartney', 23456: 'John Lennon', 34567: 'George Harrison', 45678: 'Ringo Starr'}\n", + "George Harrison\n" + ] + } + ], + "source": [ + "students = {\n", + " 12345: \"Paul McCartney\",\n", + " 23456: \"John Lennon\",\n", + " 34567: \"George Harrison\",\n", + " 45678: \"Ringo Starr\",\n", + "}\n", + "print(students)\n", + "print(students[34567])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exercise 1: Create a dictionary\n", + "\n", + "Create a dictionary with the English colors *red, green, blue, yellow* as keys \n", + "and their German translations *rot, grün, blau, gelb* as values. " + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'red': 'rot', 'green': 'grün', 'blue': 'blau', 'yellow': 'gelb'}\n" + ] + } + ], + "source": [ + "# Your code here\n", + "color_dict = {\"red\": \"rot\", \"green\": \"grün\", \"blue\": \"blau\", \"yellow\": \"gelb\"}\n", + "print(color_dict)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exercise 2: Translate\n", + "\n", + "Now, use the translation dictionary from the last exercise. Using `input()`, \n", + "ask for a color and provide its translation as output.\n", + "\n", + "Example input:\n", + "\n", + "```\n", + "Which color should be translated? red\n", + "```\n", + "\n", + "\n", + "Example output:\n", + "\n", + "```\n", + "The German word for red is rot.\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "Which color should be translated? red\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The German word for red is rot\n", + "The German word for red is rot\n" + ] + } + ], + "source": [ + "# Your code here\n", + "#print(color_dict.keys())\n", + "color_to_translate = input(\"Which color should be translated? \")\n", + "\n", + "print(\"The German word for \" + color_to_translate + \" is \" + color_dict[color_to_translate])\n", + "print(\"The German word for\", color_to_translate, \"is\", color_dict[color_to_translate])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Footnote\n", + "[1](#fn1-back) Actually, the key must be hashable in a dictionary. While \n", + "all immutable Python 🐍 data types are hashable, not all hashable types are immutable. \n", + "Further discussion is beyond the scope of this course." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Unit 2: Exercise\n", + "## Anweisungen:\n", + "\n", + "Now it's your turn. Click the button below to open CodeOcean and implement a solution for the given exercise. If you want, you can also solve it in a Jupyter Notebook first and copy the solution to CodeOcean afterwards.\n", + "\n", + "One of the nice features of Python is that it supports Unicode. Therefore it is possible to use emojis just like other characters in strings. In this exercise you will use this feature to build an emoji translator.\n", + "\n", + "Below is a dictionary that maps English terms to Emojis (broken into multiple lines for better readability).\n", + "\n", + "{\n", + "\"happy\": \"😃\", \n", + "\"heart\": \"😍\", \n", + "\"rotfl\": \"🤣\", \n", + "\"smile\": \"😊\", \n", + "\"crying\": \"😭\", \n", + "\"kiss\": \"😘\", \n", + "\"clap\": \"👏\", \n", + "\"grin\": \"😁\", \n", + "\"fire\": \"🔥\", \n", + "\"broken\": \"💔\", \n", + "\"think\": \"🤔\", \n", + "\"excited\": \"🤩\", \n", + "\"boring\": \"🙄\", \n", + "\"winking\": \"😉\", \n", + "\"ok\": \"👌\", \n", + "\"hug\": \"🤗\", \n", + "\"cool\": \"😎\", \n", + "\"angry\": \"😠\", \n", + "\"python\": \"🐍\" \n", + "} \n", + "\n", + "Use this dictionary to build a program that:\n", + "\n", + " Reads a sentence from the user.\n", + " Replaces all the words in the sentence with the corresponding Emoji.\n", + "\n", + "Below is an example execution of the program:\n", + "\n", + "Please enter a sentence: I'm so excited to learn python \n", + "I'm so 🤩 to learn 🐍\n" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": {}, + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "Please enter a sentence: If you are happy\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " If you are 😃\n" + ] + } + ], + "source": [ + "emoji_dict = { \"happy\": \"😃\", \"heart\": \"😍\", \"rotfl\": \"🤣\", \"smile\": \"😊\", \"crying\": \"😭\", \"kiss\": \"😘\", \"clap\": \"👏\", \"grin\": \"😁\", \"fire\": \"🔥\", \"broken\": \"💔\",\n", + "\"think\": \"🤔\", \"excited\": \"🤩\", \"boring\": \"🙄\", \"winking\": \"😉\", \"ok\": \"👌\", \"hug\": \"🤗\", \"cool\": \"😎\", \"angry\": \"😠\", \"python\": \"🐍\"} \n", + "\n", + "user_input = input(\"Please enter a sentence: \")\n", + "\n", + "word_list = user_input.split()\n", + "dict_keys = emoji_dict.keys()\n", + "\n", + "result = \"\"\n", + "\n", + "for word in word_list:\n", + " if word in dict_keys:\n", + " emoji = emoji_dict[word]\n", + " word = emoji\n", + " result += \" \" + word \n", + "\n", + "result.strip()\n", + "#result = [result = result + item for item in word_list]\n", + "print(result)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "file_extension": ".py", + "interpreter": { + "hash": "ac59ebe37160ed0dfa835113d9b8498d9f09ceb179beaac4002f036b9467c963" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.12" + }, + "mimetype": "text/x-python", + "name": "python", + "npconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": 3 + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/open-hpi/week-3/notebooks-week-3/week_3_unit_3_whentouse_notebook.ipynb b/open-hpi/week-3/notebooks-week-3/week_3_unit_3_whentouse_notebook.ipynb new file mode 100644 index 0000000..69a57fc --- /dev/null +++ b/open-hpi/week-3/notebooks-week-3/week_3_unit_3_whentouse_notebook.ipynb @@ -0,0 +1,298 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# When to use lists, tuples and dictionaries\n", + "\n", + "So far, we have introduced three complex data types: `list`, `tuple`, and `dictionary`. \n", + "In principle, these data types are interchangeable.[1](#fn1)\n", + "\n", + "For instance, a list can be simulated with a dictionary by using the index of a \n", + "list item as the dictionary key. However, this approach can complicate certain \n", + "operations. For example, if you delete an item from a list, all subsequent indices \n", + "decrease by one. Simulating this behavior with a dictionary requires complex \n", + "operations. Similarly, a tuple can also be mimicked with a list, but using a list \n", + "sacrifices the immutability that is intrinsic to a tuple. So, why not opt for \n", + "lists initially if they offer more possibilities? What advantages do different \n", + "complex data types provide, and when should each type be used?\n", + "\n", + "The following suggestions are based on personal experiences and programming styles. \n", + "These recommendations should not be taken as the definitive truth but merely as \n", + "guidance. These recommendations may not be shared universally by other programmers,\n", + "and that's perfectly fine.[2](#fn2)\n", + "\n", + "## Lists should be used for many objects\n", + "\n", + "A list can contain items of different data types, such as `[23, \"abc\", True, (\"a\", \"b\", \"c\")]`. \n", + "However, handling lists with varying data types can be challenging. If all elements \n", + "share the same data type, managing the list becomes easier. Knowing the data type \n", + "of list elements allows for consistent use of functions and methods. For example, \n", + "consider the phone number list from a previous notebook. Iterating through this \n", + "list ensures you're processing phone numbers, typically integers. Limiting lists \n", + "to a single data type can simplify programming.\n", + "\n", + "In the example below, the list `numbers` contains only integers. The `for` loop \n", + "takes advantage of this knowledge and divides each value by 2 using integer division \n", + "`//`. If the list contained other types, like strings or lists, this operation \n", + "would result in an error.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "numbers = [2, 4, 8, 16, 32, 64]\n", + "for i in numbers:\n", + " print(i // 2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Tuples should be used for complex objects\n", + "\n", + "As mentioned in the first notebook of this week, tuples are best suited for complex \n", + "objects requiring multiple attributes. Take students as an example: each student \n", + "might be described by a name, first name, email, student ID, course of study, \n", + "address, etc. These attributes are consistent across all students, making tuples \n", + "ideal for representing each student.\n", + "\n", + "If your program manages multiple students, you can store each student tuple in a \n", + "list. This combination of lists and tuples—a list of tuples—is effective for \n", + "handling multiple complex objects of the same type.\n", + "\n", + "The list-of-tuples structure works well when accessing data from CSV files (CSV \n", + "stands for comma-separated values), a common data format." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "student_1 = (\"Dylan\", \"Bob\", 334455, \"Philosophy\")\n", + "student_2 = (\"Cobain\", \"Kurt\", 987654, \"Mechanical Engineering\")\n", + "student_3 = (\"Winehouse\", \"Amy\", 123321, \"Medicine\")\n", + "list_of_students = [student_1, student_2, student_3]\n", + "\n", + "for s in list_of_students:\n", + " print(s[3])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## If there is an ID, use a dictionary instead of a list\n", + "\n", + "When objects have a unique ID, a dictionary may be more suitable. This allows you \n", + "to access objects directly via their IDs rather than searching through them.\n", + "\n", + "Consider the example of students: each student has a unique student ID. Operations \n", + "with students often rely on their IDs rather than names to avoid confusion—especially \n", + "among students with identical names. Unlike names, student IDs are unique. \n", + "Therefore, for student data, a dictionary of tuples can be advantageous.\n", + "\n", + "A dictionary of tuples is also a good option for data retrieved from relational \n", + "databases. These databases typically consist of records with consistent structures \n", + "and unique IDs. The ID can serve as the dictionary key, and the rest of the record, \n", + "in tuple form, becomes the dictionary value.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "student_1 = (\"Dylan\", \"Bob\", \"Philosophy\")\n", + "student_2 = (\"Cobain\", \"Kurt\", \"Mechanical Engineering\")\n", + "student_3 = (\"Winehouse\", \"Amy\", \"Medicine\")\n", + "\n", + "dict_of_students = {}\n", + "dict_of_students[334455] = student_1\n", + "dict_of_students[987654] = student_2\n", + "dict_of_students[123321] = student_3\n", + "\n", + "for student_ID in dict_of_students:\n", + " print(student_ID, dict_of_students[student_ID][2])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## If complex objects differ in their attributes, use a dictionary instead of the tuple\n", + "\n", + "Unlike tuples, dictionaries provide greater flexibility in describing objects. \n", + "Consider again the example of students: students from different departments may \n", + "take different classes and receive marks in distinct modules. In such scenarios, \n", + "dictionaries can replace tuples. If no ID is present, a list of dictionaries is \n", + "viable; if an ID is available, a dictionary of dictionaries is feasible.\n", + "\n", + "Even more complex combinations are possible. For instance, students typically share \n", + "attributes like name, first name, email, etc., making tuples appropriate. However, \n", + "students enroll in different courses, and their modules and achievements vary. \n", + "Here, a dictionary for these attributes is preferable. A combination of tuples \n", + "and dictionaries is possible: student data is stored in a tuple, while one tuple \n", + "element, containing a student's marks, is a dictionary. You end up with a list \n", + "of tuples, where one element of the tuple is a dictionary. Complex, but effective.\n", + "\n", + "There are many options, but choosing a well-suited data structure often makes \n", + "your life as a programmer easier. So, carefully consider your data structure choices." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s1 = (\"Dylan\", \"Bob\", 334455, \"Philosophy\", {\"Logic\": \"A\", \"Ethics\": \"B\"})\n", + "s2 = (\"Cobain\", \"Kurt\", 987654, \"Mechanical Engineering\", {\"Math\": \"B\"})\n", + "s3 = (\"Winehouse\", \"Amy\", 123321, \"Medicine\", {\"Math\": \"B\", \"Chemistry\": \"C\"})\n", + "\n", + "l_of_students = [s1, s2, s3]\n", + "for s in l_of_students:\n", + " print(s)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exercise 1: List of tuples\n", + "\n", + "In the cell below, a list of students is provided. Each student is defined within \n", + "a tuple. Implement `input()` statements to gather information for a new student. \n", + "Combine the data into a tuple and append the tuple to the `list_of_students`. \n", + "Finally, print the entire list using a `for` loop that iterates over the list.\n", + "\n", + "Example Input:\n", + "```\n", + " Name: Weasley\n", + " Firstname: Ginney\n", + " ...\n", + "```\n", + "\n", + "Example Output:\n", + "```\n", + " (\"Potter\", \"Harry\", 477264, \"harry@hogwarts.wiz\", \"Defence Against the Dark Arts\")\n", + " (\"Weasley\", \"Ron\", 490134, \"ron@hogwarts.wiz\", \"Care of Magical Creatures\")\n", + " ...\n", + "``` " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list_of_students = [\n", + " (\"Potter\", \"Harry\", 477264, \"harry@hogwarts.wiz\", \"Defence Against the Dark Arts\"),\n", + " (\"Weasley\", \"Ron\", 490134, \"ron@hogwarts.wiz\", \"Care of Magical Creatures\"),\n", + " (\"Granger\", \"Hermione\", 471617, \"hermione@hogwarts.wiz\", \"Alchemy\"),\n", + " (\"Creevey\", \"Colin\", 432646, \"colin@hogwarts.wiz\", \"Music\"),\n", + " (\"Finnigan\", \"Seamus\", 481989, \"seamus@hogwarts.wiz\", \"Ancient Studies\"),\n", + " (\"Abbott\", \"Hannah\", 488962, \"hannah@hogwarts.wiz\", \"Apparition\"),\n", + " (\"Parkinson\", \"Pansy\", 482103, \"pansy@hogwarts.wiz\", \"Dark Arts\"),\n", + " (\"Malfoy\", \"Draco\", 492010, \"draco@hogwarts.wiz\", \"Defence Against the Dark Arts\"),\n", + " (\"Thomas\", \"Dean\", 447924, \"dean.thomas@hogwarts.wiz\", \"Divination\"),\n", + "]\n", + "\n", + "# Your code here" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exercise 2: Transform the above list into a dictionary\n", + "\n", + "Convert the `list_of_students` from the previous exercise into a dictionary of \n", + "tuples. Each student remains stored as a `tuple`, but use the third element — the \n", + "ID — as the dictionary key. To do this, create a new tuple for each student that \n", + "includes all original elements except the ID. This new tuple becomes the value, \n", + "while the ID becomes the key in the resulting dictionary." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Your code here" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exercise 3: Transform the dictionary of tuples into a dictionary of dictionaries\n", + "\n", + "Using the result of the previous exercise, iterate over the dictionary with a \n", + "`for` loop. The dictionary value is always a `tuple`. Transform this `tuple` into \n", + "a dictionary. Choose suitable names for each entry." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Your code here" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Footnote\n", + "[1](#fn1-back) Of course using a list instead of a dictionary or vice versa might have severe\n", + "implications on the performance and memory usage of your program. However, a detailed discussion of the advantages and\n", + "disadvantages of the list, tuples and dictionaries is beyond the scope of this course. \n", + "\n", + "[2](#fn2-back) Stephan and Christian had quite a few discussions about these recommendations — and they're still not in complete agreement 😉. However, after learning about [Haskell](https://en.wikipedia.org/wiki/Haskell), Christian now mostly supports Stephan's recommendations." + ] + } + ], + "metadata": { + "file_extension": ".py", + "interpreter": { + "hash": "ac59ebe37160ed0dfa835113d9b8498d9f09ceb179beaac4002f036b9467c963" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.2" + }, + "mimetype": "text/x-python", + "name": "python", + "npconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": 3 + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/open-hpi/week-3/notebooks-week-3/week_3_unit_4_safedict_notebook.ipynb b/open-hpi/week-3/notebooks-week-3/week_3_unit_4_safedict_notebook.ipynb new file mode 100644 index 0000000..e4b0367 --- /dev/null +++ b/open-hpi/week-3/notebooks-week-3/week_3_unit_4_safedict_notebook.ipynb @@ -0,0 +1,133 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Safe dictionary access\n", + "\n", + "When you try to access a key that does not exist in a dictionary, you will\n", + "encounter a `KeyError`. This is demonstrated in the following example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dict = {1234: \"Peter\", 2345: \"Jane\"}\n", + "print(dict[1234])\n", + "print(dict[3456])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Safe access to a dictionary using `in`\n", + "\n", + "To prevent your program from being interrupted by errors, it is a good idea to\n", + "catch these potential errors. Using the keyword `in`, you can check if a key\n", + "exists in the dictionary, and thus avoid the error entirely. The expression\n", + "`key in dict` will return `True` if the key exists in the dictionary, and\n", + "`False` otherwise. You can use this within an `if` statement to handle the\n", + "situation gracefully." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dict_of_students = {\n", + " 477264: (\"Potter\", \"Harry\", \"harry@hogwarts.wiz\", \"Defence Against the Dark Arts\"),\n", + " 490134: (\"Weasley\", \"Ron\", \"ron@hogwarts.wiz\", \"Care of Magical Creatures\"),\n", + " 471617: (\"Granger\", \"Hermione\", \"hermione@hogwarts.wiz\", \"Alchemy\"),\n", + " 432646: (\"Creevey\", \"Colin\", \"colin@hogwarts.wiz\", \"Music\"),\n", + " 481989: (\"Finnigan\", \"Seamus\", \"seamus@hogwarts.wiz\", \"Ancient Studies\"),\n", + " 488962: (\"Abbott\", \"Hannah\", \"hannah@hogwarts.wiz\", \"Apparition\"),\n", + " 482103: (\"Parkinson\", \"Pansy\", \"pansy@hogwarts.wiz\", \"Dark Arts\"),\n", + " 492010: (\"Malfoy\", \"Draco\", \"draco@hogwarts.wiz\", \"Defence Against the Dark Arts\"),\n", + " 447924: (\"Thomas\", \"Dean\", \"dean.thomas@hogwarts.wiz\", \"Divination\"),\n", + "}\n", + "\n", + "matrnr = int(input(\"Please enter matriculation number: \"))\n", + "if matrnr in dict_of_students:\n", + " print(\"The student you are looking for is:\", dict_of_students[matrnr])\n", + "else:\n", + " print(\"A student with this matriculation number does not exist\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Alternative: Using [dict.get()](https://docs.python.org/3/library/stdtypes.html#dict.get) to access values safely\n", + "\n", + "Another way to avoid errors is by using the `get()` method to retrieve the\n", + "value associated with a given key. This method can take two arguments:\n", + "\n", + "`dict.get(key, default)`\n", + "\n", + "The first argument is the `key` you want to retrieve the value for (such as the\n", + "matriculation number in the above example). The second, **optional** argument,\n", + "`default`, specifies the return value if the `key` is not found in the\n", + "dictionary. If you do not provide the `default` argument, the method will \n", + "return `None` by default, and no error will be raised, so your program will \n", + "continue to run smoothly. Below is an example of how to implement this \n", + "technique:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "input_num = int(input(\"Please enter matriculation number:\"))\n", + "student = dict_of_students.get(\n", + " input_num, \"no matching student found for this matriculation number\"\n", + ")\n", + "\n", + "print(\n", + " \"For the matriculation number\",\n", + " input_num,\n", + " \"the dictionary returned the following result:\",\n", + " student,\n", + ")" + ] + } + ], + "metadata": { + "file_extension": ".py", + "interpreter": { + "hash": "ac59ebe37160ed0dfa835113d9b8498d9f09ceb179beaac4002f036b9467c963" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.3" + }, + "mimetype": "text/x-python", + "name": "python", + "npconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": 3 + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/open-hpi/week-3/notebooks-week-3/week_3_unit_5_impfunct_notebook.ipynb b/open-hpi/week-3/notebooks-week-3/week_3_unit_5_impfunct_notebook.ipynb new file mode 100644 index 0000000..4a56feb --- /dev/null +++ b/open-hpi/week-3/notebooks-week-3/week_3_unit_5_impfunct_notebook.ipynb @@ -0,0 +1,116 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Important functions and methods for complex data types\n", + "\n", + "This unit serves as a recap to summarize the key functions and methods for \n", + "[Lists](https://docs.python.org/3/library/stdtypes.html#lists), \n", + "[Tuples](https://docs.python.org/3/library/stdtypes.html#tuples), and \n", + "[Dictionaries](https://docs.python.org/3/library/stdtypes.html#mapping-types-dict).\n", + "\n", + "The tables below provide an overview of various methods and functions, along with \n", + "an indication of whether they are applicable to the data types mentioned above:\n", + "\n", + "| **Method** | **Description** | **List** | **Tuple** | **Dictionary** |\n", + "| :----------------- | :---------------------------------------- | :------: | :-------: | :------------: |\n", + "| `.sort()` | Sorts the object | X | | |\n", + "| `.append(x)` | Appends *x* to the end of the object | X | | |\n", + "| `.pop(i)` | Removes item at index *i* from the object | X | | X |\n", + "| `.insert(i, x)` | Inserts *x* at index *i* | X | | |\n", + "| `.remove(x)` | Removes the first occurrence of *x* | X | | |\n", + "| `.count(x)` | Counts the number of occurrences of *x* | X | X | |\n", + "| `.index(x)` | Returns the index of *x* | X | X | |\n", + "| `.keys()` | Returns all `keys` of the dictionary | | | X |\n", + "| `.values()` | Returns all `values` of the dictionary | | | X |\n", + "| `.items()` | Returns all `items` of the dictionary | | | X |\n", + "\n", + "\n", + "\n", + "| **Function** | **Description** | **List** | **Tuple** | **Dictionary** |\n", + "| :----------------- | :---------------------------------------- | :------: | :-------: | :------------: |\n", + "| `del object[item]` | Deletes *item* from *object* | X | | X |\n", + "| `len(object)` | Returns the length of *object* | X | X | X |\n", + "| `min(object)` | Returns the smallest item in *object* | X | X | [1](#fn1) |\n", + "| `max(object)` | Returns the largest item in *object* | X | X | [1](#fn1) |\n", + "| `sorted(object)` | Returns a sorted list of the items in *object* | X | X | [1](#fn1) |\n", + "\n", + "It should be evident why the first five **methods** apply only to lists — they modify \n", + "the object, and since tuples are immutable, these methods are unavailable for them. \n", + "Regarding dictionaries, sorting is unnecessary because values are accessed using a \n", + "`key`, not an index. Inserting values into a dictionary uses different syntax, and \n", + "removing the first occurrence of a value doesn't make sense because dictionaries \n", + "can have any order, and items are accessed using keys. \n", + "The last set of **methods** is specific to dictionaries, as lists and tuples do not \n", + "have keys and values. Therefore, these methods work only for dictionaries.\n", + "\n", + "The `del` keyword operates only on mutable data types and not on tuples. \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example\n", + "\n", + "The methods mentioned above can be used to identify which letter occurs most often \n", + "in a text. Note that this example is solely to demonstrate the use of these \n", + "methods. A dictionary would provide a more efficient solution." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "text = \"lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\"\n", + "letters = \"abcdefghijklmnopqrstuvwxyz\"\n", + "\n", + "result = []\n", + "for l in letters:\n", + " result.append(text.count(l))\n", + "\n", + "max_count = max(result)\n", + "max_index = result.index(max_count)\n", + "\n", + "print(letters[max_index])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Footnote\n", + "[1](#fn1-back) In general, the last three functions are also applicable to dictionaries. However, \n", + "it is more common to use these functions with the values or keys of a dictionary." + ] + } + ], + "metadata": { + "interpreter": { + "hash": "fb768b9b3ecf961ba38b0c7e836f7f85a23c08c1797458e7c652470f2ae90c9c" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.2" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/open-hpi/week-3/notebooks-week-3/week_3_unit_6_while_notebook.ipynb b/open-hpi/week-3/notebooks-week-3/week_3_unit_6_while_notebook.ipynb new file mode 100644 index 0000000..869b878 --- /dev/null +++ b/open-hpi/week-3/notebooks-week-3/week_3_unit_6_while_notebook.ipynb @@ -0,0 +1,386 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# The [while](https://docs.python.org/3/reference/compound_stmts.html#while) loop\n", + "\n", + "You are already familiar with the `for` loop. There is another loop in Python, the\n", + "`while` loop. The `while` loop offers more flexibility, but can be a bit more\n", + "complex to use.\n", + "\n", + "The `for` loop works well when you have a sequence to iterate over, like a list or\n", + "a range. However, there are scenarios where you need to repeat an operation multiple\n", + "times without a predefined sequence:\n", + "- At an ATM, the system prompts for a PIN until it is entered correctly.\n", + "- A user must enter a value that can be converted into a number.\n", + "- A user should keep answering questions until none remain. In such cases, a `while`\n", + " loop is a better choice than using a `for` loop with \"tricks\".\n", + "\n", + "## Syntax of the `while` loop\n", + "\n", + "The simplest form of a `while` loop is structured as follows:\n", + "\n", + "```python\n", + "while condition:\n", + " statement1\n", + " statement2\n", + " ...\n", + " statementN\n", + "```\n", + "\n", + "The loop begins with the keyword `while`, followed by a condition and a colon, much\n", + "like an `if` statement. The subsequent block of code should be indented, similar to\n", + "other control structures. When the loop's indented code is done executing, the program\n", + "resumes execution with the non-indented code following the loop.\n", + "\n", + "## How a `while` loop works\n", + "\n", + "To start, the loop checks the condition specified after `while`. If the condition is\n", + "`True`, the loop's body is executed. Upon reaching the end of the loop body, the\n", + "program returns to the `while` condition and checks it again. \n", + "The loop continues running as long as the condition remains `True`. Once the condition\n", + "becomes `False`, the loop ends and the program continues with the code following the\n", + "loop. \n", + "If the condition is `False` the first time the `while` loop is encountered, the loop's\n", + "body is skipped entirely and the program continues immediately after the loop.\n", + "\n", + "## Example 1: Using `input()` until a number is entered\n", + "\n", + "Consider this problem: You need to enter a number using `input()`. For calculations,\n", + "the entered string must first be converted to a number, such as an integer using\n", + "`int()`. If the user enters non-numeric input, the conversion will cause an error.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "number = input(\"Please enter a number: \")\n", + "number = int(number)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To avoid this, we can use a `while` loop. The loop's condition checks if the input is\n", + "numeric using the `.isdecimal()` method. Until a valid number is entered, the loop can\n", + "keep requesting input. Once a number is entered, conversion occurs **after** the loop." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# The loop runs until a suitable input is available\n", + "number = \"x\"\n", + "while not (number.isdecimal()):\n", + " number = input(\"Please enter number: \")\n", + "\n", + "number = int(number)\n", + "print(number)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "An *imperfection* in the loop is visible above: To check the condition initially, the\n", + "variable `number` must be declared first but with a value that is not a number.\n", + "Setting `number = \"x\"` is solely to initialize it with something besides an integer.\n", + "\n", + "Alternatively, this variant is possible:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# The loop runs until a suitable input is available\n", + "number = input(\"Please enter number: \")\n", + "while not (number.isdecimal()):\n", + " number = input(\"Please enter number: \")\n", + "\n", + "number = int(number)\n", + "print(number)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Although the statement in line two is less \"meaningless,\" there is now a\n", + "duplicated `input` statements. This is not the most elegant programming style. \n", + "Question: Why can the `int(number)` conversion occur only after the loop?\n", + "\n", + "## Example 2: Check PIN\n", + "\n", + "In this example, we have a secret PIN. The user must enter the correct PIN to proceed\n", + "beyond the `while` loop. The program structure is similar to the previous example.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "secret_pin = 1234\n", + "\n", + "pin = int(input(\"Please enter PIN: \"))\n", + "\n", + "while pin != secret_pin:\n", + " print(\"The PIN was wrong.\")\n", + " pin = int(input(\"Please enter PIN: \"))\n", + "\n", + "print(pin, \"is correct\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In real life, attempts at entering a PIN are usually limited. ATMs typically allow \n", + "only three tries before blocking. How can we represent this in our loop?\n", + "\n", + "We need a condition that checks both if the PIN is correct and if attempts exceed \n", + "three. Here's how this can be implemented:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "secret_pin = 1234\n", + "\n", + "pin = int(input(\"Please enter PIN: \"))\n", + "attempts = 1\n", + "\n", + "while (pin != secret_pin) and (attempts < 3):\n", + " print(\"The PIN was wrong.\")\n", + " pin = int(input(\"Please enter PIN: \"))\n", + " attempts += 1\n", + "\n", + "if pin == secret_pin:\n", + " print(pin, \"is correct\")\n", + "else:\n", + " print(\"You entered the wrong PIN three times, your card will be confiscated.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This program also has *imperfections*. A second variable `attempts` is required, \n", + "initialized before the loop and incremented inside it. After the loop, it's not \n", + "directly clear why it ended — was it a correct PIN or exceeded attempts? An `if` \n", + "statement is needed to clarify this." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 3: Simple counter\n", + "\n", + "In the previous example, a counter (`attempts`) was used, illustrating the basic act \n", + "of incrementing variables. Here's how you can count from 1 to 10 with a `while` loop:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Simple counter\n", + "# The following program is to count from 1 - 10\n", + "i = 1\n", + "while i <= 10:\n", + " print(i)\n", + " i += 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This solution appears straightforward. However, subtle details can \n", + "alter program behavior:\n", + "- Should `i` start at 1 or 0?\n", + "- Should the condition be `i <= 10` or `i < 10`?\n", + "- Should the comparison value be 10 or 11?\n", + "- Should the increment (`i += 1`) occur before or after `print()` in the loop body?\n", + "\n", + "Each small variation changes how the program behaves. Therefore, when applying `while`\n", + "loops, pay attention to boundary conditions. Experiment by modifying the above program\n", + "with these questions in mind and predict its output.\n", + "\n", + "## A Classic error: The infinite loop\n", + "\n", + "A common mistake is an infinite loop: a loop that never stops because its condition \n", + "always remains `True`. For instance, in the previous example, if the increment is \n", + "forgotten, the loop becomes infinite. \n", + "Note: To halt an infinite loop, press the Stop button at the top.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# The following program is to count from 1 - 10\n", + "i = 1\n", + "while i <= 10:\n", + " print(i)\n", + " # Because of the commented out (forgotten) increment, the loop runs endlessly.\n", + " # i += 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Using a `for` loop is simpler and more robust as it automatically checks boundaries. \n", + "Use a `for` loop when possible; it is simpler and thus reduces potential errors.\n", + "\n", + "# Example 4: Guessing a random number\n", + "\n", + "A `while` loop is well-suited for implementing a number guessing game.\n", + "In this game, a random number between 1 and 100 is generated. Unlike the previous PIN example, the secret number \n", + "is unknown to the user. After each guess, the program provides a hint indicating whether the secret number is higher \n", + "or lower than the user's guess. This process continues until the user correctly guesses the number.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "\n", + "secret_number = random.randint(1, 100)\n", + "\n", + "guessed_number = int(input(\"Please guess a number: \"))\n", + "\n", + "while guessed_number != secret_number:\n", + " if guessed_number < secret_number:\n", + " print(\"The number\", guessed_number, \"was too small.\")\n", + " else:\n", + " print(\"The number\", guessed_number, \"was too big.\")\n", + "\n", + " guessed_number = int(input(\"Please guess a number: \"))\n", + "\n", + "print(\"Correct!\", guessed_number, \"was the number you were looking for.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Exiting the loop with a `break`\n", + "\n", + "A while loop can be exited early using the break statement. When break is executed, the loop stops immediately — even if the loop condition is still true. After a break, the condition is no longer checked. Therefore, break is typically used inside an if statement within the loop.\n", + "\n", + "Using break can simplify certain programming tasks. For example, suppose we want to enter data for multiple students, where each student has a matriculation number, last name, and first name. Since we don’t know in advance how many students will be entered, a while loop is a good choice.\n", + "\n", + "We want the loop to stop when the user presses the return key without entering a matriculation number. This can be implemented cleanly using break, as shown below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list_of_students = []\n", + "\n", + "while True:\n", + " matnr = input(\"Enter matriculation number: \")\n", + " if matnr == \"\":\n", + " break\n", + " name = input(\"Enter name: \")\n", + " firstname = input(\"Enter first name: \")\n", + " list_of_students.append((matnr, name, firstname))\n", + "\n", + "print(list_of_students)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`while True:` sets up an infinite loop since `True` is always true. The loop only exits \n", + "via `break`. After the first `input()`, a condition checks whether to exit. If so, \n", + "the `break` ends the loop and prevents further `input()` calls." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exercise: Calculating value growth\n", + "\n", + "The value of a property increases by *p* percent every year. Write a program that calculates the value of the property\n", + "for each year until the value has doubled. Use `input()` to ask for the percentage and the initial value. \n", + "\n", + "Example input:\n", + "```\n", + "What is the property's value? 10000 \n", + "By what percentage does it increase yearly? 5\n", + "```\n", + "\n", + "Example output:\n", + "```\n", + "Year 0 - 10000.0 \n", + "Year 1 - 10500.0 \n", + "Year 2 - 11025.0\n", + "Year 3 - 11576.25\n", + "...\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Your code here" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.2" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/open-hpi/week-3/week-3-unit-1-tuples.pdf b/open-hpi/week-3/week-3-unit-1-tuples.pdf new file mode 100644 index 0000000000000000000000000000000000000000..76f0317dc99a3750c639192721aa29c46ca5fb0c GIT binary patch literal 1142856 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