diff --git a/.gitignore b/.gitignore index ad46379..981b13d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ __pycache__/ .DS_Store .vscode venv +.venv .idea/* diff --git a/README.md b/README.md index c88387e..06929d4 100755 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ In this code repository you find the solutions and sample implementations for the solutions and challenges posed in our [Python Basics](https://realpython.com/products/python-basics-book/) book. All solutions and sample files are ordered by chapter so you can quickly navigate to the code you're looking for. +In most cases, the solutions presented here represent just one way out of many that the exercises and challenges can be solved. If you find a better way to solve one of the exercises or challenges feel free to open an issue or pull request! + ## Get the Book [ยป Click here to learn more about the book and get your copy](https://realpython.com/products/python-basics-book/) @@ -32,4 +34,4 @@ hello hi ``` -> **Note:** Depending on your installation, you may need to type `python3.7` or `python37` to run the examples. +> **Note:** Depending on your installation, you may need to type `python3.9` or `python39` to run the examples. diff --git a/ch04-strings-and-string-methods/2-concatenation-indexing-and-slicing.py b/ch04-strings-and-string-methods/2-concatenation-indexing-and-slicing.py index 08e1050..713d7d5 100644 --- a/ch04-strings-and-string-methods/2-concatenation-indexing-and-slicing.py +++ b/ch04-strings-and-string-methods/2-concatenation-indexing-and-slicing.py @@ -15,13 +15,6 @@ print(string_left + string_right) -# Exercise 3 -# Display two strings together, with a space in between -string_one = "heebie" -string_two = "jeebies" -print(string_one, string_two) - - # Exercise 3 # Display two strings together, with a space in between string_one = "heebie" diff --git a/ch04-strings-and-string-methods/3-manipulate-strings-with-methods.py b/ch04-strings-and-string-methods/3-manipulate-strings-with-methods.py index a37fed6..79b6279 100644 --- a/ch04-strings-and-string-methods/3-manipulate-strings-with-methods.py +++ b/ch04-strings-and-string-methods/3-manipulate-strings-with-methods.py @@ -27,7 +27,7 @@ string3 = " Cheeseburger " print(string1.strip()) # Could also use .lstrip() -print(string1.strip()) # Could also use .rstrip() +print(string2.strip()) # Could also use .rstrip() print(string3.strip()) diff --git a/ch04-strings-and-string-methods/5-challenge.py b/ch04-strings-and-string-methods/5-challenge-pick-apart-your-users-input.py similarity index 75% rename from ch04-strings-and-string-methods/5-challenge.py rename to ch04-strings-and-string-methods/5-challenge-pick-apart-your-users-input.py index c045b1b..2079edd 100644 --- a/ch04-strings-and-string-methods/5-challenge.py +++ b/ch04-strings-and-string-methods/5-challenge-pick-apart-your-users-input.py @@ -6,4 +6,4 @@ user_input = input("Tell me your password: ") first_letter = user_input[0] -print("The first letter you entered was:", first_letter.upper()) +print("The first letter you entered was: " + first_letter.upper()) diff --git a/ch04-strings-and-string-methods/6-working-with-strings-and-numbers.py b/ch04-strings-and-string-methods/6-working-with-strings-and-numbers.py index 10c2a23..9b4ca3a 100644 --- a/ch04-strings-and-string-methods/6-working-with-strings-and-numbers.py +++ b/ch04-strings-and-string-methods/6-working-with-strings-and-numbers.py @@ -28,9 +28,9 @@ # Exercise 4 -# Get two numbers from the user, multiple them, +# Get two numbers from the user, multiply them, # and display the result a = input("Enter a number: ") b = input("Enter another number: ") product = float(a) * float(b) -print("The product of " + a + " and " + b + " is " + str(product)) +print("The product of " + a + " and " + b + " is " + str(product) + ".") diff --git a/ch04-strings-and-string-methods/7-streamline-your-print-statements.py b/ch04-strings-and-string-methods/7-streamline-your-prints.py similarity index 81% rename from ch04-strings-and-string-methods/7-streamline-your-print-statements.py rename to ch04-strings-and-string-methods/7-streamline-your-prints.py index 18c29c4..d89832c 100644 --- a/ch04-strings-and-string-methods/7-streamline-your-print-statements.py +++ b/ch04-strings-and-string-methods/7-streamline-your-prints.py @@ -1,4 +1,4 @@ -# 4.7 - Streamline Your Print Statements +# 4.7 - Streamline Your Prints # Solutions to review exercies @@ -6,7 +6,7 @@ weight = 0.2 animal = "newt" -# Concatenate a number and a string in one print statement +# Concatenate a number and a string in one print call print(str(weight) + " kg is the weight of the " + animal + ".") diff --git a/ch04-strings-and-string-methods/8-find-a-string-in-a-string.py b/ch04-strings-and-string-methods/8-find-a-string-in-a-string.py index 6766c5d..024ba62 100644 --- a/ch04-strings-and-string-methods/8-find-a-string-in-a-string.py +++ b/ch04-strings-and-string-methods/8-find-a-string-in-a-string.py @@ -8,11 +8,13 @@ # Exercise 2 -# Try to find a number inside a string; -# use str() to convert the number first -version = "version 2.0" -v_num = 2.0 -print(version.find(str(v_num))) +# Replace every occurrence of the character `"s"` +# with the character `"x"` +phrase = "Somebody said something to Samantha." +phrase = phrase.replace("s", "x") +print(phrase) +# NOTE: This leaves the capital "S" unchanged, so the +# output will be "Somebody xaid xomething to Samantha." # Exercise 3 diff --git a/ch04-strings-and-string-methods/9-challenge.py b/ch04-strings-and-string-methods/9-challenge-turn-your-user-into-a-l33t-h4x0r.py similarity index 100% rename from ch04-strings-and-string-methods/9-challenge.py rename to ch04-strings-and-string-methods/9-challenge-turn-your-user-into-a-l33t-h4x0r.py diff --git a/ch05-numbers-in-python/2-integers-and-floating-point-numbers.py b/ch05-numbers-in-python/1-integers-and-floating-point-numbers.py similarity index 75% rename from ch05-numbers-in-python/2-integers-and-floating-point-numbers.py rename to ch05-numbers-in-python/1-integers-and-floating-point-numbers.py index c55ad44..832a0fb 100644 --- a/ch05-numbers-in-python/2-integers-and-floating-point-numbers.py +++ b/ch05-numbers-in-python/1-integers-and-floating-point-numbers.py @@ -1,10 +1,10 @@ -# 5.3 - Integers and Floating-Point Numbers +# 5.1 - Integers and Floating-Point Numbers # Solutions to Review Exercises # Exercise 1 num1 = 25_000_000 -num2 = 25_000_000 +num2 = 25000000 print(num1) print(num2) diff --git a/ch05-numbers-in-python/3-challenge.py b/ch05-numbers-in-python/3-challenge-perform-calculations-on-user-input.py similarity index 100% rename from ch05-numbers-in-python/3-challenge.py rename to ch05-numbers-in-python/3-challenge-perform-calculations-on-user-input.py diff --git a/ch05-numbers-in-python/5-math-functions-and-number-methods.py b/ch05-numbers-in-python/5-math-functions-and-number-methods.py index 4302714..22592cb 100644 --- a/ch05-numbers-in-python/5-math-functions-and-number-methods.py +++ b/ch05-numbers-in-python/5-math-functions-and-number-methods.py @@ -16,6 +16,6 @@ num1 = float(input("Enter a number: ")) num2 = float(input("Enter another number: ")) print( - f"The difference between {num1} and {num2} is an integer?" + f"The difference between {num1} and {num2} is an integer? " f"{(num1 - num2).is_integer()}!" ) diff --git a/ch06-functions-and-loops/2-write-your-own-functions.py b/ch06-functions-and-loops/2-write-your-own-functions.py index d21c159..ac29db5 100644 --- a/ch06-functions-and-loops/2-write-your-own-functions.py +++ b/ch06-functions-and-loops/2-write-your-own-functions.py @@ -5,7 +5,7 @@ # Exercise 1 def cube(num): """Return the cube of the input number.""" - cube_num = num ** 3 # Could also use pow(num, 3) + cube_num = num**3 # Could also use pow(num, 3) return cube_num diff --git a/ch06-functions-and-loops/3-challenge.py b/ch06-functions-and-loops/3-challenge-convert-temperatures.py similarity index 100% rename from ch06-functions-and-loops/3-challenge.py rename to ch06-functions-and-loops/3-challenge-convert-temperatures.py diff --git a/ch06-functions-and-loops/5-challenge.py b/ch06-functions-and-loops/5-challenge-track-your-investments.py similarity index 77% rename from ch06-functions-and-loops/5-challenge.py rename to ch06-functions-and-loops/5-challenge-track-your-investments.py index 1ecabaf..0041382 100644 --- a/ch06-functions-and-loops/5-challenge.py +++ b/ch06-functions-and-loops/5-challenge-track-your-investments.py @@ -2,10 +2,11 @@ # Solution to challenge -# Calculate compound interest to track the growth of an investment +# Calculate interest to track the growth of an investment def invest(amount, rate, years): + """Display year on year growth of an initial investment""" for year in range(1, years + 1): amount = amount * (1 + rate) print(f"year {year}: ${amount:,.2f}") diff --git a/ch08-conditional-logic/1-compare-values.py b/ch08-conditional-logic/1-compare-values.py index bf2ddc6..a7f4683 100644 --- a/ch08-conditional-logic/1-compare-values.py +++ b/ch08-conditional-logic/1-compare-values.py @@ -2,6 +2,7 @@ # Solutions to review exercises +# Exercise 1 # Test whether these expressions are True or False print(1 <= 1) @@ -10,3 +11,28 @@ print("good" != "bad") print("good" != "Good") print(123 == "123") + + +# Exercise 2 +# Fill in the blank so that each of the following expressions are True + +# 3 __ 4 +# Any of the following: +3 < 4 +3 <= 4 +3 != 4 + +# 10 __ 5 +# Any of the following: +10 > 5 +10 >= 5 +10 != 5 + +# "jack" __ "jill" +# Any of the following: +"jack" < "jill" +"jack" <= "jill" +"jack" != "jill" + +# 42 __ "42" +42 != "42" diff --git a/ch08-conditional-logic/2-add-some-logic.py b/ch08-conditional-logic/2-add-some-logic.py index 23f6d8e..e7bd650 100644 --- a/ch08-conditional-logic/2-add-some-logic.py +++ b/ch08-conditional-logic/2-add-some-logic.py @@ -1,14 +1,14 @@ # 8.2 - Add Some Logic # Solutions to review exercises -# --- Exercise 1 +# Exercise 1 # Test whether these expressions are True or False print((1 <= 1) and (1 != 1)) print(not (1 != 2)) print(("good" != "bad") or False) print(("good" != "Good") and not (1 == 1)) -# --- Exercise 2 +# Exercise 2 # Add parentheses so that the following expressions all # evaluate to True @@ -18,5 +18,3 @@ print((True and False) == (True and False)) # not True and "A" == "B" print(not (True and "A" == "B")) -# "B" and not "A" != "B" -print(("B" and not "A") != "B") diff --git a/ch08-conditional-logic/3-control-the-flow-of-your-program.py b/ch08-conditional-logic/3-control-the-flow-of-your-program.py index 2f19b72..6e81837 100644 --- a/ch08-conditional-logic/3-control-the-flow-of-your-program.py +++ b/ch08-conditional-logic/3-control-the-flow-of-your-program.py @@ -2,6 +2,7 @@ # Solutions to review exercises +# Exercise 1 # Display whether the length of user input is <, > or = 5 characters my_input = input("Type something: ") @@ -12,3 +13,15 @@ print("Your input is greater than 5 characters long.") else: print("Your input is 5 characters long.") + + +# Exercise 2 +# Number guessing program ("guess" the number 3) + +print("I'm thinking of a number between 1 and 10. Guess which one.") +my_guess = input("Type in your guess: ") + +if my_guess == "3": + print("You win!") +else: + print("You lose.") diff --git a/ch08-conditional-logic/4-challenge.py b/ch08-conditional-logic/4-challenge-find-the-factors-of-a-number.py similarity index 100% rename from ch08-conditional-logic/4-challenge.py rename to ch08-conditional-logic/4-challenge-find-the-factors-of-a-number.py diff --git a/ch08-conditional-logic/5-break-out-of-the-pattern.py b/ch08-conditional-logic/5-break-out-of-the-pattern.py index a7e4b44..662a28c 100644 --- a/ch08-conditional-logic/5-break-out-of-the-pattern.py +++ b/ch08-conditional-logic/5-break-out-of-the-pattern.py @@ -5,8 +5,8 @@ # Exercise 1 # Run in an infinite loop until the user types "q" or "Q" while True: - my_input = input('Type "q" or "Q" to quit: ') - if my_input.upper() == "Q": + user_input = input('Type "q" or "Q" to quit: ') + if user_input.upper() == "Q": break diff --git a/ch08-conditional-logic/6-recover-from-errors.py b/ch08-conditional-logic/6-recover-from-errors.py index 0569c40..250144b 100644 --- a/ch08-conditional-logic/6-recover-from-errors.py +++ b/ch08-conditional-logic/6-recover-from-errors.py @@ -2,13 +2,27 @@ # Solution to review exercises +# Exercise 1 # Ask the user to enter an integer. # Repeat the process if the user hasn't entered an integer. -repeat = True -while repeat: +while True: try: my_input = input("Type an integer: ") print(int(my_input)) - repeat = False + break except ValueError: print("try again") + + +# Exercise 2 +# Print character and specified index in string + +input_string = input("Enter a string: ") + +try: + index = int(input("Enter an integer: ")) + print(input_string[index]) +except ValueError: + print("Invalid number") +except IndexError: + print("Index is out of bounds") diff --git a/ch08-conditional-logic/7-simulate-events-and-calculate-probabilities.py b/ch08-conditional-logic/7-simulate-events-and-calculate-probabilities.py index c1b15f6..d7b939f 100644 --- a/ch08-conditional-logic/7-simulate-events-and-calculate-probabilities.py +++ b/ch08-conditional-logic/7-simulate-events-and-calculate-probabilities.py @@ -6,15 +6,20 @@ # Exercise 1 -# Simulate the toss of a die -print(randint(1, 6)) +# Write a function that simulates the roll of a die. +def roll(): + """Return random integer between 1 and 6""" + return randint(1, 6) # Exercise 2 -# Simulate 10,000 throws of dice and display the average result -trials = 10000 +# Simulate 10,000 rolls of a die and display the average number rolled. +num_rolls = 10_000 total = 0 -for trial in range(0, trials): - total += randint(1, 6) -avg_result = total / trials -print(f"The average result of {trials} throws was {avg_result}") + +for trial in range(num_rolls): + total = total + roll() + +avg_roll = total / num_rolls + +print(f"The average result of {num_rolls} rolls is {avg_roll}") diff --git a/ch08-conditional-logic/8-challenge.py b/ch08-conditional-logic/8-challenge.py deleted file mode 100644 index 9737ff9..0000000 --- a/ch08-conditional-logic/8-challenge.py +++ /dev/null @@ -1,35 +0,0 @@ -# 8.8 - Challenge: Simulate an Election -# Solution to challenge - - -# Simulate the results of an election using a Monte Carlo simulation - -from random import random - -total_A_wins = 0 -total_B_wins = 0 - -trials = 10_000 -for trial in range(0, trials): - A_win = 0 - B_win = 0 - if random() < 0.87: # 1st region - A_win += 1 - else: - B_win += 1 - if random() < 0.65: # 2nd region - A_win += 1 - else: - B_win += 1 - if random() < 0.17: # 3rd region - A_win += 1 - else: - B_win += 1 - # Determine overall election outcome - if A_win > B_win: - total_A_wins += 1 - else: - total_B_wins += 1 - -print(f"Probability A wins: {total_A_wins / trials}") -print(f"Probability B wins: {total_B_wins / trials}") diff --git a/ch08-conditional-logic/8a-challenge-simulate-a-coin-toss-experiment.py b/ch08-conditional-logic/8a-challenge-simulate-a-coin-toss-experiment.py new file mode 100644 index 0000000..04a3cb5 --- /dev/null +++ b/ch08-conditional-logic/8a-challenge-simulate-a-coin-toss-experiment.py @@ -0,0 +1,55 @@ +# 8.8 - Challenge: Simulate a Coin Toss Experiment +# Solution to challenge + + +# Simulate the results of a series of coin tosses and track the results + +# This one is tricky to structure correctly. Try writing out the logic before +# you start coding. Some additional pointers if you're stuck: +# 1. You will need to use a `for` loop over a range of trials. +# 2. For each trial, first you should check the outcome of the first flip. +# 3. Make sure you add the first flip to the total number of flips. +# 4. After the first toss, you'll need another loop to keep flipping while you +# get the same result as the first flip. + +import random + + +def coin_flip(): + """Randomly return 'heads' or 'tails'.""" + if random.randint(0, 1) == 0: + return "heads" + else: + return "tails" + + +flips = 0 +num_trials = 10_000 + +for trial in range(num_trials): + if coin_flip() == "heads": + # Increment the number of flips by 1 + flips = flips + 1 + while coin_flip() == "heads": + # Keep incrementing the total number of flips + # until "tails" is returned by coin_flip() + flips = flips + 1 + # Once coin_flip() return "tails", the loop will exit, + # but we need to add one more to flips to track the + # last flip that generated "tails" + flips = flips + 1 + else: + # coin_flip() returned "tails" on the first flip. + # Increment the number of flips by 1 + flips = flips + 1 + while coin_flip() == "tails": + # Keep incrementing the total number of flips + # until "heads" is returned by coin_flip() + flips = flips + 1 + # Once coin_flip() returns "heads", the loop will exit, + # but we need to add one more to flips to track the + # last flip that generated "heads" + flips = flips + 1 + +avg_flips_per_trial = flips / num_trials +print(f"The average number of flips per trial is {avg_flips_per_trial}.") diff --git a/ch08-conditional-logic/8b-challenge-simulate-a-coin-toss-experiment.py b/ch08-conditional-logic/8b-challenge-simulate-a-coin-toss-experiment.py new file mode 100644 index 0000000..9f3effe --- /dev/null +++ b/ch08-conditional-logic/8b-challenge-simulate-a-coin-toss-experiment.py @@ -0,0 +1,42 @@ +# 8.8 - Challenge: Simulate a Coin Toss Experiement +# Alternative solution to challenge + + +# Simulate the results of a series of coin tosses and track the results + +# This one is tricky to structure correctly. Try writing out the logic before +# you start coding. Some additional pointers if you're stuck: +# 1. You will need to use a `for` loop over a range of trials. +# 2. For each trial, first you should check the outcome of the first flip. +# 3. Make sure you add the first flip to the total number of flips. +# 4. After the first toss, you'll need another loop to keep flipping while you +# get the same result as the first flip. + +import random + + +def coin_flip(): + """Randomly return 'heads' or 'tails'.""" + if random.randint(0, 1) == 0: + return "heads" + else: + return "tails" + + +flips = 0 +num_trials = 10_000 + +for trial in range(num_trials): + # Flip the coin once and increment the flips tally by 1 + first_flip = coin_flip() + flips = flips + 1 + # Continue flipping the coin and updating the tally until + # a different result is returned by coin_flip() + while coin_flip() == first_flip: + flips = flips + 1 + # Increment the flip tally once more to account for the + # final flip with a different result + flips = flips + 1 + +avg_flips_per_trial = flips / num_trials +print(f"The average number of flips per trial is {avg_flips_per_trial}.") diff --git a/ch08-conditional-logic/8c-challenge-simulate-a-coin-toss-experiment.py b/ch08-conditional-logic/8c-challenge-simulate-a-coin-toss-experiment.py new file mode 100644 index 0000000..753e91d --- /dev/null +++ b/ch08-conditional-logic/8c-challenge-simulate-a-coin-toss-experiment.py @@ -0,0 +1,49 @@ +# 8.8 - Challenge: Simulate a Coin Toss Experiement +# Alternative solution to challenge using functions + + +# Simulate the results of a series of coin tosses and track the results + +# This one is tricky to structure correctly. Try writing out the logic before +# you start coding. Some additional pointers if you're stuck: +# 1. You will need to use a `for` loop over a range of trials. +# 2. For each trial, first you should check the outcome of the first flip. +# 3. Make sure you add the first flip to the total number of flips. +# 4. After the first toss, you'll need another loop to keep flipping while you +# get the same result as the first flip. + +import random + + +def single_trial(): + """Simulate repeatedly flipping a coin until both heads and tails are seen.""" + # This function uses random.randint() to simulate a single coin toss. + # randint(0, 1) randomly returns 0 or 1 with equal probability. We can + # use 0 to represent heads and 1 to represent tails. + + # Flip the coin the first time + flip_result = random.randint(0, 1) + # Keep a tally of how many times the coin has been flipped. We've only + # flipped once so the initial count is 1. + flip_count = 1 + + # Continue to flip the coin until randint(0, 1) returns something + # different than the original flip_result + while flip_result == random.randint(0, 1): + flip_count = flip_count + 1 + + # The last step in the loop flipped the coin but didn't update the tally, + # so we need to increase the flip_count by 1 + flip_count = flip_count + 1 + return flip_count + + +def flip_trial_avg(num_trials): + """Calculate the average number of flips per trial over num_trials total trials.""" + total = 0 + for trial in range(num_trials): + total = total + single_trial() + return total / num_trials + + +print(f"The average number of coin flips was {flip_trial_avg(10_000)}") diff --git a/ch08-conditional-logic/9a-challenge-simulate-an-election.py b/ch08-conditional-logic/9a-challenge-simulate-an-election.py new file mode 100644 index 0000000..a9ef523 --- /dev/null +++ b/ch08-conditional-logic/9a-challenge-simulate-an-election.py @@ -0,0 +1,42 @@ +# 8.9 - Challenge: Simulate an Election +# Solution to challenge + + +# Simulate the results of an election using a Monte Carlo simulation + +from random import random + +num_times_A_wins = 0 +num_times_B_wins = 0 + +num_trials = 10_000 +for trial in range(0, num_trials): + votes_for_A = 0 + votes_for_B = 0 + + # Determine who wins the 1st region + if random() < 0.87: + votes_for_A = votes_for_A + 1 + else: + votes_for_B = votes_for_B + 1 + + # Determine who wins the 2nd region + if random() < 0.65: + votes_for_A = votes_for_A + 1 + else: + votes_for_B = votes_for_B + 1 + + # Determine who wins the 3rd region + if random() < 0.17: + votes_for_A = votes_for_A + 1 + else: + votes_for_B = votes_for_B + 1 + + # Determine overall election outcome + if votes_for_A > votes_for_B: + num_times_A_wins = num_times_A_wins + 1 + else: + num_times_B_wins = num_times_B_wins + 1 + +print(f"Probability A wins: {num_times_A_wins / num_trials}") +print(f"Probability B wins: {num_times_B_wins / num_trials}") diff --git a/ch08-conditional-logic/9a-challenge.py b/ch08-conditional-logic/9a-challenge.py deleted file mode 100644 index 6d97ccd..0000000 --- a/ch08-conditional-logic/9a-challenge.py +++ /dev/null @@ -1,31 +0,0 @@ -# 8.9 - Challenge: Simulate a Coin Toss Experiment -# Solution to challenge - - -# Simulate the results of a series of coin tosses and track the results - -# This one is tricky to structure correctly. Try writing out the logic before -# you start coding. Some additional pointers if you're stuck: -# 1. You will need to use a `for` loop over a range of trials. -# 2. For each trial, first you should check the outcome of the first flip. -# 3. Make sure you add the first flip to the total number of flips. -# 4. After the first toss, you'll need another loop to keep flipping while you -# get the same result as the first flip. - -from random import randint - -flips = 0 -trials = 10000 - -for trial in range(0, trials): - flips += 1 # first flip - if randint(0, 1) == 0: # flipped tails on first flip - while randint(0, 1) == 0: # keep flipping tails - flips += 1 - flips += 1 # finally flipped heads - else: # otherwise, flipped heads on first flip - while randint(0, 1) == 1: # keep flipping heads - flips += 1 - flips += 1 # finally flipped tails - -print(flips / trials) diff --git a/ch08-conditional-logic/9b-challenge-simulate-an-election.py b/ch08-conditional-logic/9b-challenge-simulate-an-election.py new file mode 100644 index 0000000..eb45794 --- /dev/null +++ b/ch08-conditional-logic/9b-challenge-simulate-an-election.py @@ -0,0 +1,60 @@ +# 8.9 - Challenge: Simulate an Election +# Alternate solution to challenge + + +# Simulate the results of an election using a Monte Carlo simulation + +from random import random + + +def run_regional_election(chance_A_wins): + """Return the result of a regional election, either "A" or "B". + + The chances of "A" winning are determined by chance_A_wins. + """ + if random() < chance_A_wins: + return "A" + else: + return "B" + + +def run_election(regional_chances): + """Return the result of an election, either "A" or "B". + + regional_chances is a list or tuple of floats representing the + chances that candidate "A" will win in each region. + + For example, run_election([.2, .5, .7]) will run an election with + three regions, where candidate "A" has a 20% chance to win in the + first region, 50% in the second, and 70% in the third. + """ + num_regions_won_by_A = 0 + for chance_A_wins in regional_chances: + if run_regional_election(chance_A_wins) == "A": + num_regions_won_by_A = num_regions_won_by_A + 1 + + # Return the results. Note that the number of regions won by candidate + # "B" is the total number of regions minus the number of regions won by + # candidate "A". The total number of regions is the same as the length + # of the regional_chances list. + num_regions_won_by_B = len(regional_chances) - num_regions_won_by_A + if num_regions_won_by_A > num_regions_won_by_B: + return "A" + else: + return "B" + + +CHANCES_A_WINS_BY_REGION = [0.87, 0.65, 0.17] +NUM_TRIALS = 10_000 + +# Run the Monte-Carlo simulation +num_times_A_wins = 0 +for trial in range(NUM_TRIALS): + if run_election(CHANCES_A_WINS_BY_REGION) == "A": + num_times_A_wins = num_times_A_wins + 1 + +# Display the probabilities that candidate A or candidate B wins the +# election. Note the probability that B wins can be calculated by +# subtracting the probability that A wins from 1. +print(f"Probability A wins: {num_times_A_wins / NUM_TRIALS}") +print(f"Probability B wins: {1 - (num_times_A_wins / NUM_TRIALS)}") diff --git a/ch08-conditional-logic/9b-challenge.py b/ch08-conditional-logic/9b-challenge.py deleted file mode 100644 index 7a9f900..0000000 --- a/ch08-conditional-logic/9b-challenge.py +++ /dev/null @@ -1,28 +0,0 @@ -# 8.9 - Challenge: Simulate a Coin Toss Experiement -# Alternative solution to challenge - - -# Simulate the results of a series of coin tosses and track the results - -# This one is tricky to structure correctly. Try writing out the logic before -# you start coding. Some additional pointers if you're stuck: -# 1. You will need to use a `for` loop over a range of trials. -# 2. For each trial, first you should check the outcome of the first flip. -# 3. Make sure you add the first flip to the total number of flips. -# 4. After the first toss, you'll need another loop to keep flipping while you -# get the same result as the first flip. - -from random import randint - - -trials = 100_000 -flips = 0 - -for trial in range(1, trials): - first_flip = randint(0, 1) - flips += 1 - while randint(0, 1) == first_flip: - flips += 1 - flips += 1 - -print(f"The average number of coin flips was {flips / trials}") diff --git a/ch08-conditional-logic/9c-challenge.py b/ch08-conditional-logic/9c-challenge.py deleted file mode 100644 index 86d708e..0000000 --- a/ch08-conditional-logic/9c-challenge.py +++ /dev/null @@ -1,37 +0,0 @@ -# 8.9 - Challenge: Simulate a Coin Toss Experiement -# Alternative solution to challenge using functions - - -# Simulate the results of a series of coin tosses and track the results - -# This one is tricky to structure correctly. Try writing out the logic before -# you start coding. Some additional pointers if you're stuck: -# 1. You will need to use a `for` loop over a range of trials. -# 2. For each trial, first you should check the outcome of the first flip. -# 3. Make sure you add the first flip to the total number of flips. -# 4. After the first toss, you'll need another loop to keep flipping while you -# get the same result as the first flip. - -from random import randint - - -def single_trial(): - toss = randint(0, 1) - total_flips = 1 - - while toss == randint(0, 1): - total_flips += 1 - toss = randint(0, 1) - - total_flips += 1 - return total_flips - - -def flip_trial_avg(num_trials): - total = 0 - for trial in range(num_trials): - total += single_trial() - return total / num_trials - - -print(f"The average number of coin flips was {flip_trial_avg(10000)}") diff --git a/ch09-lists-and-dictionaries/1-lists-multipurpose-containers.py b/ch09-lists-and-dictionaries/1-lists-multipurpose-containers.py deleted file mode 100644 index e58bcd1..0000000 --- a/ch09-lists-and-dictionaries/1-lists-multipurpose-containers.py +++ /dev/null @@ -1,65 +0,0 @@ -# 9.1 - Lists: Multipurpose Containers -# Solutions to review exercises - - -# Exercise 1 -desserts = ["ice cream", "cookies"] - - -# Exercise 2 -# Sort the desserts list alphabetically and display the sorted list -desserts.sort() -print(desserts) - - -# Exercise 3 -# Display the index value of "ice cream" -print(desserts.index("ice cream")) - - -# Exercise 4 -# Copy the contents of the "desserts" list into a new "food" list -food = desserts[:] - - -# Exercise 5 -# Add some food to the list -food.append("broccoli") -food.append("turnips") - - -# Exercise 6 -# Display the contents of both lists -print(desserts) -print(food) - - -# Exercise 7 -# Take "cookies" out of the "food" list -food.remove("cookies") - - -# Exercise 8 -# Display the first two items in the "food" list -print(food[0:2]) - - -# Exercise 9 -# Create a "breakfast" list full of cookies and display it -my_breakfast = "cookies, cookies, cookies" -breakfast = my_breakfast.split(", ") -print(breakfast) - - -# Exercise 10 -# Define a function that takes a list of numbers, `[2, 4, 8, 16, 32, 64]`, as -# the argument and then outputs only the numbers from the list that fall -# between 1 and 20 (inclusive) -def print_list(list_of_nums): - for num in list_of_nums: - if num >= 2 and num <= 20: - print(num) - - -list_of_numbers = [2, 4, 8, 16, 32, 64] -print_list(list_of_numbers) diff --git a/ch09-lists-and-dictionaries/2-challenge.py b/ch09-lists-and-dictionaries/2-challenge.py deleted file mode 100644 index d5b4695..0000000 --- a/ch09-lists-and-dictionaries/2-challenge.py +++ /dev/null @@ -1,58 +0,0 @@ -# 9.2 - Challenge: List of Lists -# Solution to challenge - - -def enrollment_stats(list_of_universities): - - # Variables - total_students = [] - total_tuition = [] - - # Iterate through lists, adding values - for university in list_of_universities: - total_students.append(university[1]) - total_tuition.append(university[2]) - - # Return variables - return total_students, total_tuition - - -def mean(my_list): - if len(my_list) == 0: - return "The list is empty" - list_sum = 0 - for i in range(len(my_list)): - list_sum += float(my_list[i]) - return int(list_sum / len(my_list)) - - -def median(my_list): - sorts = sorted(my_list) - length = len(sorts) - if not length % 2: - return (sorts[int(length / 2)] + sorts[int(length / 2 - 1)]) / 2.0 - return sorts[int(length / 2)] - - -universities = [ - ["California Institute of Technology", 2175, 37704], - ["Harvard", 19627, 39849], - ["Massachusetts Institute of Technology", 10566, 40732], - ["Princeton", 7802, 37000], - ["Rice", 5879, 35551], - ["Stanford", 19535, 40569], - ["Yale", 11701, 40500], -] - -totals = enrollment_stats(universities) - -print("\n") -print("*****" * 5) -print(f"Total students: {sum(totals[0])}") -print(f"Total tuition: $ {sum(totals[1])}") -print(f"\nStudent mean: {mean(totals[0])}") -print(f"Student median: {median(totals[0])}") -print(f"\nTuition mean: $ {mean(totals[1])}") -print(f"Tuition median: $ {median(totals[1])}") -print("*****" * 5) -print("\n") diff --git a/ch09-lists-and-dictionaries/4-make-permanent-lists.py b/ch09-lists-and-dictionaries/4-make-permanent-lists.py deleted file mode 100644 index 611f73e..0000000 --- a/ch09-lists-and-dictionaries/4-make-permanent-lists.py +++ /dev/null @@ -1,20 +0,0 @@ -# 9.4 - Make Permanent Lists -# Solutions to review exercises - - -# Exercise 1 -# Create a tuple "cardinal_nums" with "first", "second" and "third" -cardinal_nums = ("first", "second", "third") - - -# Exercise 2 -# Display the second object in the tuple -print(cardinal_nums[1]) - - -# Exercise 3 -# unpack the tuple into three string and display them -pos1, pos2, pos3 = cardinal_nums -print(pos1) -print(pos2) -print(pos3) diff --git a/ch09-lists-and-dictionaries/5-store-relationships-in-dictionaries.py b/ch09-lists-and-dictionaries/5-store-relationships-in-dictionaries.py deleted file mode 100644 index c160d46..0000000 --- a/ch09-lists-and-dictionaries/5-store-relationships-in-dictionaries.py +++ /dev/null @@ -1,50 +0,0 @@ -# 9.5 - Store Relationships in Dictionaries -# Solutions to review exercises - - -# Exercise 1 -# Create an empty dictionary -birthdays = {} - - -# Exercise 2 -# Add some key-value pairs to the dictionary -birthdays["Luke Skywalker"] = "5/25/19" -birthdays["Obi-Wan Kenobi"] = "3/11/57" -birthdays["Darth Vader"] = "4/1/41" - - -# Exercise 3 -# Check if "Yoda" and "Darth Vader exist; if not, add them -if "Yoda" not in birthdays: - birthdays["Yoda"] = "unknown" -if "Darth Vader" not in birthdays: - birthdays["Darth Vader"] = "unknown" - -# Bonus points: you could instead loop over a list of names to check -# for name in ["Yoda", "Darth Vader"]: -# if not name in birthdays: -# birthdays[name] = "unknown" - - -# Exercise 4 -# Display the contents of the dictionary, one pair at a time -for name in birthdays: - print(name, birthdays[name]) - - -# Exercise 5 -# Remove "Darth Vader" -del (birthdays["Darth Vader"]) -print(birthdays) - - -# Exercise 6 (Bonus) -# Create dictionary by passing a list to dict() -birthdays = dict( - [ - ("Luke Skywalker", "5/25/19"), - ("Obi-Wan Kenobi", "3/11/57"), - ("Darth Vader", "4/1/41"), - ] -) diff --git a/ch09-lists-and-dictionaries/6-challenge.py b/ch09-lists-and-dictionaries/6-challenge.py deleted file mode 100644 index eb1f47c..0000000 --- a/ch09-lists-and-dictionaries/6-challenge.py +++ /dev/null @@ -1,23 +0,0 @@ -# 9.6 - Challenge: Capital City Loop -# Solution to challenge - - -from capitals import capitals_dict -import random - - -def capital_game(state, capital): - while True: - guess = input(f"What is the capital of '{state}'? ").lower() - if guess == "exit": - print(f"The capital of '{state}' is '{capital}'.") - print("Goodbye") - break - elif guess == (capital).lower(): - print("Correct! Nice job.") - break - - -state = random.choice(list(capitals_dict.keys())) -capital = capitals_dict[state] -capital_game(state, capital) diff --git a/ch09-lists-and-dictionaries/7a-challenge.py b/ch09-lists-and-dictionaries/7a-challenge.py deleted file mode 100644 index 26d275d..0000000 --- a/ch09-lists-and-dictionaries/7a-challenge.py +++ /dev/null @@ -1,21 +0,0 @@ -# 9.7 - Challenge: Cats With Hats -# Solution to challenge - - -def get_cats_with_hats(array_of_cats): - cats_with_hats_on = [] - for num in range(1, 100 + 1): - for cat in range(1, 100 + 1): - if cat % num == 0: - if array_of_cats[cat] is True: - array_of_cats[cat] = False - else: - array_of_cats[cat] = True - for cat in range(1, 100 + 1): - if cats[cat] is True: - cats_with_hats_on.append(cat) - return cats_with_hats_on - - -cats = [False] * (100 + 1) -print(get_cats_with_hats(cats)) diff --git a/ch09-lists-tuples-and-dictionaries/1-tuples-are-immutable-sequences.py b/ch09-lists-tuples-and-dictionaries/1-tuples-are-immutable-sequences.py new file mode 100644 index 0000000..1b0bcf0 --- /dev/null +++ b/ch09-lists-tuples-and-dictionaries/1-tuples-are-immutable-sequences.py @@ -0,0 +1,32 @@ +# 9.1 - Tuples are Immutable Sequences +# Solutions to review exercises + + +# Exercise 1 +# Create a tuple "cardinal_numbers" with "first", "second" and "third" +cardinal_numbers = ("first", "second", "third") + + +# Exercise 2 +# Display the second object in the tuple +print(cardinal_numbers[1]) + + +# Exercise 3 +# Unpack the tuple into three strings and display them +position1, position2, position3 = cardinal_numbers +print(position1) +print(position2) +print(position3) + +# Exercise 4 +# Create a tuple containing the letters of your name from a string +my_name = tuple("David") + +# Exercise 5 +# Check whether or not x is in my_name +print("x" in my_name) + +# Exercise 6 +# Create tuple containing all but the first letter in my_name +print(my_name[1:]) diff --git a/ch09-lists-tuples-and-dictionaries/2-lists-are-mutable-sequences.py b/ch09-lists-tuples-and-dictionaries/2-lists-are-mutable-sequences.py new file mode 100644 index 0000000..6151b2b --- /dev/null +++ b/ch09-lists-tuples-and-dictionaries/2-lists-are-mutable-sequences.py @@ -0,0 +1,48 @@ +# 9.2 - Lists are Mutable Sequences +# Solutions to review exercises + + +# Exercise 1 +# Create a list named food with two elements "rice" and "beans". +food = ["rice", "beans"] + + +# Exercise 2 +# Append the string "broccoli" to the food list using .append() +food.append("broccoli") + + +# Exercise 3 +# Add the strings "bread" and "pizza" to food using .extend() +food.extend(["bread", "pizza"]) + + +# Exercise 4 +# Print the first two items in food using slicing notation +print(food[:2]) + +# NOTE: The following is also acceptable +print(food[0:2]) + + +# Exercise 5 +# Print the last item in food using index notation +print(food[-1]) + + +# Exercise 6 +# Create a list called breakfast from the string "eggs, fruit, orange juice" +breakfast = "eggs, fruit, orange juice".split(", ") + + +# Exercise 7 +# Verify that breakfast has three items using len() +print(len(breakfast) == 3) + + +# Exercise 8 +# Create a new list called `lengths` using a list +# comprehension that contains the lengths of each +# string in the `breakfast` list. +lengths = [len(item) for item in breakfast] +print(lengths) diff --git a/ch09-lists-tuples-and-dictionaries/3-nesting-sorting-and-copying-lists-and-tuples.py b/ch09-lists-tuples-and-dictionaries/3-nesting-sorting-and-copying-lists-and-tuples.py new file mode 100644 index 0000000..a7604b3 --- /dev/null +++ b/ch09-lists-tuples-and-dictionaries/3-nesting-sorting-and-copying-lists-and-tuples.py @@ -0,0 +1,31 @@ +# 9.3 - Nesting, Copying, and Sorting Lists and Tuples +# Solutions to review exercises + + +# Exercise 1 +# Create a tuple called data with two values, (1, 2) and (3, 4) +data = ((1, 2), (3, 4)) + + +# Exercise 2 +# Loop over data and print the sum of each nested tuple +index = 1 +for row in data: + print(f"Row {index} sum: {sum(row)}") + index += 1 + + +# Exercise 3 +# Create the list [4, 3, 2, 1] and assign it to variable numbers +numbers = [4, 3, 2, 1] + + +# Exercise 4 +# Create a copy of the number list using [:] +numbers_copy = numbers[:] + + +# Exercise 5 +# Sort the numbers list in numerical order +numbers.sort() +print(numbers) diff --git a/ch09-lists-tuples-and-dictionaries/4-challenge-list-of-lists.py b/ch09-lists-tuples-and-dictionaries/4-challenge-list-of-lists.py new file mode 100644 index 0000000..f723245 --- /dev/null +++ b/ch09-lists-tuples-and-dictionaries/4-challenge-list-of-lists.py @@ -0,0 +1,64 @@ +# 9.4 - Challenge: List of Lists +# Solution to challenge + + +def enrollment_stats(list_of_universities): + # Variables + total_students = [] + total_tuition = [] + + # Iterate through lists, adding values + for university in list_of_universities: + total_students.append(university[1]) + total_tuition.append(university[2]) + + # Return variables + return total_students, total_tuition + + +def mean(values): + """Return the mean value in the list `values`""" + return sum(values) / len(values) + + +def median(values): + """Return the median value of the list `values`""" + values.sort() + # If the number of values is odd, + # return the middle value of the list + if len(values) % 2 == 1: + # The value at the center of the list is the value + # at whose index is half of the length of the list, + # rounded down + center_index = int(len(values) / 2) + return values[center_index] + # Otherwise, if the length of the list is even, return + # the mean of the two center values + else: + left_center_index = (len(values) - 1) // 2 + right_center_index = (len(values) + 1) // 2 + return mean([values[left_center_index], values[right_center_index]]) + + +universities = [ + ["California Institute of Technology", 2175, 37704], + ["Harvard", 19627, 39849], + ["Massachusetts Institute of Technology", 10566, 40732], + ["Princeton", 7802, 37000], + ["Rice", 5879, 35551], + ["Stanford", 19535, 40569], + ["Yale", 11701, 40500], +] + +totals = enrollment_stats(universities) + +print("\n") +print("*****" * 6) +print(f"Total students: {sum(totals[0]):,}") +print(f"Total tuition: $ {sum(totals[1]):,}") +print(f"\nStudent mean: {mean(totals[0]):,.2f}") +print(f"Student median: {median(totals[0]):,}") +print(f"\nTuition mean: $ {mean(totals[1]):,.2f}") +print(f"Tuition median: $ {median(totals[1]):,}") +print("*****" * 6) +print("\n") diff --git a/ch09-lists-and-dictionaries/3-challenge.py b/ch09-lists-tuples-and-dictionaries/5-challenge-wax-poetic.py similarity index 55% rename from ch09-lists-and-dictionaries/3-challenge.py rename to ch09-lists-tuples-and-dictionaries/5-challenge-wax-poetic.py index 6288020..7b9ed8a 100644 --- a/ch09-lists-and-dictionaries/3-challenge.py +++ b/ch09-lists-tuples-and-dictionaries/5-challenge-wax-poetic.py @@ -1,10 +1,10 @@ -# 9.3 - Challenge: Wax Poetic +# 9.5 - Challenge: Wax Poetic # Solution to challenge # Generate a random poem based on a set structure -from random import choice +import random noun = [ "fossil", @@ -57,54 +57,57 @@ def make_poem(): """Create a randomly generated poem, returned as a multi-line string.""" # Pull three nouns randomly - n1 = choice(noun) - n2 = choice(noun) - n3 = choice(noun) + n1 = random.choice(noun) + n2 = random.choice(noun) + n3 = random.choice(noun) # Make sure that all the nouns are different while n1 == n2: - n2 = choice(noun) + n2 = random.choice(noun) while n1 == n3 or n2 == n3: - n3 = choice(noun) + n3 = random.choice(noun) # Pull three different verbs - v1 = choice(verb) - v2 = choice(verb) - v3 = choice(verb) + v1 = random.choice(verb) + v2 = random.choice(verb) + v3 = random.choice(verb) while v1 == v2: - v2 = choice(verb) + v2 = random.choice(verb) while v1 == v3 or v2 == v3: - v3 = choice(verb) + v3 = random.choice(verb) # Pull three different adjectives - adj1 = choice(adjective) - adj2 = choice(adjective) - adj3 = choice(adjective) + adj1 = random.choice(adjective) + adj2 = random.choice(adjective) + adj3 = random.choice(adjective) while adj1 == adj2: - adj2 = choice(adjective) + adj2 = random.choice(adjective) while adj1 == adj3 or adj2 == adj3: - adj3 = choice(adjective) + adj3 = random.choice(adjective) # Pull two different prepositions - prep1 = choice(preposition) - prep2 = choice(preposition) + prep1 = random.choice(preposition) + prep2 = random.choice(preposition) while prep1 == prep2: - prep2 = choice(preposition) + prep2 = random.choice(preposition) # Pull one adverb - adv1 = choice(adverb) + adv1 = random.choice(adverb) - if "aeiou".find(adj1[0]) != -1: # first letter is a vowel + if "aeiou".find(adj1[0]) != -1: # First letter is a vowel article = "An" else: article = "A" - # add lines to poem - poem = f"{article} {adj1} {n1}\n\n" - poem = poem + f"{article} {adj1} {n1} {v1} {prep1} the {adj2} {n2}\n" - poem = poem + f"{adv1}, the {n1} {v2}\n" - poem = poem + f"the {n2} {v3} {prep2} a {adj3} {n3}" + # Create the poem + poem = ( + f"{article} {adj1} {n1}\n\n" + f"{article} {adj1} {n1} {v1} {prep1} the {adj2} {n2}\n" + f"{adv1}, the {n1} {v2}\n" + f"the {n2} {v3} {prep2} a {adj3} {n3}" + ) return poem -print(make_poem()) +poem = make_poem() +print(poem) diff --git a/ch09-lists-tuples-and-dictionaries/6-store-relationships-in-dictionaries.py b/ch09-lists-tuples-and-dictionaries/6-store-relationships-in-dictionaries.py new file mode 100644 index 0000000..2d4509e --- /dev/null +++ b/ch09-lists-tuples-and-dictionaries/6-store-relationships-in-dictionaries.py @@ -0,0 +1,49 @@ +# 9.6 - Store Relationships in Dictionaries +# Solutions to review exercises + + +# Exercise 1 +# Create an empty dictionary +captains = {} + + +# Exercise 2 +# Add some key-value pairs to the dictionary +captains["Enterprise"] = "Picard" +captains["Voyager"] = "Janeway" +captains["Defiant"] = "Sisko" + + +# Exercise 3 +# Check if "Enterprise" and "Discovery" exist; if not, add them +if "Enterprise" not in captains: + captains["Enterprise"] = "unknown" +if "Discovery" not in captains: + captains["Discovery"] = "unknown" + +# Bonus points: you could instead loop over a list of names to check +# for ship in ["Enterprise", "Discovery"]: +# if not ship in captains: +# captains[ship] = "unknown" + + +# Exercise 4 +# Display the contents of the dictionary, one pair at a time +for ship, captain in captains.items(): + print(f"The {ship} is captained by {captain}.") + + +# Exercise 5 +# Remove "Discovery" +del captains["Discovery"] + + +# Exercise 6 (Bonus) +# Create dictionary by passing a list to dict() +captains = dict( + [ + ("Enterprise", "Picard"), + ("Voyager", "Janeway"), + ("Defiant", "Sisko"), + ] +) diff --git a/ch09-lists-and-dictionaries/capitals.py b/ch09-lists-tuples-and-dictionaries/7-challenge-capital-city-loop.py similarity index 71% rename from ch09-lists-and-dictionaries/capitals.py rename to ch09-lists-tuples-and-dictionaries/7-challenge-capital-city-loop.py index b153fd7..8eca70b 100644 --- a/ch09-lists-and-dictionaries/capitals.py +++ b/ch09-lists-tuples-and-dictionaries/7-challenge-capital-city-loop.py @@ -1,6 +1,7 @@ -# 10.6 - Challenge: Capital City Loop -# This is the capitals.py module to be used in the solution to the challenge. +# 9.7 - Challenge: Capital City Loop +# Solution to challenge +import random capitals_dict = { "Alabama": "Montgomery", @@ -54,3 +55,18 @@ "Wisconsin": "Madison", "Wyoming": "Cheyenne", } + +# Pull random state and capital pair from the dict by casting to list of tuples +state, capital = random.choice(list(capitals_dict.items())) + +# Game loop continues until the user inputs "exit" +# or guesses the correct capital +while True: + guess = input(f"What is the capital of '{state}'? ").lower() + if guess == "exit": + print(f"The capital of '{state}' is '{capital}'.") + print("Goodbye") + break + elif guess == capital.lower(): + print("Correct! Nice job.") + break diff --git a/ch09-lists-tuples-and-dictionaries/9a-challenge-cats-with-hats.py b/ch09-lists-tuples-and-dictionaries/9a-challenge-cats-with-hats.py new file mode 100644 index 0000000..74cb1ed --- /dev/null +++ b/ch09-lists-tuples-and-dictionaries/9a-challenge-cats-with-hats.py @@ -0,0 +1,33 @@ +# 9.9 - Challenge: Cats With Hats +# Solution to challenge + + +def get_cats_with_hats(array_of_cats): + cats_with_hats_on = [] + # We want to walk around the circle 100 times + for num in range(1, 100 + 1): + # Each time we walk around, we visit 100 cats + for cat in range(1, 100 + 1): + # Determine whether to visit the cat + # Use modulo operator to visit every 2nd, 3rd, 4th,... etc. + if cat % num == 0: + # Remove or add hat depending on + # whether the cat already has one + if array_of_cats[cat] is True: + array_of_cats[cat] = False + else: + array_of_cats[cat] = True + + # Add all number of each cat with a hat to list + for cat in range(1, 100 + 1): + if array_of_cats[cat] is True: + cats_with_hats_on.append(cat) + + # Return the resulting list + return cats_with_hats_on + + +# Cats contains whether each cat already has a hat on, +# by default all are set to false since none have been visited +cats = [False] * (100 + 1) +print(get_cats_with_hats(cats)) diff --git a/ch09-lists-and-dictionaries/7b-challenge.py b/ch09-lists-tuples-and-dictionaries/9b-challenge-cats-with-hats.py similarity index 93% rename from ch09-lists-and-dictionaries/7b-challenge.py rename to ch09-lists-tuples-and-dictionaries/9b-challenge-cats-with-hats.py index 64cd1be..fad7176 100644 --- a/ch09-lists-and-dictionaries/7b-challenge.py +++ b/ch09-lists-tuples-and-dictionaries/9b-challenge-cats-with-hats.py @@ -1,4 +1,4 @@ -# 9.7 - Challenge: Cats With Hats +# 9.9 - Challenge: Cats With Hats # Alternative solution to challenge diff --git a/ch09-lists-and-dictionaries/7c-challenge.py b/ch09-lists-tuples-and-dictionaries/9c-challenge-cats-with-hats.py similarity index 60% rename from ch09-lists-and-dictionaries/7c-challenge.py rename to ch09-lists-tuples-and-dictionaries/9c-challenge-cats-with-hats.py index bcf902e..1e4c885 100644 --- a/ch09-lists-and-dictionaries/7c-challenge.py +++ b/ch09-lists-tuples-and-dictionaries/9c-challenge-cats-with-hats.py @@ -1,20 +1,27 @@ -# 9.7 - Challenge: Cats With Hats +# 9.9 - Challenge: Cats With Hats # Alternative solution to challenge using dictionaries theCats = {} +# By default, no cats have been visited +# so we set every cat's number to False for i in range(1, 101): theCats[i] = False +# Walk around the circle 100 times for i in range(1, 101): + # Visit all cats each time we do a lap for cats, hats in theCats.items(): + # Determine whether or not we visit a cat if cats % i == 0: + # Add or remove the hat if theCats[cats]: theCats[cats] = False else: theCats[cats] = True +# Print whether each cat has a hat for cats, hats in theCats.items(): if theCats[cats]: print(f"Cat {cats} has a hat.") diff --git a/ch10-primer-on-oop/3-inherit-from-other-classes.py b/ch10-primer-on-oop/3-inherit-from-other-classes.py index 12de3d9..36efe49 100644 --- a/ch10-primer-on-oop/3-inherit-from-other-classes.py +++ b/ch10-primer-on-oop/3-inherit-from-other-classes.py @@ -25,18 +25,23 @@ def speak(self, sound="Bark"): # Exercise 2 -# Modified Dog class -class Dog: - species = "Canis familiaris" +# Rectangle and Square classes +class Rectangle: + def __init__(self, length, width): + self.length = length + self.width = width - def __init__(self, name, age): - self.name = name - self.age = age + def area(self): + return self.length * self.width - def __str__(self): - return f"{self.name} is {self.age} years old" - def speak(self, sound): - if not isinstance(sound, str): - raise TypeError("sound should be a string") - return f"{self.name} says {sound}" +class Square(Rectangle): + def __init__(self, side_length): + super().__init__(side_length, side_length) + + +square = Square(4) +print(square.area()) # 16 + +square.width = 5 # Modifies .width but not .length +print(square.area()) # 20 diff --git a/ch10-primer-on-oop/4-challenge.py b/ch10-primer-on-oop/4-challenge-model-a-farm.py similarity index 100% rename from ch10-primer-on-oop/4-challenge.py rename to ch10-primer-on-oop/4-challenge-model-a-farm.py diff --git a/ch11-file-input-and-output/1-read-and-write-simple-files.py b/ch11-file-input-and-output/1-read-and-write-simple-files.py deleted file mode 100644 index ddfe566..0000000 --- a/ch11-file-input-and-output/1-read-and-write-simple-files.py +++ /dev/null @@ -1,45 +0,0 @@ -# 11.1 - Read and Write Simple Files -# Solutions to review exercises - - -""" -In order to run correctly, this script first needs to -be placed in the Chapter 10 "practice_files" folder. -(File paths are covered in section 10.2) -""" - -# Read a text file by looping over individual lines -my_poem = open("poem.txt", "r") -for line in my_poem.readlines(): - # Replace automatic line break at end of line; - # file already contains newline characters - print(line, end="") -my_poem.close() - -# Print some blank lines to separate the two examples -print("\n\n") - -# Use "with" to automatically close a file when finished -with open("poem.txt", "r") as my_poem: - for line in my_poem.readlines(): - print(line, end="") - -# Write the contents of one file into another, line-by-line -poem_in = open("poem.txt", "r") -poem_out = open("output.txt", "w") -for line in poem_in.readlines(): - poem_out.write(line) -poem_in.close() -poem_out.close() - -# Repeat the previous exercise using the "with" keyword -# (This will overwrite the previous output file.) -with open("poem.txt", "r") as poem_in, open("output.txt", "w") as poem_out: - for line in poem_in.readlines(): - poem_out.write(line) - - -# Append a new line to the end of "output.txt" -# (Need to start on a new line, so add "\n" to the beginning.) -with open("output.txt", "a") as poem_append: - poem_append.write("\nThus ends the haiku.") diff --git a/ch11-file-input-and-output/2-working-with-paths-in-python.py b/ch11-file-input-and-output/2-working-with-paths-in-python.py deleted file mode 100644 index 5e6fdb7..0000000 --- a/ch11-file-input-and-output/2-working-with-paths-in-python.py +++ /dev/null @@ -1,53 +0,0 @@ -# 11.2 - Working With Paths in Python -# Solutions to review exercises - - -# Initial setup -import os -import glob - -# This path may need to be changed depending on your setup -path = "C:/Real Python/python-basics-exercises/ch11-file-input-and-output\ -/practice_files/images" - - -# Exercise 1 -# Display the full paths of all files and folders in the main "images" folder -print('Full contents of "images" folder:') -for file_name in os.listdir(path): - print(os.path.join(path, file_name)) - - -# Exercise 2 -# Display the full paths of any `*.png` files in the "images" folder -file_matches = os.path.join(path, "*.png") -print('All PNG files in "images" folder:') -for file_name in glob.glob(file_matches): - print(file_name) - - -# Exercise 3 -# Rename all `*.png` files in the "images" folder and its subfolders -# to `*_backup.png` -for current_folder, subfolders, file_names in os.walk(path): - for file_name in file_names: - file_path = os.path.join(current_folder, file_name) - if file_path.lower().endswith(".png"): - new_path = file_path[-4] + "_backup.png" - os.rename(file_path, new_path) - - -# Exercsie 4 -# Check that the two files have been converted to JPGs successfully -print(os.path.exists(os.path.join(path, "png file - not a gif_backup.png"))) -print( - os.path.exists( - os.path.join(path, "additional files/one last image_backup.png") - ) -) - - -# Exercise 5 -os.mkdir("Output") -with open("Output/python.txt", "w") as out_file: - out_file.write("I was put here by Python!") diff --git a/ch11-file-input-and-output/3-challenge.py b/ch11-file-input-and-output/3-challenge.py deleted file mode 100644 index 815a1c1..0000000 --- a/ch11-file-input-and-output/3-challenge.py +++ /dev/null @@ -1,21 +0,0 @@ -# 11.3 Challenge: Use Pattern Matching to Delete Files -# Solution to challenge - - -# Remove JPG files from multiple folders based on file size - -import os - -path = "C:/Real Python/python-basics-exercises/ch11-file-input-and-output\ -/practice_files/little pics" - -for current_folder, subfolders, file_names in os.walk(path): - for file_name in file_names: - full_path = os.path.join(current_folder, file_name) - # check if file is a JPG - check_JPG = file_name.lower().endswith(".jpg") - # check if size is less than 2Kb - check_size = os.path.getsize(full_path) < 2000 - if check_JPG and check_size: # both conditions must be True - print(f'Deleting "{file_name}"...') - os.remove(full_path) diff --git a/ch11-file-input-and-output/4-read-and-write-csv-data.py b/ch11-file-input-and-output/4-read-and-write-csv-data.py deleted file mode 100644 index 663e563..0000000 --- a/ch11-file-input-and-output/4-read-and-write-csv-data.py +++ /dev/null @@ -1,33 +0,0 @@ -# 11.4 - Read and Write CSV Data -# Solutions to review exercises - - -# Initial setup -import os -import csv - -# This path may need to be changed depending on your setup -path = "C:/Real Python/python-basics-exercises/\ -/ch11-file-input-and-output/practice_files" - -# Read in a CSV and display each row except the header row -# Append a third column and write out the resulting CSV with a new header -in_file_path = os.path.join(path, "pastimes.csv") -out_file_path = os.path.join(path, "Output/categorized pastimes.csv") -with open(in_file_path, "r") as in_file, open(out_file_path, "w") as out_file: - csv_reader = csv.reader(in_file) - csv_writer = csv.writer(out_file) - - # skip header row and write a new output header row - next(csv_reader) - csv_writer.writerow(["Name", "Favorite Pastime", "Type of pastime"]) - - for row in csv_reader: - print(row) - # Check if "Favorite Pastime" includes "fighting" - if row[1].lower().find("fighting") != -1: - row.append("Combat") - else: - row.append("Other") - # Add the new row to the output - csv_writer.writerow(row) diff --git a/ch11-file-input-and-output/5-challenge.py b/ch11-file-input-and-output/5-challenge.py deleted file mode 100644 index be2aace..0000000 --- a/ch11-file-input-and-output/5-challenge.py +++ /dev/null @@ -1,26 +0,0 @@ -# 11.5 - Challenge: Create a High Scores List From CSV Data -# Solution to challenge - - -# Read in CSV data containing names and scores; display a high score list - -import csv -import os - -# Change my_path to the correct path on your system -path = "C:/Real Python/python-basics-exercises/\ -/ch11-file-input-and-output/practice_files" - -high_scores_dict = {} -with open(os.path.join(path, "scores.csv"), "r") as myFile: - my_file_reader = csv.reader(myFile) - for name, score in my_file_reader: # get each name/score pair - score = int(score) # convert string score to integer - if name in high_scores_dict: # already had an entry for that name - if score > high_scores_dict[name]: # new score is higher - high_scores_dict[name] = score - else: # haven't seen this name yet; add it to dictionary - high_scores_dict[name] = score - -for name in sorted(high_scores_dict): - print(name, high_scores_dict[name]) diff --git a/ch11-file-input-and-output/6-challenge.py b/ch11-file-input-and-output/6-challenge.py deleted file mode 100644 index c040899..0000000 --- a/ch11-file-input-and-output/6-challenge.py +++ /dev/null @@ -1,148 +0,0 @@ -# 11.6 - Challenge: Split a CSV File -# Solution to challenge - -import sys -import os -import csv -import argparse - -""" - -Splits a CSV file into multiple pieces based on command line arguments. - - Arguments: - - `-h`: help file of usage of the script - `-i`: input file name - `-o`: output file name - `-r`: row limit to split - - Default settings: - - `output_path` is the current directory - headers are displayed on each split file - the default delimeter is a comma - - Example usage: - - ``` - # split csv by every 100 rows - >> python csv_split.py -i input.csv -o output -r 100 - ``` - -""" - - -def get_arguments(): - """Grab user supplied arguments using the argparse library.""" - - # Use arparse to get command line arguments - parser = argparse.ArgumentParser() - parser.add_argument( - "-i", - "--input_file", - required=True, - help="csv input file (with extension)", - type=str, - ) - parser.add_argument( - "-o", - "--output_file", - required=True, - help="csv output file (without extension)", - type=str, - ) - parser.add_argument( - "-r", - "--row_limit", - required=True, - help="row limit to split csv at", - type=int, - ) - args = parser.parse_args() - - # Check if the input_file exits - is_valid_file(parser, args.input_file) - - # Check if the input_file is valid - is_valid_csv(parser, args.input_file, args.row_limit) - - return args.input_file, args.output_file, args.row_limit - - -def is_valid_file(parser, file_name): - """Ensure that the input_file exists.""" - if not os.path.exists(file_name): - parser.error(f"The file '{file_name}' does not exist!") - sys.exit(1) - - -def is_valid_csv(parser, file_name, row_limit): - """ - Ensure that the # of rows in the input_file - is greater than the row_limit. - """ - row_count = 0 - for row in csv.reader(open(file_name)): - row_count += 1 - # Note: You could also use a generator expression - # and the sum() function to count the rows: - # row_count = sum(1 for row in csv.reader(open(file_name))) - if row_limit > row_count: - parser.error( - f"The 'row_count' of '{row_limit}' is > the number of rows in '{file_name}'!" - ) - sys.exit(1) - - -def parse_file(arguments): - """ - Splits the CSV into multiple files or chunks based on the row_limit. - Then create new CSV files. - """ - input_file = arguments[0] - output_file = arguments[1] - row_limit = arguments[2] - output_path = "." # Current directory - - # Read CSV, split into list of lists - with open(input_file, "r") as input_csv: - datareader = csv.reader(input_csv) - all_rows = [] - for row in datareader: - all_rows.append(row) - - # Remove header - header = all_rows.pop(0) - - # Split list of list into chunks - current_chunk = 1 - # Loop through list - for i in range(0, len(all_rows), row_limit): - # Create single chunk - chunk = all_rows[i : i + row_limit] - # Create new output file - current_output = os.path.join( - output_path, f"{output_file}-{current_chunk}.csv" - ) - - # Add header - chunk.insert(0, header) - - # Write chunk to output file - with open(current_output, "w") as output_csv: - writer = csv.writer(output_csv) - writer = writer.writerows(chunk) - - # Output info - print() - print(f"Chunk # {current_chunk}:") - print(f"Filepath: {current_output}") - print(f"# of rows: {len(chunk)}") - - # Create new chunk - current_chunk += 1 - - -arguments = get_arguments() -parse_file(arguments) diff --git a/ch11-file-input-and-output/practice_files/backup/images/an image file.gif b/ch11-file-input-and-output/practice_files/backup/images/an image file.gif deleted file mode 100644 index 3e09a6b..0000000 Binary files a/ch11-file-input-and-output/practice_files/backup/images/an image file.gif and /dev/null differ diff --git a/ch11-file-input-and-output/practice_files/backup/images/another image.gif b/ch11-file-input-and-output/practice_files/backup/images/another image.gif deleted file mode 100644 index 59ff190..0000000 Binary files a/ch11-file-input-and-output/practice_files/backup/images/another image.gif and /dev/null differ diff --git a/ch11-file-input-and-output/practice_files/backup/little pics/save me please.jpg b/ch11-file-input-and-output/practice_files/backup/little pics/save me please.jpg deleted file mode 100644 index 27cd846..0000000 Binary files a/ch11-file-input-and-output/practice_files/backup/little pics/save me please.jpg and /dev/null differ diff --git a/ch11-file-input-and-output/practice_files/backup/little pics/to be deleted.jpg b/ch11-file-input-and-output/practice_files/backup/little pics/to be deleted.jpg deleted file mode 100644 index 83cf5de..0000000 Binary files a/ch11-file-input-and-output/practice_files/backup/little pics/to be deleted.jpg and /dev/null differ diff --git a/ch11-file-input-and-output/practice_files/example.txt b/ch11-file-input-and-output/practice_files/example.txt deleted file mode 100644 index 9bad3f4..0000000 --- a/ch11-file-input-and-output/practice_files/example.txt +++ /dev/null @@ -1,3 +0,0 @@ -Hi there. -This is a simple text file. -If you can read me, congratulations! \ No newline at end of file diff --git a/ch11-file-input-and-output/practice_files/images/additional files/one last image.png b/ch11-file-input-and-output/practice_files/images/additional files/one last image.png deleted file mode 100644 index 65a6b04..0000000 Binary files a/ch11-file-input-and-output/practice_files/images/additional files/one last image.png and /dev/null differ diff --git a/ch11-file-input-and-output/practice_files/images/an image file.gif b/ch11-file-input-and-output/practice_files/images/an image file.gif deleted file mode 100644 index 3e09a6b..0000000 Binary files a/ch11-file-input-and-output/practice_files/images/an image file.gif and /dev/null differ diff --git a/ch11-file-input-and-output/practice_files/images/another image.gif b/ch11-file-input-and-output/practice_files/images/another image.gif deleted file mode 100644 index 59ff190..0000000 Binary files a/ch11-file-input-and-output/practice_files/images/another image.gif and /dev/null differ diff --git a/ch11-file-input-and-output/practice_files/images/one more image.gif b/ch11-file-input-and-output/practice_files/images/one more image.gif deleted file mode 100644 index 84dcfe3..0000000 Binary files a/ch11-file-input-and-output/practice_files/images/one more image.gif and /dev/null differ diff --git a/ch11-file-input-and-output/practice_files/images/png file - not a gif.png b/ch11-file-input-and-output/practice_files/images/png file - not a gif.png deleted file mode 100644 index 65a6b04..0000000 Binary files a/ch11-file-input-and-output/practice_files/images/png file - not a gif.png and /dev/null differ diff --git a/ch11-file-input-and-output/practice_files/little pics/better not delete me.txt b/ch11-file-input-and-output/practice_files/little pics/better not delete me.txt deleted file mode 100644 index 02337f2..0000000 --- a/ch11-file-input-and-output/practice_files/little pics/better not delete me.txt +++ /dev/null @@ -1 +0,0 @@ -hi mom \ No newline at end of file diff --git a/ch11-file-input-and-output/practice_files/little pics/look in here too/definitely has to go.jpg b/ch11-file-input-and-output/practice_files/little pics/look in here too/definitely has to go.jpg deleted file mode 100644 index 7fcde1c..0000000 Binary files a/ch11-file-input-and-output/practice_files/little pics/look in here too/definitely has to go.jpg and /dev/null differ diff --git a/ch11-file-input-and-output/practice_files/little pics/save me please.jpg b/ch11-file-input-and-output/practice_files/little pics/save me please.jpg deleted file mode 100644 index 27cd846..0000000 Binary files a/ch11-file-input-and-output/practice_files/little pics/save me please.jpg and /dev/null differ diff --git a/ch11-file-input-and-output/practice_files/little pics/to be deleted.jpg b/ch11-file-input-and-output/practice_files/little pics/to be deleted.jpg deleted file mode 100644 index 83cf5de..0000000 Binary files a/ch11-file-input-and-output/practice_files/little pics/to be deleted.jpg and /dev/null differ diff --git a/ch11-file-input-and-output/practice_files/pastimes.csv b/ch11-file-input-and-output/practice_files/pastimes.csv deleted file mode 100644 index 4680021..0000000 --- a/ch11-file-input-and-output/practice_files/pastimes.csv +++ /dev/null @@ -1,5 +0,0 @@ -Person,Favorite pastime -Fezzik,Fighting -Westley,Winning -Inigo Montoya,Sword fighting -Buttercup,Complaining diff --git a/ch11-file-input-and-output/practice_files/poem.txt b/ch11-file-input-and-output/practice_files/poem.txt deleted file mode 100644 index e4c8614..0000000 --- a/ch11-file-input-and-output/practice_files/poem.txt +++ /dev/null @@ -1,3 +0,0 @@ -This is the first line -The second line is longer -Hippopotamus \ No newline at end of file diff --git a/ch11-file-input-and-output/practice_files/tabbed wonka.csv b/ch11-file-input-and-output/practice_files/tabbed wonka.csv deleted file mode 100644 index eb83686..0000000 --- a/ch11-file-input-and-output/practice_files/tabbed wonka.csv +++ /dev/null @@ -1,4 +0,0 @@ -First name Last name Reward -Charlie Bucket golden ticket, chocolate factory -Veruca Salt squirrel revolution -Violet Beauregarde fruit chew diff --git a/ch11-file-input-and-output/practice_files/wonka.csv b/ch11-file-input-and-output/practice_files/wonka.csv deleted file mode 100644 index 665f2e8..0000000 --- a/ch11-file-input-and-output/practice_files/wonka.csv +++ /dev/null @@ -1,4 +0,0 @@ -First name,Last name,Reward -Charlie,Bucket,"golden ticket, chocolate factory" -Veruca,Salt,squirrel revolution -Violet,Beauregarde,fruit chew diff --git a/ch11-file-input-and-output/sample_csv.csv b/ch11-file-input-and-output/sample_csv.csv deleted file mode 100644 index ce6a3f5..0000000 --- a/ch11-file-input-and-output/sample_csv.csv +++ /dev/null @@ -1,101 +0,0 @@ -First Name,Last Name,Email Address,Phone Number,Company,Date Hired -Abigail,Branch,volutpat.ornare.facilisis@Phasellusvitaemauris.co.uk,(412) 540-6276,Sem Eget PC,07/02/2013 -Roanna,Lambert,tristique.pharetra@arcuvelquam.ca,(747) 536-6748,Eget Laoreet Foundation,11/23/2013 -Amanda,England,semper.rutrum@blandit.com,(669) 164-6411,Magna Nec Quam Limited,08/11/2012 -Hilel,Chapman,ultrices@tempor.ca,(683) 531-0279,Sed Molestie PC,06/25/2012 -Basia,Bowers,Quisque.ornare@tinciduntnibh.com,(135) 986-6437,Tincidunt Nunc Ac Associates,05/11/2013 -Dylan,Dunlap,est.Mauris@etnetuset.org,(877) 604-4603,Eu Ultrices Institute,07/02/2012 -Regan,Cardenas,vitae.semper@ultriciesornareelit.org,(693) 378-7235,Neque Morbi Corporation,10/30/2012 -Sade,Green,tortor@sagittis.co.uk,(816) 255-5508,Eleifend Ltd,09/03/2012 -Marshall,Richardson,sed.facilisis@eu.com,(460) 132-4621,Purus Maecenas Libero LLC,12/21/2012 -Regina,Brown,semper.auctor@sem.co.uk,(185) 963-9365,Vulputate Consulting,06/16/2013 -Irma,Rivers,vitae@luctusvulputate.net,(701) 393-3679,Nec Leo Morbi Incorporated,05/07/2013 -Rudyard,Cline,fringilla@risusatfringilla.org,(971) 228-3147,Risus Quis Consulting,04/25/2013 -Justina,Richmond,sapien.Nunc.pulvinar@vitaeerat.co.uk,(755) 103-3125,Ullamcorper Associates,02/12/2013 -Reece,Blackburn,felis@Aliquamauctor.com,(239) 528-2742,Suspendisse Associates,04/03/2014 -Lillith,Holden,ut.dolor.dapibus@porttitor.net,(305) 797-1579,Dapibus Id Blandit LLP,09/11/2013 -Taylor,Vinson,ac@vellectusCum.net,(355) 993-1099,Egestas Institute,05/16/2012 -Colton,Barker,volutpat@necluctus.ca,(705) 978-5992,Ornare Consulting,04/24/2013 -Vladimir,Walls,mollis.lectus@imperdietullamcorperDuis.edu,(311) 406-4856,Faucibus Ut Nulla LLP,08/12/2012 -Freya,Rowland,sagittis@elementumduiquis.co.uk,(284) 850-7506,Turpis PC,05/31/2013 -Cullen,Phelps,Nam.ligula@orciluctus.ca,(425) 280-1763,Rhoncus Id Mollis Consulting,09/10/2013 -Boris,Lopez,posuere@adipiscingligula.edu,(769) 701-0055,Nunc Sed Orci Industries,07/26/2013 -Alvin,Meyer,Etiam@felis.ca,(783) 312-0821,Dignissim Pharetra Ltd,03/02/2013 -Nicole,Boyle,tortor.Integer@imperdiet.edu,(675) 678-1160,Dictum Eleifend Nunc LLC,05/05/2012 -Flynn,Petersen,dui@lectusrutrum.com,(787) 543-7411,Penatibus Et Associates,03/11/2013 -Troy,Herman,a.felis.ullamcorper@sem.ca,(932) 900-7922,Dolor Donec Associates,11/16/2012 -Constance,Shields,nec.leo.Morbi@eunulla.com,(221) 761-2368,Vel Quam Company,02/14/2014 -Ocean,Green,vulputate.dui@bibendumDonecfelis.net,(481) 832-0298,Nunc Associates,03/03/2013 -Steven,Lopez,Suspendisse.ac@sedpedeCum.net,(294) 415-0435,Ipsum Company,07/25/2013 -Adara,Lee,magna.Duis@erat.org,(760) 291-7826,Eu Ultrices PC,10/05/2013 -Noble,Hancock,Donec.tincidunt.Donec@dictumcursusNunc.edu,(333) 272-8234,Vitae Risus Duis LLC,09/13/2012 -Kendall,Wilcox,quis.pede@Pellentesqueut.ca,(173) 982-4381,Ultrices Industries,01/26/2013 -Sebastian,Barton,orci.Ut@ametfaucibus.ca,(951) 817-9217,In Mi Pede Corporation,05/11/2014 -Gavin,Clark,metus.facilisis.lorem@Sedetlibero.ca,(671) 714-8378,Vestibulum Neque Limited,06/06/2012 -Charles,Woods,Maecenas.mi.felis@lacusvarius.org,(559) 935-9739,Amet Ante Company,09/02/2013 -Elvis,Roberts,tempor.diam@risus.co.uk,(184) 182-5324,Facilisis Vitae Inc.,01/07/2014 -Caldwell,Carey,Suspendisse@Proin.edu,(125) 243-9354,Egestas Lacinia Sed Inc.,10/24/2012 -Jesse,Leblanc,sit@tellussemmollis.com,(726) 216-8000,Lectus Ltd,11/22/2013 -Hu,Adkins,purus.in.molestie@acmattisvelit.co.uk,(370) 317-7556,Aliquam Vulputate Company,10/19/2013 -Hamilton,Tyler,taciti.sociosqu.ad@Sedmalesuadaaugue.com,(234) 744-3868,Nunc Sed LLC,10/19/2012 -Cade,Osborn,at.iaculis.quis@doloregestas.org,(501) 753-9793,Consectetuer Industries,08/14/2013 -Ashely,Kent,Cum.sociis.natoque@odioPhasellusat.edu,(789) 869-6558,Imperdiet Ornare Corporation,02/04/2013 -Veda,Cameron,tristique.pharetra@necenimNunc.co.uk,(522) 127-0654,Egestas Incorporated,12/29/2012 -Burke,Ferrell,orci.sem@semPellentesque.co.uk,(975) 891-3694,Purus Accumsan Institute,07/26/2013 -Fuller,Lamb,orci.Donec@vulputatedui.edu,(523) 614-5785,Pede Cum Sociis Limited,12/02/2013 -Natalie,Taylor,In@lorem.ca,(117) 594-2685,A Facilisis Non LLP,12/06/2013 -Astra,Morton,nec@scelerisquenequeNullam.com,(390) 867-2558,Non Ante Bibendum Foundation,05/07/2012 -David,Espinoza,gravida@a.co.uk,(287) 945-5239,Lobortis Nisi Nibh Industries,05/11/2014 -Sybil,Todd,risus@sitametrisus.edu,(611) 848-4765,Massa Mauris Vestibulum Incorporated,01/19/2013 -Lee,Barron,cursus.non@Praesentinterdumligula.ca,(765) 654-9167,In Ornare Inc.,01/01/2013 -Zachery,Reed,nulla.Integer.urna@amet.edu,(667) 465-1222,Ac Corp.,10/07/2012 -Marshall,Brady,lobortis.nisi.nibh@molestiearcu.edu,(391) 336-5310,Ac Sem Ut Incorporated,07/12/2012 -Selma,Floyd,eros.turpis.non@lectusconvallis.net,(398) 920-1076,Non Foundation,07/21/2012 -Ivy,Garrison,posuere@euodio.net,(428) 321-5542,Semper Erat Foundation,12/19/2013 -Wyatt,Gibbs,Sed@nequeNullamut.ca,(973) 141-9840,Pellentesque Corp.,11/21/2013 -Vaughan,Moss,adipiscing@Phasellusfermentum.net,(597) 730-0228,Tempor Institute,10/27/2013 -Elijah,Mcgowan,Aliquam@Quisqueornaretortor.ca,(127) 171-1859,Tempor Bibendum Donec LLC,08/26/2012 -Miranda,Ingram,fermentum@velitSedmalesuada.net,(864) 873-7359,Feugiat Non Lobortis Institute,08/20/2012 -Anastasia,Lawrence,Mauris.eu@pedeultrices.net,(106) 260-8688,Sit Amet Consulting,05/31/2012 -Samson,Patton,non.arcu@enimnislelementum.ca,(302) 330-4251,Hendrerit Associates,12/27/2013 -Erasmus,Sexton,lectus.justo@aliquam.org,(972) 793-9187,Feugiat Industries,10/15/2013 -Emery,Gardner,erat@lorem.org,(848) 534-1656,Nunc Sit Amet Industries,08/24/2012 -Nomlanga,Hensley,Fusce@leoVivamus.org,(644) 169-6243,Consectetuer Company,08/29/2012 -Jason,Craft,nunc.nulla@sapien.ca,(691) 770-9143,Blandit LLC,03/23/2013 -Kathleen,Haley,sed.dolor.Fusce@imperdietornare.edu,(891) 454-8400,Lorem Company,07/02/2012 -Aline,Flynn,a@Nunclaoreet.edu,(563) 400-6803,Et Netus LLP,01/28/2013 -Ursa,Dickson,Integer.sem@ullamcorpervelit.com,(371) 615-7750,Nullam Company,12/22/2012 -Wesley,Lopez,enim.non.nisi@vulputateduinec.edu,(287) 777-3724,Lobortis Ultrices Vivamus Corp.,06/17/2013 -Victoria,Mcleod,lectus.justo.eu@ut.ca,(583) 108-1294,Justo Faucibus Lectus Corporation,10/17/2012 -Shana,Roach,scelerisque.sed.sapien@afelisullamcorper.edu,(921) 385-2342,Quis Turpis Vitae Incorporated,05/26/2014 -Maxine,Ruiz,Donec.porttitor@hymenaeosMaurisut.edu,(520) 801-0808,Luctus Foundation,12/05/2013 -Harriet,Bishop,Quisque@Crasdictum.com,(758) 716-9401,Dictum Phasellus In Inc.,09/08/2013 -Serina,Williams,tincidunt.vehicula.risus@sedliberoProin.ca,(270) 288-0136,At Egestas A Corporation,03/17/2014 -Rhea,Copeland,laoreet.ipsum@Aliquam.co.uk,(775) 493-9118,Ipsum Incorporated,05/22/2013 -Evan,Holcomb,neque.sed@ullamcorperDuis.ca,(695) 656-8621,Sem Institute,02/16/2013 -Basil,Mccall,arcu.Vestibulum.ante@luctuslobortis.co.uk,(144) 989-4125,Feugiat Tellus Lorem Institute,02/25/2013 -Florence,Riley,sit.amet@Proinvel.org,(663) 529-4829,Enim Sit PC,01/14/2014 -Heather,Peck,mauris@scelerisqueneque.edu,(850) 444-0917,Curabitur Limited,01/16/2014 -Dara,Robinson,egestas@utnisi.net,(106) 576-1355,Urna Incorporated,12/15/2012 -Kylan,Maxwell,conubia.nostra@accumsan.com,(973) 206-2558,Aliquam Eros Turpis Company,08/21/2012 -Petra,Blake,faucibus.orci.luctus@dapibusrutrum.ca,(901) 207-9872,Ac Metus Institute,06/17/2013 -Fiona,Goff,tincidunt@enim.net,(265) 255-7749,Odio Phasellus Corp.,12/03/2012 -Kameko,Diaz,ac@turpisNulla.edu,(731) 354-4848,Montes Nascetur Corporation,08/16/2013 -Craig,Valentine,tristique@urnaVivamus.net,(437) 229-8198,Etiam Gravida Molestie Consulting,05/06/2014 -Samson,Cunningham,semper.pretium@auctor.edu,(335) 666-7758,Nec Ante Associates,07/02/2013 -Yoko,Rogers,nunc@Vivamus.net,(893) 405-6889,Fermentum Vel Mauris Corp.,03/29/2014 -Walter,Burnett,nisi.Mauris.nulla@felis.co.uk,(336) 411-9222,Suscipit Est Institute,06/26/2012 -Gisela,Nash,euismod@lectusrutrum.ca,(917) 249-0166,Non Magna LLP,11/23/2012 -Wanda,Pierce,Nulla@dolorsit.com,(480) 872-3389,Cum Sociis Natoque Limited,11/02/2013 -Jane,Dixon,eu.odio@Infaucibus.com,(112) 139-8563,Id Ante Dictum LLC,03/14/2014 -Octavius,Shannon,iaculis.aliquet@ante.ca,(541) 652-3295,Libero Est Institute,05/28/2014 -Rigel,Hunt,metus.Aenean.sed@inhendrerit.org,(792) 358-7505,Enim PC,09/05/2013 -Rachel,Gray,erat.in.consectetuer@Fuscealiquetmagna.org,(165) 973-1366,Suscipit Nonummy Fusce LLC,05/08/2013 -Madeline,Bradley,dignissim.Maecenas@egetmassaSuspendisse.co.uk,(436) 223-3135,Posuere PC,01/24/2014 -Emma,Conner,dictum@magnaDuisdignissim.com,(304) 429-2622,Nulla Incorporated,11/05/2013 -Halee,Mclean,amet.faucibus@Phasellus.net,(669) 364-0148,Ligula Consulting,03/05/2014 -Conan,Williams,massa@felisNulla.net,(999) 649-4433,Velit Eu Limited,05/15/2014 -Martena,Fowler,mi.lacinia@maurisa.ca,(405) 661-1762,Blandit Nam Institute,02/27/2013 -Robin,Buckley,cursus.Nunc.mauris@nislQuisque.net,(376) 771-9862,Sed Corp.,10/30/2012 -Isadora,Adams,arcu.Vestibulum@urna.co.uk,(138) 774-6058,Blandit Viverra Donec Institute,08/07/2012 -Bernard,Price,ultrices@Praesent.ca,(368) 882-6146,Egestas Blandit LLP,11/03/2013 \ No newline at end of file diff --git a/ch11-modules-and-packages/1-working-with-modules/greeter.py b/ch11-modules-and-packages/1-working-with-modules/greeter.py new file mode 100644 index 0000000..436349b --- /dev/null +++ b/ch11-modules-and-packages/1-working-with-modules/greeter.py @@ -0,0 +1,6 @@ +# Ch 11.1 - Modules and Packages +# Solution to Exercise 1 + + +def greet(name): + print(f"Hello {name}!") diff --git a/ch11-modules-and-packages/1-working-with-modules/main.py b/ch11-modules-and-packages/1-working-with-modules/main.py new file mode 100644 index 0000000..13cc631 --- /dev/null +++ b/ch11-modules-and-packages/1-working-with-modules/main.py @@ -0,0 +1,7 @@ +# Ch 11.1 - Modules and Packages +# Solution to Exercise 2 + +import greeter + + +greeter.greet("Real Python") diff --git a/ch11-modules-and-packages/2-working-with-packages/packages_exercises/helpers/__init__.py b/ch11-modules-and-packages/2-working-with-packages/packages_exercises/helpers/__init__.py new file mode 100644 index 0000000..6ea23c6 --- /dev/null +++ b/ch11-modules-and-packages/2-working-with-packages/packages_exercises/helpers/__init__.py @@ -0,0 +1,2 @@ +# Ch 11.2 - Working With Packages +# __init__.py - Part of solution to Exercise 1 diff --git a/ch11-modules-and-packages/2-working-with-packages/packages_exercises/helpers/math.py b/ch11-modules-and-packages/2-working-with-packages/packages_exercises/helpers/math.py new file mode 100644 index 0000000..745c003 --- /dev/null +++ b/ch11-modules-and-packages/2-working-with-packages/packages_exercises/helpers/math.py @@ -0,0 +1,6 @@ +# Ch 11.2 - Working With Packages +# helpers/math.py - Part of solution to Exercise 1 + + +def area(length, width): + return length * width diff --git a/ch11-modules-and-packages/2-working-with-packages/packages_exercises/helpers/string.py b/ch11-modules-and-packages/2-working-with-packages/packages_exercises/helpers/string.py new file mode 100644 index 0000000..a6480aa --- /dev/null +++ b/ch11-modules-and-packages/2-working-with-packages/packages_exercises/helpers/string.py @@ -0,0 +1,6 @@ +# Ch 11.2 - Working With Packages +# helpers/string.py - Part of solution to Exercise 1 + + +def shout(string): + return string.upper() diff --git a/ch11-modules-and-packages/2-working-with-packages/packages_exercises/main.py b/ch11-modules-and-packages/2-working-with-packages/packages_exercises/main.py new file mode 100644 index 0000000..33c8648 --- /dev/null +++ b/ch11-modules-and-packages/2-working-with-packages/packages_exercises/main.py @@ -0,0 +1,11 @@ +# Ch 11.2 - Working With Packages +# main.py - Solution to Exercise 2 + +from helpers.string import shout +from helpers.math import area + + +length = 5 +width = 8 +message = f"The area of a {length}-by-{width} rectangle is {area(length, width)}" +print(shout(message)) diff --git a/ch12-file-input-and-output/2-working-with-file-paths-in-python.py b/ch12-file-input-and-output/2-working-with-file-paths-in-python.py new file mode 100644 index 0000000..8726232 --- /dev/null +++ b/ch12-file-input-and-output/2-working-with-file-paths-in-python.py @@ -0,0 +1,20 @@ +# 12.2 - Working With File Paths in Python +# Solutions to review exercises + + +# Exercise 1 +from pathlib import Path + +file_path = Path.home() / "my_folder" / "my_file.txt" + + +# Exercise 2 +print(file_path.exists()) + + +# Exercise 3 +print(file_path.name) + + +# Exercise 4 +print(file_path.parent.name) diff --git a/ch12-file-input-and-output/3-common-file-system-operations.py b/ch12-file-input-and-output/3-common-file-system-operations.py new file mode 100644 index 0000000..eaf8042 --- /dev/null +++ b/ch12-file-input-and-output/3-common-file-system-operations.py @@ -0,0 +1,35 @@ +# 12.3 Common File System Operations +# Solutions to Exercises + + +# Exercise 1 +from pathlib import Path + +new_dir = Path.home() / "my_folder" +new_dir.mkdir() + + +# Exercise 2 +file1 = new_dir / "file1.txt" +file2 = new_dir / "file2.txt" +image1 = new_dir / "image1.png" + +file1.touch() +file2.touch() +image1.touch() + + +# Exercise 3 +images_dir = new_dir / "images" +images_dir.mkdir() +image1.replace(images_dir / "image1.png") + + +# Exercise 4 +file1.unlink() + + +# Exercise 5 +import shutil + +shutil.rmtree(new_dir) diff --git a/ch12-file-input-and-output/4-challenge-move-all-image-files-to-a-new-directory.py b/ch12-file-input-and-output/4-challenge-move-all-image-files-to-a-new-directory.py new file mode 100644 index 0000000..b39266c --- /dev/null +++ b/ch12-file-input-and-output/4-challenge-move-all-image-files-to-a-new-directory.py @@ -0,0 +1,17 @@ +# 12.4 Challenge: Move All Image Files To a New Directory +# Solution to Challenge + +from pathlib import Path + +# Change this path to match the location on your computer +documents_dir = Path.cwd() / "practice_files" / "documents" + +# Create an images/ directory in your home directory +images_dir = Path.home() / "images" +images_dir.mkdir(exist_ok=True) + +# Search for image files in the documents directory and move +# them to the images/ directory +for path in documents_dir.rglob("*.*"): + if path.suffix.lower() in [".png", ".jpg", ".gif"]: + path.replace(images_dir / path.name) diff --git a/ch12-file-input-and-output/5-reading-and-writing-files.py b/ch12-file-input-and-output/5-reading-and-writing-files.py new file mode 100644 index 0000000..2dd4f0c --- /dev/null +++ b/ch12-file-input-and-output/5-reading-and-writing-files.py @@ -0,0 +1,25 @@ +# 12.5 - Reading and Writing Files +# Solutions to Exercises + + +# Exercise 1 +from pathlib import Path + +starships = ["Discovery\n", "Enterprise\n", "Defiant\n", "Voyager"] + +file_path = Path.home() / "starships.txt" +with file_path.open(mode="w", encoding="utf-8") as file: + file.writelines(starships) + + +# Exercise 2 +with file_path.open(mode="r", encoding="utf-8") as file: + for starship in file.readlines(): + print(starship, end="") + + +# Exercise 3 +with file_path.open(mode="r", encoding="utf-8") as file: + for starship in file.readlines(): + if starship.startswith("D"): + print(starship, end="") diff --git a/ch12-file-input-and-output/6-read-and-write-csv-data.py b/ch12-file-input-and-output/6-read-and-write-csv-data.py new file mode 100644 index 0000000..233b1d4 --- /dev/null +++ b/ch12-file-input-and-output/6-read-and-write-csv-data.py @@ -0,0 +1,57 @@ +# 12.5 Read and Write CSV Data +# Solutions to Exercises + + +# Exercise 1 +import csv +from pathlib import Path + +numbers = [ + [1, 2, 3, 4, 5], + [6, 7, 8, 9, 10], + [11, 12, 13, 14, 15], +] + +file_path = Path.home() / "numbers.csv" + +with file_path.open(mode="w", encoding="utf-8") as file: + writer = csv.writer(file) + writer.writerows(numbers) + + +# Exercise 2 +numbers = [] + +with file_path.open(mode="r", encoding="utf-8") as file: + reader = csv.reader(file) + for row in reader: + int_row = [int(num) for num in row] + numbers.append(int_row) + +print(numbers) + + +# Exercise 3 +favorite_colors = [ + {"name": "Joe", "favorite_color": "blue"}, + {"name": "Anne", "favorite_color": "green"}, + {"name": "Bailey", "favorite_color": "red"}, +] + +file_path = Path.home() / "favorite_colors.csv" + +with file_path.open(mode="w", encoding="utf-8") as file: + writer = csv.DictWriter(file, fieldnames=["name", "favorite_color"]) + writer.writeheader() + writer.writerows(favorite_colors) + + +# Exercise 4 +favorite_colors = [] + +with file_path.open(mode="r", encoding="utf-8") as file: + reader = csv.DictReader(file) + for row in reader: + favorite_colors.append(row) + +print(favorite_colors) diff --git a/ch12-file-input-and-output/7-challenge-create-a-high-scores-list.py b/ch12-file-input-and-output/7-challenge-create-a-high-scores-list.py new file mode 100644 index 0000000..f9539f8 --- /dev/null +++ b/ch12-file-input-and-output/7-challenge-create-a-high-scores-list.py @@ -0,0 +1,41 @@ +# 12.7 Challenge: Create a High Scores List +# Solution to Challenge + +import csv +from pathlib import Path + +# Change the path below to match the location on your computer +scores_csv_path = ( + Path.cwd() + / "practice_files" + / "scores.csv" +) + +with scores_csv_path.open(mode="r", encoding="utf-8") as file: + reader = csv.DictReader(file) + scores = [row for row in reader] + +high_scores = {} +for item in scores: + name = item["name"] + score = int(item["score"]) + # If the name has not been added to the high_score dictionary, then + # create a new key with the name and set its value to the score + if name not in high_scores: + high_scores[name] = score + # Otherwise, check to see if score is greater than the score currently + # assigned to high_scores[name] and replace it if it is + else: + if score > high_scores[name]: + high_scores[name] = score + +# The high_scores dictionary now contains one key for each name that was +# in the scores.csv file, and each value is that player's highest score. + +output_csv_file = Path.cwd() / "high_scores.csv" +with output_csv_file.open(mode="w", encoding="utf-8") as file: + writer = csv.DictWriter(file, fieldnames=["name", "high_score"]) + writer.writeheader() + for name in high_scores: + row_dict = {"name": name, "high_score": high_scores[name]} + writer.writerow(row_dict) diff --git a/ch11-file-input-and-output/practice_files/backup/images/additional files/one last image.png b/ch12-file-input-and-output/practice_files/documents/files/additional files/image3.png similarity index 100% rename from ch11-file-input-and-output/practice_files/backup/images/additional files/one last image.png rename to ch12-file-input-and-output/practice_files/documents/files/additional files/image3.png diff --git a/ch12-file-input-and-output/practice_files/documents/files/dad.txt b/ch12-file-input-and-output/practice_files/documents/files/dad.txt new file mode 100644 index 0000000..0641008 --- /dev/null +++ b/ch12-file-input-and-output/practice_files/documents/files/dad.txt @@ -0,0 +1 @@ +Yo dad, what's up? diff --git a/ch12-file-input-and-output/practice_files/documents/files/stuff.csv b/ch12-file-input-and-output/practice_files/documents/files/stuff.csv new file mode 100644 index 0000000..570d0f1 --- /dev/null +++ b/ch12-file-input-and-output/practice_files/documents/files/stuff.csv @@ -0,0 +1,2 @@ +1,2,3,4,5 +a,b,c,d,e diff --git a/ch11-file-input-and-output/practice_files/backup/images/png file - not a gif.png b/ch12-file-input-and-output/practice_files/documents/image1.png similarity index 100% rename from ch11-file-input-and-output/practice_files/backup/images/png file - not a gif.png rename to ch12-file-input-and-output/practice_files/documents/image1.png diff --git a/ch11-file-input-and-output/practice_files/backup/little pics/look in here too/definitely has to go.jpg b/ch12-file-input-and-output/practice_files/documents/more_files/even_more_files/image4.jpg similarity index 100% rename from ch11-file-input-and-output/practice_files/backup/little pics/look in here too/definitely has to go.jpg rename to ch12-file-input-and-output/practice_files/documents/more_files/even_more_files/image4.jpg diff --git a/ch12-file-input-and-output/practice_files/documents/more_files/even_more_files/the_answer.txt b/ch12-file-input-and-output/practice_files/documents/more_files/even_more_files/the_answer.txt new file mode 100644 index 0000000..d81cc07 --- /dev/null +++ b/ch12-file-input-and-output/practice_files/documents/more_files/even_more_files/the_answer.txt @@ -0,0 +1 @@ +42 diff --git a/ch11-file-input-and-output/practice_files/backup/images/one more image.gif b/ch12-file-input-and-output/practice_files/documents/more_files/image2.gif similarity index 100% rename from ch11-file-input-and-output/practice_files/backup/images/one more image.gif rename to ch12-file-input-and-output/practice_files/documents/more_files/image2.gif diff --git a/ch11-file-input-and-output/practice_files/backup/little pics/better not delete me.txt b/ch12-file-input-and-output/practice_files/documents/more_files/mom.txt similarity index 100% rename from ch11-file-input-and-output/practice_files/backup/little pics/better not delete me.txt rename to ch12-file-input-and-output/practice_files/documents/more_files/mom.txt diff --git a/ch11-file-input-and-output/practice_files/scores.csv b/ch12-file-input-and-output/practice_files/scores.csv similarity index 94% rename from ch11-file-input-and-output/practice_files/scores.csv rename to ch12-file-input-and-output/practice_files/scores.csv index c7429ab..7663358 100644 --- a/ch11-file-input-and-output/practice_files/scores.csv +++ b/ch12-file-input-and-output/practice_files/scores.csv @@ -1,3 +1,4 @@ +name,score LLCoolDave,23 LLCoolDave,27 red,12 diff --git a/ch13-interact-with-pdf-files/1-work-with-the-contents-of-a-pdf-file.py b/ch13-interact-with-pdf-files/1-work-with-the-contents-of-a-pdf-file.py deleted file mode 100644 index f3fc609..0000000 --- a/ch13-interact-with-pdf-files/1-work-with-the-contents-of-a-pdf-file.py +++ /dev/null @@ -1,42 +0,0 @@ -# 13.1 - Work With the Contents of a PDF File -# Solutions to review exercises - - -import os -from PyPDF2 import PdfFileReader, PdfFileWriter - - -# Exercise 1 -path = "C:/python-basics-exercises/ch13-interact-with-pdf-files/\ -practice_files" - -input_file_path = os.path.join(path, "The Whistling Gypsy.pdf") -input_file = PdfFileReader(input_file_path) - -# Display meta-data about file -print("Title:", input_file.getDocumentInfo().title) -print("Author:", input_file.getDocumentInfo().author) -print("Number of pages:", input_file.getNumPages()) - - -# Exercise 2 -# Specify and open output text file -output_file_path = os.path.join(path, "Output/The Whistling Gypsy.txt") -with open(output_file_path, "w") as output_file: - # Extract every page of text - for page_num in range(0, input_file.getNumPages()): - text = input_file.getPage(page_num).extractText() - output_file.write(text) - - -# Exercise 3 -# Save file without cover page -output_PDF = PdfFileWriter() -for page_num in range(1, input_file.getNumPages()): - output_PDF.addPage(input_file.getPage(page_num)) - -output_file_name = os.path.join( - path, "Output/The Whistling Gypsy un-covered.pdf" -) -with open(output_file_name, "wb") as output_file: - output_PDF.write(output_file) diff --git a/ch13-interact-with-pdf-files/2-manipulate-pdf-files.py b/ch13-interact-with-pdf-files/2-manipulate-pdf-files.py deleted file mode 100644 index 485f50a..0000000 --- a/ch13-interact-with-pdf-files/2-manipulate-pdf-files.py +++ /dev/null @@ -1,41 +0,0 @@ -# 13.2 - Manipulate PDF Files -# Solutions to review exercises - -import os -import copy -from pyPDF2 import PdfFileReader, PdfFileWriter - - -# Exercise 1 -path = "C:/python-basics-exercises/ch13-interact-with-pdf-files/\ - practice_files" - -input_file_path = os.path.join(path, "Walrus.pdf") -input_file = PdfFileReader(input_file_path) -output_PDF = PdfFileWriter() - -input_file.decrypt("IamtheWalrus") # decrypt the input file - - -# Exercise 2 -for page_num in range(0, input_file.getNumPages()): - # rotate pages (call everything page_left for now; will make a copy) - page_left = input_file.getPage(page_num) - page_left.rotateCounterClockwise(90) - - page_right = copy.copy(page_left) # split each page in half - upper_right = page_left.mediaBox.upperRight # get original page corner - - # crop and add left-side page - page_left.mediaBox.upperRight = (upper_right[0] / 2, upper_right[1]) - output_PDF.addPage(page_left) - # crop and add right-side page - page_right.mediaBox.upperLeft = (upper_right[0] / 2, upper_right[1]) - output_PDF.addPage(page_right) - - -# Exercise 3 -# save new pages to an output file -output_file_path = os.path.join(path, "Output/Updated Walrus.pdf") -with open(output_file_path, "wb") as output_file: - output_PDF.write(output_file) diff --git a/ch13-interact-with-pdf-files/3-challenge.py b/ch13-interact-with-pdf-files/3-challenge.py deleted file mode 100644 index 3cbae00..0000000 --- a/ch13-interact-with-pdf-files/3-challenge.py +++ /dev/null @@ -1,35 +0,0 @@ -# 13.3 - Challenge: Add a Cover Sheet to a PDF File -# Solution to challenge - - -# Add a cover sheet to a PDF; save the full output as a new PDF - -import os -from pyPDF2 import PdfFileReader, PdfFileWriter - -path = "C:/python-basics-exercises/ch13-interact-with-pdf-files/\ - practice_files" - -input_file_path1 = os.path.join(path, "Emperor cover sheet.pdf") -input_file1 = PdfFileReader(input_file_path1) - -input_file_path2 = os.path.join(path, "The Emperor.pdf") -input_file2 = PdfFileReader(input_file_path2) - -output_PDF = PdfFileWriter() - -# Read in all pages from the cover sheet PDF file -for page_num in range(0, input_file1.getNumPages()): - page = input_file1.getPage(page_num) - output_PDF.addPage(page) - -# Read in all pages from "The Emperor.pdf" into the same output file -for page_num in range(0, input_file2.getNumPages()): - page = input_file2.getPage(page_num) - output_PDF.addPage(page) - -# Output the results into a new PDF -output_file_path = os.path.join(path, "Output/The Covered Emperor.pdf") - -with open(output_file_path, "wb") as output_file: - output_PDF.write(output_file) diff --git a/ch13-interact-with-pdf-files/practice_files/The Whistling Gypsy.pdf b/ch13-interact-with-pdf-files/practice_files/The Whistling Gypsy.pdf deleted file mode 100644 index cdaf7a8..0000000 Binary files a/ch13-interact-with-pdf-files/practice_files/The Whistling Gypsy.pdf and /dev/null differ diff --git a/ch13-interact-with-pdf-files/practice_files/Walrus.pdf b/ch13-interact-with-pdf-files/practice_files/Walrus.pdf deleted file mode 100644 index f86ea3d..0000000 Binary files a/ch13-interact-with-pdf-files/practice_files/Walrus.pdf and /dev/null differ diff --git a/ch13-interact-with-pdf-files/practice_files/top secret.pdf b/ch13-interact-with-pdf-files/practice_files/top secret.pdf deleted file mode 100644 index a4fd123..0000000 Binary files a/ch13-interact-with-pdf-files/practice_files/top secret.pdf and /dev/null differ diff --git a/ch14-interact-with-pdf-files/1-extract-text-from-a-pdf.py b/ch14-interact-with-pdf-files/1-extract-text-from-a-pdf.py new file mode 100644 index 0000000..49e1d76 --- /dev/null +++ b/ch14-interact-with-pdf-files/1-extract-text-from-a-pdf.py @@ -0,0 +1,74 @@ +# 14.1 - Extract Text From a PDF +# Solutions to review exercises + + +# *********** +# Exercise 1 +# +# In the Chapter 13 Practice Files directory there is a PDF file called +# `zen.pdf`. Create a `PdfFileReader` from this PDF. +# *********** + +# Before you can do anything, you need to import the right objects from +# the PyPDF2 and pathlib libraries +from pathlib import Path +from PyPDF2 import PdfFileReader + +# To create a PdfFileReader instance, you need to path to the PDF file. +# We'll assume you downloaded the solutions folder and are running this +# program from the solutions folder. If this is not the case, you'll +# need to update the path below. +pdf_path = Path.cwd() / "practice_files" / "zen.pdf" + +# Now you can create the PdfFileReader instance. Remember that +# PdfFileReader objects can only be instantiated with path strings, not +# Path objects! +pdf_reader = PdfFileReader(str(pdf_path)) + + +# *********** +# Exercise 2 +# +# Using the `PdfFileReader` instance from Exercise 1, print the total +# number of pages in the PDF. +# *********** + +# Use .getNumPages() to get the number of pages, then print the result +# using the print() built-in +num_pages = pdf_reader.getNumPages() +print(num_pages) + + +# *********** +# Exercise 3 +# +# Print the text from the first page of the PDF in Exercise 1. +# *********** + +# Use .getPage() to get the first page. Remember pages are indexed +# starting with 0! +first_page = pdf_reader.getPage(0) + +# Then use .extractText() to extract the text +text = first_page.extractText() + +# Finally, print the text +print(text) + + +# **NOTE**: The text in zen.pdf is from "The Zen Of Python" written by +# Tim Peters in 1999. The Zen is a collection of 19 guiding principles +# for developing with Python. The story goes that there are actually 20 +# such principles, but only 19 were written down! +# +# You can see the original submission for The Zen of Python in PEP20: +# https://www.python.org/dev/peps/pep-0020/ +# +# For some historical context surrounding The Zen, see: +# https://mail.python.org/pipermail/python-list/1999-June/001951.html +# +# Author Al Seigart has an interpretation of The Zen on his blog: +# https://inventwithpython.com/blog/2018/08/17/the-zen-of-python-explained/ +# +# Moshe Zadka has another great article on The Zen: +# https://orbifold.xyz/zen-of-python.html diff --git a/ch14-interact-with-pdf-files/2-extract-pages-from-a-pdf.py b/ch14-interact-with-pdf-files/2-extract-pages-from-a-pdf.py new file mode 100644 index 0000000..fc34b2b --- /dev/null +++ b/ch14-interact-with-pdf-files/2-extract-pages-from-a-pdf.py @@ -0,0 +1,124 @@ +# 14.2 - Extract Pages From a PDF +# Solutions to review exercises + +# *********** +# Exercise 1 +# +# Extract the last page from the `Pride_and_Prejudice.pdf` file and +# save it to a new file called `last_page.pdf` in your home directory. +# *********** + +# First import the classes and libraries needed +from pathlib import Path +from PyPDF2 import PdfFileReader, PdfFileWriter + +# Get the path to the `Pride_and_Prejudice.pdf` file. We'll assume you +# downloaded the solutions folder and extracted it into the home +# directory on your computer. If this is not the case, you'll need to +# update the path below. +pdf_path = Path.home() / "python-basics-exercises/ch14-interact-with-pdf-files/practice_files/Pride_and_Prejudice.pdf" + +# Now you can create the PdfFileReader instance. Remember that +# PdfFileReader objects can only be instantiated with path strings, not +# Path objects! +pdf_reader = PdfFileReader(str(pdf_path)) + +# Use the .pages attribute to get an iterable over all pages in the +# PDF. The last page can be accessed with the index -1. +last_page = pdf_reader.pages[-1] + +# Now you can create a PdfFileWriter instance and add the last page to it. +pdf_writer = PdfFileWriter() +pdf_writer.addPage(last_page) + +# Finally, write the contents of pdf_writer to the file `last_page.pdf` +# in your home directory. +output_path = Path.home() / "last_page.pdf" +with output_path.open(mode="wb") as output_file: + pdf_writer.write(output_file) + + +# *********** +# Exercise 2 +# +# Extract all pages with even numbered _indices_ from the +# `Pride_and_Prejudice.pdf` and save them to a new file called +# `every_other_page.pdf` in your home directory. +# *********** + +# There are several ways to extract pages with even numbered indices +# so we'll cover a few of them here. + +# Solution A: Using a `for` loop +# ------------------------------ + +# One way to do it is with a `for` loop. We'll create a new PdfFileWriter +# instance, then loop over the numbers 0 up to the number of pages in the +# PDF, and add the pages with even indices to the PdfFileWriter instance. +pdf_writer = PdfFileWriter() +num_pages = pdf_reader.getNumPages() + +for idx in range(num_pages): # NOTE: idx is a common short name for "index" + if idx % 2 == 0: # Check that the index is even + page = pdf_reader.getPage(idx) # Get the page at the index + pdf_writer.addPage(page) # Add the page to `pdf_writer` + +# Now write the contents of `pdf_writer` the the file `every_other_page.pdf` +# in your home directory +output_path = Path.home() / "every_other_page.pdf" +with output_path.open(mode="wb") as output_file: + pdf_writer.write(output_file) + +# Solution B: Slicing .`pages` with steps +# ------------------------------ + +# A more succinct, alghouth possibly more difficult to understand, +# solution involves slicing the `.pages` iterable. The indices start +# with 0 and every even index can be obtained by iterating over +# `.pages` in steps of size 2, so `.pages[::2]` is an iterable +# containing just the pages with even indices. +pdf_writer = PdfFileWriter() + +for page in pdf_reader.pages[::2]: + pdf_writer.addPage(page) + +# Now write the contents of `pdf_writer` the the file +# `every_other_page.pdf` in your home directory. +output_path = Path.home() / "every_other_page.pdf" +with output_path.open(mode="wb") as output_file: + pdf_writer.write(output_file) + + +# *********** +# Exercise 3 +# +# Split the `Pride_and_Prejudice.pdf` file into two new PDF files. The +# first file should contain the first 150 pages, and the second file +# should contain the remaining pages. Save both files in your home +# directory as `part_1.pdf` and `part_2.pdf`. +# *********** + +# Start by creating two new PdfFileWriter instances. +part1_writer = PdfFileWriter() +part2_writer = PdfFileWriter() + +# Next, create two new iterables containing the correct pages. +part1_pages = pdf_reader.pages[:150] # Contains pages 0 - 149 +part2_pages = pdf_reader.pages[150:] # Contains pages 150 - last page + +# Add the pages to their corresponding writers. +for page in part1_pages: + part1_writer.addPage(page) + +for page in part2_pages: + part2_writer.addPage(page) + +# Now write the contents of each writer to the files `part_1.pdf` and +# `part_2.pdf` in your home directory. +part1_output_path = Path.home() / "part_1.pdf" +with part1_output_path.open(mode="wb") as part1_output_file: + part1_writer.write(part1_output_file) + +part2_output_path = Path.home() / "part_2.pdf" +with part2_output_path.open(mode="wb") as part2_output_file: + part2_writer.write(part2_output_file) diff --git a/ch14-interact-with-pdf-files/3-challenge-PdfFileSplitter-class.py b/ch14-interact-with-pdf-files/3-challenge-PdfFileSplitter-class.py new file mode 100644 index 0000000..5357574 --- /dev/null +++ b/ch14-interact-with-pdf-files/3-challenge-PdfFileSplitter-class.py @@ -0,0 +1,47 @@ +# 14.5 - Challenge: PdfFileSplitter Class +# Solution to challenge + +from pathlib import Path + +from PyPDF2 import PdfFileReader, PdfFileWriter + + +class PdfFileSplitter: + """Class for splitting a PDF into two files.""" + + def __init__(self, pdf_path): + # Open the PDF file with a new PdfFileReader instance + self.pdf_reader = PdfFileReader(pdf_path) + # Initialize the .writer1 and .writer2 attributes to None + self.writer1 = None + self.writer2 = None + + def split(self, breakpoint): + """Split the PDF into two PdfFileWriter instances""" + # Set .writer1 and .writer2 to new PdfFileWriter intances + self.writer1 = PdfFileWriter() + self.writer2 = PdfFileWriter() + # Add all pages up to, but not including, the breakpoint + # to writer1 + for page in self.pdf_reader.pages[:breakpoint]: + self.writer1.addPage(page) + # Add all the remaining pages to writer2 + for page in self.pdf_reader.pages[breakpoint:]: + self.writer2.addPage(page) + + def write(self, filename): + """Write both PdfFileWriter instances to files""" + # Write the first file to _1.pdf + with Path(filename + "_1.pdf").open(mode="wb") as output_file: + self.writer1.write(output_file) + # Write the second file to _2.pdf + with Path(filename + "_2.pdf").open(mode="wb") as output_file: + self.writer2.write(output_file) + + +# Split the Pride_and_Prejudice.pdf file into two PDFs, the first +# containing the first 150 pages, and the second containing the +# remaining pages. +pdf_splitter = PdfFileSplitter("ch14-interact-with-pdf-files/practice_files/Pride_and_Prejudice.pdf") +pdf_splitter.split(breakpoint=150) +pdf_splitter.write("pride_split") diff --git a/ch14-interact-with-pdf-files/4-concatenating-and-merging-pdfs.py b/ch14-interact-with-pdf-files/4-concatenating-and-merging-pdfs.py new file mode 100644 index 0000000..0753bf6 --- /dev/null +++ b/ch14-interact-with-pdf-files/4-concatenating-and-merging-pdfs.py @@ -0,0 +1,51 @@ +# 14.4 - Concatenating and Merging PDFs +# Solutions to review exercises + +# *********** +# Exercise 1 +# +# In the Chapter 13 Practice Files directory there are three PDFs called +# `merge1.pdf`, `merge2.pdf`, and `merge3.pdf`. Using a `PdfFileMerger` +# instance, concatenate the two files `merge1.pdf` and `merge2.pdf` +# using`.append()`. +# *********** + +from pathlib import Path + +from PyPDF2 import PdfFileMerger + + +BASE_PATH = Path.cwd() / "practice_files" + +pdf_paths = [BASE_PATH / "merge1.pdf", BASE_PATH / "merge2.pdf"] +pdf_merger = PdfFileMerger() + +for path in pdf_paths: + pdf_merger.append(str(path)) + +output_path = Path.home() / "concatenated.pdf" +with output_path.open(mode="wb") as output_file: + pdf_merger.write(output_file) + + +# *********** +# Exercise 2 +# +# Using the same `PdfFileMerger` instance from exercise 1, merge the +# file `merge3.pdf` in-between the pages from `merge1.pdf` and +# `merge2.pdf` using `.merge()`. +# +# The final result should be a PDF with three pages. The first page +# should have the number `1` on it, the second should have `2`, and the +# third should have `3`. +# *********** + +pdf_merger = PdfFileMerger() +pdf_merger.append(str(output_path)) + +pdf_path = BASE_PATH / "merge3.pdf" +pdf_merger.merge(1, str(pdf_path)) + +output_path = Path.home() / "merged.pdf" +with output_path.open(mode="wb") as output_file: + pdf_merger.write(output_file) diff --git a/ch14-interact-with-pdf-files/5-rotating-and-cropping-pdf-pages.py b/ch14-interact-with-pdf-files/5-rotating-and-cropping-pdf-pages.py new file mode 100644 index 0000000..3abe217 --- /dev/null +++ b/ch14-interact-with-pdf-files/5-rotating-and-cropping-pdf-pages.py @@ -0,0 +1,68 @@ +# 14.5 - Rotating and Cropping PDF pages +# Solutions to review exercises + +# *********** +# Exercise 1 +# +# In the Chapter 13 Practice Files folder there is a PDF called +# `split_and_rotate.pdf`. Create a new PDF called `rotated.pdf` in your +# home directory containing the pages of `split_and_rotate.pdf` rotated +# counter-clockwise 90 degrees. +# *********** + +from pathlib import Path + +from PyPDF2 import PdfFileReader, PdfFileWriter + + +pdf_path = Path.cwd() / "practice_files" / "split_and_rotate.pdf" + +pdf_reader = PdfFileReader(str(pdf_path)) +pdf_writer = PdfFileWriter() + +for page in pdf_reader.pages: + rotated_page = page.rotateCounterClockwise(90) + pdf_writer.addPage(rotated_page) + +output_path = Path.home() / "rotated.pdf" +with output_path.open(mode="wb") as output_file: + pdf_writer.write(output_file) + + +# *********** +# Exercise 2 +# +# Using the `rotated.pdf` file you created in exercise 1, split each +# page of the PDF vertically in the middle. Create a new PDF called +# `split.pdf` in your home directory containing all of the split pages. +# +# `split.pdf` should have four pages with the numbers `1`, `2`, `3`, +# and `4`, in order. +# *********** +import copy + +pdf_path = Path.home() / "rotated.pdf" + +pdf_reader = PdfFileReader(str(pdf_path)) +pdf_writer = PdfFileWriter() + +for page in pdf_reader.pages: + # Calculate the coordinates at the top center of the page + upper_right_coords = page.mediaBox.upperRight + center_coords = (upper_right_coords[0] / 2, upper_right_coords[1]) + # Create two copies of the page, one for the left side and one for + # the right side + left_page = copy.deepcopy(page) + right_page = copy.deepcopy(page) + # Crop the pages by setting the upper right corner coordinates + # of the left hand page and the upper left corner coordinates of + # the right hand page to the top center coordinates + left_page.mediaBox.upperRight = center_coords + right_page.mediaBox.upperLeft = center_coords + # Add the cropped pages to the PDF writer + pdf_writer.addPage(left_page) + pdf_writer.addPage(right_page) + +output_path = Path.home() / "split.pdf" +with output_path.open(mode="wb") as output_file: + pdf_writer.write(output_file) diff --git a/ch14-interact-with-pdf-files/6-encrypting-and-decrypting-pdfs.py b/ch14-interact-with-pdf-files/6-encrypting-and-decrypting-pdfs.py new file mode 100644 index 0000000..22a365a --- /dev/null +++ b/ch14-interact-with-pdf-files/6-encrypting-and-decrypting-pdfs.py @@ -0,0 +1,44 @@ +# 14.6 - Encrypting and Decrypting PDFs +# Solutions to review exercises + +# *********** +# Exercise 1 +# +# In the Chapter 13 Practice Files folder there is a PDF file called +# `top_secret.pdf`. Encrypt the file with the user password +# `Unguessable`. Save the encrypted file as in your home directory as +# `top_secret_encrypted.pdf`. +# *********** + +from pathlib import Path + +from PyPDF2 import PdfFileReader, PdfFileWriter + + +pdf_path = Path.cwd() / "practice_files" / "top_secret.pdf" + +pdf_reader = PdfFileReader(str(pdf_path)) +pdf_writer = PdfFileWriter() + +pdf_writer.appendPagesFromReader(pdf_reader) +pdf_writer.encrypt(user_pwd="Unguessable") + +output_path = Path.home() / "top_secret_encrypted.pdf" +with output_path.open(mode="wb") as output_file: + pdf_writer.write(output_file) + + +# *********** +# Exercise 2 +# +# Open the `top_secret_encrpyted.pdf` file you created in exercise 1, +# decrypt it, and print the text on the first page of the PDF. +# *********** + +pdf_path = Path.home() / "top_secret_encrypted.pdf" +pdf_reader = PdfFileReader(str(pdf_path)) + +pdf_reader.decrypt("Unguessable") + +first_page = pdf_reader.getPage(0) +print(first_page.extractText()) diff --git a/ch14-interact-with-pdf-files/7-challenge-unscramble-a-pdf.py b/ch14-interact-with-pdf-files/7-challenge-unscramble-a-pdf.py new file mode 100644 index 0000000..ded54b2 --- /dev/null +++ b/ch14-interact-with-pdf-files/7-challenge-unscramble-a-pdf.py @@ -0,0 +1,29 @@ +# 14.7 - Challenge: Unscramble a PDF +# Solution to challenge + +from pathlib import Path + +from PyPDF2 import PdfFileReader, PdfFileWriter + + +def get_page_text(page): + return page.extractText() + + +pdf_path = Path.cwd() / "practice_files" / "scrambled.pdf" + +pdf_reader = PdfFileReader(str(pdf_path)) +pdf_writer = PdfFileWriter() + +pages = list(pdf_reader.pages) +pages.sort(key=get_page_text) + +for page in pages: + rotation_degrees = page["/Rotate"] + if rotation_degrees != 0: + page.rotateCounterClockwise(rotation_degrees) + pdf_writer.addPage(page) + +output_path = Path.home() / "unscrambled.pdf" +with output_path.open(mode="wb") as output_file: + pdf_writer.write(output_file) diff --git a/ch13-interact-with-pdf-files/practice_files/Emperor cover sheet.pdf b/ch14-interact-with-pdf-files/practice_files/Emperor cover sheet.pdf similarity index 100% rename from ch13-interact-with-pdf-files/practice_files/Emperor cover sheet.pdf rename to ch14-interact-with-pdf-files/practice_files/Emperor cover sheet.pdf diff --git a/ch13-interact-with-pdf-files/practice_files/Pride and Prejudice.pdf b/ch14-interact-with-pdf-files/practice_files/Pride_and_Prejudice.pdf similarity index 100% rename from ch13-interact-with-pdf-files/practice_files/Pride and Prejudice.pdf rename to ch14-interact-with-pdf-files/practice_files/Pride_and_Prejudice.pdf diff --git a/ch13-interact-with-pdf-files/practice_files/The Emperor.pdf b/ch14-interact-with-pdf-files/practice_files/The Emperor.pdf similarity index 100% rename from ch13-interact-with-pdf-files/practice_files/The Emperor.pdf rename to ch14-interact-with-pdf-files/practice_files/The Emperor.pdf diff --git a/ch14-interact-with-pdf-files/practice_files/expense_reports/Expense report 1.pdf b/ch14-interact-with-pdf-files/practice_files/expense_reports/Expense report 1.pdf new file mode 100644 index 0000000..7df034e Binary files /dev/null and b/ch14-interact-with-pdf-files/practice_files/expense_reports/Expense report 1.pdf differ diff --git a/ch14-interact-with-pdf-files/practice_files/expense_reports/Expense report 2.pdf b/ch14-interact-with-pdf-files/practice_files/expense_reports/Expense report 2.pdf new file mode 100644 index 0000000..7a76b28 Binary files /dev/null and b/ch14-interact-with-pdf-files/practice_files/expense_reports/Expense report 2.pdf differ diff --git a/ch14-interact-with-pdf-files/practice_files/expense_reports/Expense report 3.pdf b/ch14-interact-with-pdf-files/practice_files/expense_reports/Expense report 3.pdf new file mode 100644 index 0000000..fdd4448 Binary files /dev/null and b/ch14-interact-with-pdf-files/practice_files/expense_reports/Expense report 3.pdf differ diff --git a/ch13-interact-with-pdf-files/practice_files/half and half.pdf b/ch14-interact-with-pdf-files/practice_files/half_and_half.pdf similarity index 100% rename from ch13-interact-with-pdf-files/practice_files/half and half.pdf rename to ch14-interact-with-pdf-files/practice_files/half_and_half.pdf diff --git a/ch14-interact-with-pdf-files/practice_files/merge1.pdf b/ch14-interact-with-pdf-files/practice_files/merge1.pdf new file mode 100644 index 0000000..75ef74f Binary files /dev/null and b/ch14-interact-with-pdf-files/practice_files/merge1.pdf differ diff --git a/ch14-interact-with-pdf-files/practice_files/merge2.pdf b/ch14-interact-with-pdf-files/practice_files/merge2.pdf new file mode 100644 index 0000000..7842d61 Binary files /dev/null and b/ch14-interact-with-pdf-files/practice_files/merge2.pdf differ diff --git a/ch14-interact-with-pdf-files/practice_files/merge3.pdf b/ch14-interact-with-pdf-files/practice_files/merge3.pdf new file mode 100644 index 0000000..ca6b494 Binary files /dev/null and b/ch14-interact-with-pdf-files/practice_files/merge3.pdf differ diff --git a/ch14-interact-with-pdf-files/practice_files/newsletter.pdf b/ch14-interact-with-pdf-files/practice_files/newsletter.pdf new file mode 100644 index 0000000..64b597e Binary files /dev/null and b/ch14-interact-with-pdf-files/practice_files/newsletter.pdf differ diff --git a/ch14-interact-with-pdf-files/practice_files/quarterly_report/full_report.pdf b/ch14-interact-with-pdf-files/practice_files/quarterly_report/full_report.pdf new file mode 100644 index 0000000..ae84a25 Binary files /dev/null and b/ch14-interact-with-pdf-files/practice_files/quarterly_report/full_report.pdf differ diff --git a/ch14-interact-with-pdf-files/practice_files/quarterly_report/report.pdf b/ch14-interact-with-pdf-files/practice_files/quarterly_report/report.pdf new file mode 100644 index 0000000..6ec79cb Binary files /dev/null and b/ch14-interact-with-pdf-files/practice_files/quarterly_report/report.pdf differ diff --git a/ch14-interact-with-pdf-files/practice_files/quarterly_report/toc.pdf b/ch14-interact-with-pdf-files/practice_files/quarterly_report/toc.pdf new file mode 100644 index 0000000..22cedaa Binary files /dev/null and b/ch14-interact-with-pdf-files/practice_files/quarterly_report/toc.pdf differ diff --git a/ch14-interact-with-pdf-files/practice_files/scrambled.pdf b/ch14-interact-with-pdf-files/practice_files/scrambled.pdf new file mode 100644 index 0000000..0761b0b Binary files /dev/null and b/ch14-interact-with-pdf-files/practice_files/scrambled.pdf differ diff --git a/ch14-interact-with-pdf-files/practice_files/split_and_rotate.pdf b/ch14-interact-with-pdf-files/practice_files/split_and_rotate.pdf new file mode 100644 index 0000000..a5c13f3 Binary files /dev/null and b/ch14-interact-with-pdf-files/practice_files/split_and_rotate.pdf differ diff --git a/ch14-interact-with-pdf-files/practice_files/top_secret.pdf b/ch14-interact-with-pdf-files/practice_files/top_secret.pdf new file mode 100644 index 0000000..64cfe11 Binary files /dev/null and b/ch14-interact-with-pdf-files/practice_files/top_secret.pdf differ diff --git a/ch13-interact-with-pdf-files/practice_files/ugly.pdf b/ch14-interact-with-pdf-files/practice_files/ugly.pdf similarity index 100% rename from ch13-interact-with-pdf-files/practice_files/ugly.pdf rename to ch14-interact-with-pdf-files/practice_files/ugly.pdf diff --git a/ch14-interact-with-pdf-files/practice_files/zen.pdf b/ch14-interact-with-pdf-files/practice_files/zen.pdf new file mode 100644 index 0000000..839484f Binary files /dev/null and b/ch14-interact-with-pdf-files/practice_files/zen.pdf differ diff --git a/ch14-sql-database-connections/1-use-sqlite.py b/ch14-sql-database-connections/1-use-sqlite.py deleted file mode 100644 index 483d0e9..0000000 --- a/ch14-sql-database-connections/1-use-sqlite.py +++ /dev/null @@ -1,33 +0,0 @@ -# 14.1 - Use SQLite -# Solutions to review exercises - -import sqlite3 - -# Create a temporary database connection in RAM -with sqlite3.connect(":memory:") as connection: - c = connection.cursor() - - # Exercise 1 - # Create a "Roster" table with Name, Species and IQ fields - c.execute("CREATE TABLE Roster(Name TEXT, Species TEXT, IQ INT)") - - # Exercise 2 - # Add some data into the database - roster_data = ( - ("Jean-Baptiste Zorg", "Human", 122), - ("Korben Dallas", "Meat Popsicle", 100), - ("Ak'not", "Mangalore", -5), - ) - c.executemany("INSERT INTO Roster VALUES(?, ?, ?)", roster_data) - - # Exercise 3 - # Update the Species of Korben Dallas to "Human" - c.execute( - "UPDATE Roster SET Species=? WHERE Name=?", ("Human", "Korben Dallas") - ) - - # Exercise 4 - # Display the names and IQs of everyone classified as Human - c.execute("SELECT Name, IQ FROM Roster WHERE Species = 'Human'") - for row in c.fetchall(): - print(row) diff --git a/ch15-sql-database-connections/1-use-sqlite.py b/ch15-sql-database-connections/1-use-sqlite.py new file mode 100644 index 0000000..d5cfc09 --- /dev/null +++ b/ch15-sql-database-connections/1-use-sqlite.py @@ -0,0 +1,33 @@ +# 15.1 - Use SQLite +# Solutions to review exercises + +import sqlite3 + +# Create a temporary database connection in RAM +with sqlite3.connect(":memory:") as connection: + c = connection.cursor() + + # Exercise 1 + # Create a "Roster" table with Name, Species and Age fields + c.execute("CREATE TABLE Roster(Name TEXT, Species TEXT, Age INT)") + + # Exercise 2 + # Add some data into the database + roster_data = ( + ("Benjamin Sisko", "Human", 40), + ("Jadzia Dax", "Trill", 300), + ("Kira Nerys", "Bajoran", 29), + ) + c.executemany("INSERT INTO Roster VALUES(?, ?, ?)", roster_data) + + # Exercise 3 + # Update the Name of Jadzia Dax to "Ezri Dax" + c.execute( + "UPDATE Roster SET Name=? WHERE Name=?", ("Ezri Dax", "Jadzia Dax") + ) + + # Exercise 4 + # Display the names and ages of everyone classified as Bajoran + c.execute("SELECT Name, Age FROM Roster WHERE Species = 'Bajoran'") + for row in c.fetchall(): + print(row) diff --git a/ch15-interacting-with-the-web/1-scrape-and-parse-text-from-websites.py b/ch16-interacting-with-the-web/1-scrape-and-parse-text-from-websites.py similarity index 95% rename from ch15-interacting-with-the-web/1-scrape-and-parse-text-from-websites.py rename to ch16-interacting-with-the-web/1-scrape-and-parse-text-from-websites.py index 7f33108..851515d 100644 --- a/ch15-interacting-with-the-web/1-scrape-and-parse-text-from-websites.py +++ b/ch16-interacting-with-the-web/1-scrape-and-parse-text-from-websites.py @@ -1,4 +1,4 @@ -# 15.1 - Scrape and Parse Text From Websites +# 16.1 - Scrape and Parse Text From Websites # Solutions to review exercises from urllib.request import urlopen diff --git a/ch15-interacting-with-the-web/2-use-an-html-parser-to-scrape-websites.py b/ch16-interacting-with-the-web/2-use-an-html-parser-to-scrape-websites.py similarity index 83% rename from ch15-interacting-with-the-web/2-use-an-html-parser-to-scrape-websites.py rename to ch16-interacting-with-the-web/2-use-an-html-parser-to-scrape-websites.py index 1b9c20b..4781c1f 100644 --- a/ch15-interacting-with-the-web/2-use-an-html-parser-to-scrape-websites.py +++ b/ch16-interacting-with-the-web/2-use-an-html-parser-to-scrape-websites.py @@ -1,4 +1,4 @@ -# 15.2 - Use an HTML Parser to Scrape Websites +# 16.2 - Use an HTML Parser to Scrape Websites # Solutions to review exercises # Make sure BeautifulSoup is installed first with: @@ -13,7 +13,7 @@ address = base_URL + "/profiles" html_page = urlopen(address) html_text = html_page.read().decode("utf-8") -soup = BeautifulSoup(html_text, features="html.parser") +soup = BeautifulSoup(html_text, "html.parser") # Exercise 2 # Parse out all the values of the page links @@ -26,5 +26,5 @@ # Display the text in the HTML page of each link link_page = urlopen(link_address) link_text = link_page.read().decode("utf-8") - link_soup = BeautifulSoup(link_text, features="html.parser") + link_soup = BeautifulSoup(link_text, "html.parser") print(link_soup.get_text()) diff --git a/ch15-interacting-with-the-web/3-interact-with-html-forms.py b/ch16-interacting-with-the-web/3-interact-with-html-forms.py similarity index 97% rename from ch15-interacting-with-the-web/3-interact-with-html-forms.py rename to ch16-interacting-with-the-web/3-interact-with-html-forms.py index c1741e3..26eaf2a 100644 --- a/ch15-interacting-with-the-web/3-interact-with-html-forms.py +++ b/ch16-interacting-with-the-web/3-interact-with-html-forms.py @@ -1,4 +1,4 @@ -# 15.3 - Interact with HTML Forms +# 16.3 - Interact with HTML Forms # Solutions to review exercises # Make sure BeautifulSoup is installed first with: diff --git a/ch15-interacting-with-the-web/4-interact-with-websites-in-realtime.py b/ch16-interacting-with-the-web/4-interact-with-websites-in-realtime.py similarity index 95% rename from ch15-interacting-with-the-web/4-interact-with-websites-in-realtime.py rename to ch16-interacting-with-the-web/4-interact-with-websites-in-realtime.py index 60ac5ee..90767c1 100644 --- a/ch15-interacting-with-the-web/4-interact-with-websites-in-realtime.py +++ b/ch16-interacting-with-the-web/4-interact-with-websites-in-realtime.py @@ -1,4 +1,4 @@ -# 15.4 - Interact With Websites in Real-Time +# 16.4 - Interact With Websites in Real-Time # Solutions to review exercise # Make sure BeautifulSoup is installed first with: diff --git a/ch17-graphical-user-interfaces/4-control-layout-with-geometry-managers.py b/ch17-graphical-user-interfaces/4-control-layout-with-geometry-managers.py deleted file mode 100644 index 73d0f4e..0000000 --- a/ch17-graphical-user-interfaces/4-control-layout-with-geometry-managers.py +++ /dev/null @@ -1,119 +0,0 @@ -# 17.4 - Control Layout With Geometry Managers -# Review Exercise #2 - -# NOTE: The first exercise in this section is instructional and does -# not require a solution to be shown here. For this reason, only the -# solution to the second exercise is presented. - -import tkinter as tk - - -# Create a new window with the title "Address Entry Form" -window = tk.Tk() -window.title("Address Entry Form") - -# Create a new frame `form_frame` to contain the Label -# and Entry widgets for entering address information. -form_frame = tk.Frame() -# Give the frame a sunken border effect -form_frame["relief"] = tk.SUNKEN -form_frame["borderwidth"] = 3 -# Pack the frame into the window -form_frame.pack() - -# Create the Label and Entry widgets for "First Name" -fname_label = tk.Label(master=form_frame) -fname_label["text"] = "First Name:" -fname_entry = tk.Entry(master=form_frame) -fname_entry["width"] = 50 -# Use the grid geometry manager to place the Label and -# Entry widgets in the first and second columns of the -# first row of the grid -fname_label.grid(row=0, column=0, sticky=tk.E) -fname_entry.grid(row=0, column=1) - -# Create the Label and Entry widgets for "Last Name" -lname_label = tk.Label(master=form_frame) -lname_label["text"] = "Last Name:" -lname_entry = tk.Entry(master=form_frame) -lname_entry["width"] = 50 -# Place the widgets in the second row of the grid -lname_label.grid(row=1, column=0, sticky=tk.E) -lname_entry.grid(row=1, column=1) - -# Create the Label and Entry widgets for "Address Line 1" -address1_entry_label = tk.Label(master=form_frame) -address1_entry_label["text"] = "Address Line 1:" -address1_entry = tk.Entry(master=form_frame) -address1_entry["width"] = 50 -# Place the widgets in the third row of the grid -address1_entry_label.grid(row=2, column=0, sticky=tk.E) -address1_entry.grid(row=2, column=1) - -# Create the Label and Entry widgets for "Address Line 2" -address2_entry_label = tk.Label(master=form_frame) -address2_entry_label["text"] = "Address Line 2:" -address2_entry = tk.Entry(master=form_frame) -address2_entry["width"] = 50 -# Place the widgets in the fourth row of the grid -address2_entry_label.grid(row=3, column=0, sticky=tk.E) -address2_entry.grid(row=3, column=1) -address2_entry["width"] = 50 - -# Create the Label and Entry widgets for "City" -city_entry_label = tk.Label(master=form_frame) -city_entry_label["text"] = "City:" -city_entry = tk.Entry(master=form_frame) -city_entry["width"] = 50 -# Place the widgets in the fifth row of the grid -city_entry_label.grid(row=4, column=0, sticky=tk.E) -city_entry.grid(row=4, column=1) - -# Create the Label and Entry widgets for "State/Province" -state_entry_label = tk.Label(master=form_frame) -state_entry_label["text"] = "State/Province:" -state_entry = tk.Entry(master=form_frame) -state_entry["width"] = 50 -# Place the widgets in the sixth row of the grid -state_entry_label.grid(row=5, column=0, sticky=tk.E) -state_entry.grid(row=5, column=1) - -# Create the Label and Entry widgets for "Postal Code" -postal_entry_label = tk.Label(master=form_frame) -postal_entry_label["text"] = "Postal Code:" -postal_entry = tk.Entry(master=form_frame) -postal_entry["width"] = 50 -# Place the widgets in the seventh row of the grid -postal_entry_label.grid(row=6, column=0, sticky=tk.E) -postal_entry.grid(row=6, column=1) - -# Create the Label and Entry widgets for "Country" -country_entry_label = tk.Label(master=form_frame) -country_entry_label["text"] = "Country:" -country_entry = tk.Entry(master=form_frame) -country_entry["width"] = 50 -# Place the widgets in the eight row of the grid -country_entry_label.grid(row=7, column=0, sticky=tk.E) -country_entry.grid(row=7, column=1) - -# Create a new frame `button_frame` to contain the -# Submit and Clear buttons. This frame fills the -# whole window in the horizontal direction and has -# 5 pixels of horizontal and vertical padding. -button_frame = tk.Frame() -button_frame.pack(fill=tk.X, ipadx=5, ipady=5) - -# Create the "Submit" button and pack it to the -# right side of `button_frame` -submit_button = tk.Button(master=button_frame) -submit_button["text"] = "Submit" -submit_button.pack(side=tk.RIGHT, padx=10, ipadx=10) - -# Create the "Clear" button and pack it to the -# right side of `button_frame` -clear_button = tk.Button(master=button_frame) -clear_button["text"] = "Clear" -clear_button.pack(side=tk.RIGHT, ipadx=10) - -# Start the application -window.mainloop() diff --git a/ch16-scientific-computing-and-graphing/1-use-numpy-for-matrix-manipulation.py b/ch17-scientific-computing-and-graphing/1-use-numpy-for-matrix-manipulation.py similarity index 90% rename from ch16-scientific-computing-and-graphing/1-use-numpy-for-matrix-manipulation.py rename to ch17-scientific-computing-and-graphing/1-use-numpy-for-matrix-manipulation.py index 839fc31..20a3307 100644 --- a/ch16-scientific-computing-and-graphing/1-use-numpy-for-matrix-manipulation.py +++ b/ch17-scientific-computing-and-graphing/1-use-numpy-for-matrix-manipulation.py @@ -1,4 +1,4 @@ -# 16.2 - Use matplotlib for Plotting Graphs +# 17.2 - Use matplotlib for Plotting Graphs # Solution to review exercise #2 # Setup @@ -17,7 +17,7 @@ # Exercise 3 # Square every entry and save in a new matrix -second_matrix = first_matrix ** 2 +second_matrix = first_matrix**2 # Exercise 4 # Put first_matrix on top of second_matrix diff --git a/ch16-scientific-computing-and-graphing/2-use-matplotlib-for-plotting-graphs.py b/ch17-scientific-computing-and-graphing/2-use-matplotlib-for-plotting-graphs.py similarity index 82% rename from ch16-scientific-computing-and-graphing/2-use-matplotlib-for-plotting-graphs.py rename to ch17-scientific-computing-and-graphing/2-use-matplotlib-for-plotting-graphs.py index 4fdd9f9..3afa8eb 100644 --- a/ch16-scientific-computing-and-graphing/2-use-matplotlib-for-plotting-graphs.py +++ b/ch17-scientific-computing-and-graphing/2-use-matplotlib-for-plotting-graphs.py @@ -1,4 +1,4 @@ -# 16.3 - Use matplotlib for Plotting Graphs +# 17.3 - Use matplotlib for Plotting Graphs # Solution to review exercise #2 # Graph pirates versus global warming @@ -8,8 +8,7 @@ import os # Change `path` to actual path on your system -path = "C:/Real Python/python-basics-exercises/ch16-scientific-computing-and-graphing/\ -practice_files" +path = "C:/Real Python/python-basics-exercises/ch16-scientific-computing-and-graphing/practice_files" years = [] temperatures = [] @@ -19,9 +18,9 @@ my_file_reader = csv.reader(my_file) next(my_file_reader) # skip header row for year, temperature, pirate_count in my_file_reader: - years.append(year) - temperatures.append(temperature) - pirates.append(pirate_count) + years.append(int(year)) + temperatures.append(float(temperature)) + pirates.append(int(pirate_count)) plt.plot(pirates, temperatures, "r-o") diff --git a/ch16-scientific-computing-and-graphing/practice_files/pirates.csv b/ch17-scientific-computing-and-graphing/practice_files/pirates.csv similarity index 100% rename from ch16-scientific-computing-and-graphing/practice_files/pirates.csv rename to ch17-scientific-computing-and-graphing/practice_files/pirates.csv diff --git a/ch18-graphical-user-interfaces/1-add-gui-elements-with-easygui.py b/ch18-graphical-user-interfaces/1-add-gui-elements-with-easygui.py new file mode 100644 index 0000000..bd09fb9 --- /dev/null +++ b/ch18-graphical-user-interfaces/1-add-gui-elements-with-easygui.py @@ -0,0 +1,12 @@ +# 18.1 - Add GUI Elements with EasyGUI +# Review Exercises + +import easygui as gui + + +# Exercise #1 +gui.msgbox(msg="Warning!", title="Watch out!", ok_button="I'll be careful") + + +# Exercise #2 +gui.enterbox(msg="What is your name?") diff --git a/ch17-graphical-user-interfaces/6-challenge.py b/ch18-graphical-user-interfaces/10-challenge-return-of-the-poet.py similarity index 99% rename from ch17-graphical-user-interfaces/6-challenge.py rename to ch18-graphical-user-interfaces/10-challenge-return-of-the-poet.py index 592fd19..b7af37f 100644 --- a/ch17-graphical-user-interfaces/6-challenge.py +++ b/ch18-graphical-user-interfaces/10-challenge-return-of-the-poet.py @@ -1,4 +1,4 @@ -# 17.6 - Challenge: Return of the Poet +# 18.10 - Challenge: Return of the Poet # Solution to challenge # Please note that there are many ways to solve this challenge. The code diff --git a/ch17-graphical-user-interfaces/1-add-gui-elements-with-easy-gui.py b/ch18-graphical-user-interfaces/2-example-app-pdf-page-rotator.py similarity index 88% rename from ch17-graphical-user-interfaces/1-add-gui-elements-with-easy-gui.py rename to ch18-graphical-user-interfaces/2-example-app-pdf-page-rotator.py index 3f2351b..3399e32 100644 --- a/ch17-graphical-user-interfaces/1-add-gui-elements-with-easy-gui.py +++ b/ch18-graphical-user-interfaces/2-example-app-pdf-page-rotator.py @@ -1,9 +1,5 @@ -# 17.1 - Add GUI Elements With EasyGUI -# Review Exercise #4 - -# NOTE: The first three exercises in this section are instructional -# and do not require solutions to be shown here. For this reason, -# only the solution to the fourth exercise is presented. +# 18.2 - Example App: PDF Page Rotator +# Review Exercise #1 import easygui as gui from PyPDF2 import PdfFileReader, PdfFileWriter diff --git a/ch17-graphical-user-interfaces/2-challenge.py b/ch18-graphical-user-interfaces/3-challenge-use-gui-elements-to-help-a-user-modify-files.py similarity index 54% rename from ch17-graphical-user-interfaces/2-challenge.py rename to ch18-graphical-user-interfaces/3-challenge-use-gui-elements-to-help-a-user-modify-files.py index b941f19..128dd6a 100644 --- a/ch17-graphical-user-interfaces/2-challenge.py +++ b/ch18-graphical-user-interfaces/3-challenge-use-gui-elements-to-help-a-user-modify-files.py @@ -1,4 +1,4 @@ -# 17.2 - Challenge: Use GUI Elements to Help a User Modify Files +# 18.3 - Challenge: Use GUI Elements to Help a User Modify Files # Solution to challenge # save part of a PDF based on a user-supplied page range using a GUI @@ -6,25 +6,28 @@ import easygui as gui from PyPDF2 import PdfFileReader, PdfFileWriter -# let the user choose an input file +# 1. Ask the user to select a PDF file to open. input_file_path = gui.fileopenbox("", "Select a PDF to trim...", "*.pdf") -if input_file_path is None: # exit on "Cancel" - exit() -# get the page length of the input file -input_file = PdfFileReader(input_file_path) -total_pages = input_file.getNumPages() +# 2. If no PDF file is chosen, exit the program. +if input_file_path is None: + exit() -# let the user choose a beginning page +# 3. Ask for a starting page number. page_start = gui.enterbox( "Enter the number of the first page to use:", "Where to begin?" ) -if page_start is None: # exit on "Cancel" + +# 4. If the user does not enter a starting page number, exit the program. +if page_start is None: exit() -# check for possible problems and try again: -# 1) input page number isn't a (non-negative) digit -# or 2) input page number is 0 -# or 3) page number is greater than total number of pages + +# 5. Valid page numbers are positive integers. If the user enters an invalid page number: +# - Warn the user that the entry is invalid +# - Return to step 3. +# This solution also checks that the starting page number is not beyond the last page of the PDF!# 3. Asl for a starting page number. +input_file = PdfFileReader(input_file_path) # Open the PDF +total_pages = input_file.getNumPages() # Get the total number of pages while ( not page_start.isdigit() or page_start == "0" @@ -37,17 +40,20 @@ if page_start is None: # exit on "Cancel" exit() -# let the user choose an ending page +# 6. Ask for an ending page number page_end = gui.enterbox( "Enter the number of the last page to use:", "Where to end?" ) + +# 7. If the user does not enter and ending page number, exit the program. if page_end is None: # exit on "Cancel" exit() -# check for possible problems and try again: -# 1) input page number isn't a (non-negative) digit -# or 2) input page number is 0 -# or 3) input page number is greater than total number of pages -# or 4) input page number for end is less than page number for beginning + +# 8. If the user enters an invalid page number: +# - Warn the user that the entry is invalid +# - Return to step 6. +# This solution also check that the ending page number is not less than the starting +# page number, and that it is not greater than the last page in the PDF while ( not page_end.isdigit() or page_end == "0" @@ -61,8 +67,16 @@ if page_end is None: # exit on "Cancel" exit() -# let the user choose an output file name +# 9. Ask for the location to save the extracted pages output_file_path = gui.filesavebox("", "Save the trimmed PDF as...", "*.pdf") + +# 10. If the user does not select a save location, exit the program. +if output_file_path is None: + exit() + +# 11. If the chosen save location is the same as the input file path: +# - Warn the user that they can not overwrite the input file. +# - Return the step 9. while input_file_path == output_file_path: # cannot use same file as input gui.msgbox( "Cannot overwrite original file!", "Please choose another file..." @@ -70,12 +84,12 @@ output_file_path = gui.filesavebox( "", "Save the trimmed PDF as...", "*.pdf" ) -if output_file_path is None: - exit() # exit on "Cancel" + if output_file_path is None: + exit() -# read in file, write new file with trimmed page range and save the new file +# 12. Perform the page extraction output_PDF = PdfFileWriter() -# subtract 1 from supplied start page number to get correct index value + for page_num in range(int(page_start) - 1, int(page_end)): page = input_file.getPage(page_num) output_PDF.addPage(page) diff --git a/ch18-graphical-user-interfaces/4-introduction-to-tkinter.py b/ch18-graphical-user-interfaces/4-introduction-to-tkinter.py new file mode 100644 index 0000000..8f612c8 --- /dev/null +++ b/ch18-graphical-user-interfaces/4-introduction-to-tkinter.py @@ -0,0 +1,24 @@ +# 18.4 - Introduction to Tkinter +# Review exercises + +import tkinter as tk + +# Exercise 1 +window = tk.Tk() +label = tk.Label(text="GUIs are great!") +label.pack() +window.mainloop() + + +# Exercise 2 +window = tk.Tk() +label = tk.Label(text="Python rocks!") +label.pack() +window.mainloop() + + +# Exercise 3 +window = tk.Tk() +label = tk.Label(text="Engage!") +label.pack() +window.mainloop() diff --git a/ch18-graphical-user-interfaces/5-working-with-widgets.py b/ch18-graphical-user-interfaces/5-working-with-widgets.py new file mode 100644 index 0000000..3074995 --- /dev/null +++ b/ch18-graphical-user-interfaces/5-working-with-widgets.py @@ -0,0 +1,25 @@ +# 18.4 - Introduction to Tkinter +# Review exercises + +import tkinter as tk + + +# Exercise 1 asks you to recreate all the windows in the chapter. +# The source code for each window can be found in the chapter text. + + +# Exercise 2 +window = tk.Tk() +button = tk.Button( + width=50, height=25, bg="white", fg="blue", text="Click here" +) +button.pack() +window.mainloop() + + +# Exercise 3 +window = tk.Tk() +entry = tk.Entry(width=40) +entry.insert(0, "What is your name?") +entry.pack() +window.mainloop() diff --git a/ch18-graphical-user-interfaces/6-control-layout-with-geometry-managers.py b/ch18-graphical-user-interfaces/6-control-layout-with-geometry-managers.py new file mode 100644 index 0000000..5c2869a --- /dev/null +++ b/ch18-graphical-user-interfaces/6-control-layout-with-geometry-managers.py @@ -0,0 +1,97 @@ +# 18.6 - Control Layout With Geometry Managers +# Review Exercise #2 + +# NOTE: The first exercise in this section is instructional and does +# not require a solution to be shown here. For this reason, only the +# solution to the second exercise is presented. + +import tkinter as tk + + +# Create a new window with the title "Address Entry Form" +window = tk.Tk() +window.title("Address Entry Form") + +# Create a new frame `frm_form` to contain the Label +# and Entry widgets for entering address information. +frm_form = tk.Frame(relief=tk.SUNKEN, borderwidth=3) +# Pack the frame into the window +frm_form.pack() + +# Create the Label and Entry widgets for "First Name" +lbl_first_name = tk.Label(master=frm_form, text="First Name:") +ent_first_name = tk.Entry(master=frm_form, width=50) +# Use the grid geometry manager to place the Label and +# Entry widgets in the first and second columns of the +# first row of the grid +lbl_first_name.grid(row=0, column=0, sticky="e") +ent_first_name.grid(row=0, column=1) + +# Create the Label and Entry widgets for "Last Name" +lbl_last_name = tk.Label(master=frm_form, text="Last Name:") +ent_last_name = tk.Entry(master=frm_form, width=50) +# Place the widgets in the second row of the grid +lbl_last_name.grid(row=1, column=0, sticky="e") +ent_last_name.grid(row=1, column=1) + +# Create the Label and Entry widgets for "Address Line 1" +lbl_address1 = tk.Label(master=frm_form, text="Address Line 1:") +ent_address1 = tk.Entry(master=frm_form, width=50) +# Place the widgets in the third row of the grid +lbl_address1.grid(row=2, column=0, sticky="e") +ent_address1.grid(row=2, column=1) + +# Create the Label and Entry widgets for "Address Line 2" +lbl_address2 = tk.Label(master=frm_form, text="Address Line 2:") +ent_address2 = tk.Entry(master=frm_form, width=5) +# Place the widgets in the fourth row of the grid +lbl_address2.grid(row=3, column=0, sticky=tk.E) +ent_address2.grid(row=3, column=1) + +# Create the Label and Entry widgets for "City" +lbl_city = tk.Label(master=frm_form, text="City:") +ent_city = tk.Entry(master=frm_form, width=50) +# Place the widgets in the fifth row of the grid +lbl_city.grid(row=4, column=0, sticky=tk.E) +ent_city.grid(row=4, column=1) + +# Create the Label and Entry widgets for "State/Province" +lbl_state = tk.Label(master=frm_form, text="State/Province:") +ent_state = tk.Entry(master=frm_form, width=50) +# Place the widgets in the sixth row of the grid +lbl_state.grid(row=5, column=0, sticky=tk.E) +ent_state.grid(row=5, column=1) + +# Create the Label and Entry widgets for "Postal Code" +lbl_postal_code = tk.Label(master=frm_form, text="Postal Code:") +ent_postal_code = tk.Entry(master=frm_form, width=50) +# Place the widgets in the seventh row of the grid +lbl_postal_code.grid(row=6, column=0, sticky=tk.E) +ent_postal_code.grid(row=6, column=1) + +# Create the Label and Entry widgets for "Country" +lbl_country = tk.Label(master=frm_form, text="Country:") +ent_country = tk.Entry(master=frm_form, width=50) +# Place the widgets in the eight row of the grid +lbl_country.grid(row=7, column=0, sticky=tk.E) +ent_country.grid(row=7, column=1) + +# Create a new frame `frm_buttons` to contain the +# Submit and Clear buttons. This frame fills the +# whole window in the horizontal direction and has +# 5 pixels of horizontal and vertical padding. +frm_buttons = tk.Frame() +frm_buttons.pack(fill=tk.X, ipadx=5, ipady=5) + +# Create the "Submit" button and pack it to the +# right side of `frm_buttons` +btn_submit = tk.Button(master=frm_buttons, text="Submit") +btn_submit.pack(side=tk.RIGHT, padx=10, ipadx=10) + +# Create the "Clear" button and pack it to the +# right side of `frm_buttons` +btn_clear = tk.Button(master=frm_buttons, text="Clear") +btn_clear.pack(side=tk.RIGHT, ipadx=10) + +# Start the application +window.mainloop() diff --git a/ch18-graphical-user-interfaces/7-make-your-applications-interactive.py b/ch18-graphical-user-interfaces/7-make-your-applications-interactive.py new file mode 100644 index 0000000..6f2a39a --- /dev/null +++ b/ch18-graphical-user-interfaces/7-make-your-applications-interactive.py @@ -0,0 +1,42 @@ +# 18.7 - Make Your Applications Interactive +# Review Exercises + + +# Exercise 1 +import random +import tkinter as tk + + +def change_bg_color(): + color = random.choice( + ["red", "orange", "yellow", "blue", "green", "indigo", "violet"] + ) + button["bg"] = color + + +window = tk.Tk() +button = tk.Button(text="Click me", command=change_bg_color) +button.pack() +window.mainloop() + + +# Exercise 2 +import random +import tkinter as tk + + +def roll(): + lbl_result["text"] = str(random.randint(1, 6)) + + +window = tk.Tk() +window.columnconfigure(0, minsize=150) +window.rowconfigure([0, 1], minsize=50) + +btn_roll = tk.Button(text="Roll", command=roll) +lbl_result = tk.Label() + +btn_roll.grid(row=0, column=0, sticky="nsew") +lbl_result.grid(row=1, column=0) + +window.mainloop() pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy