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-pick-apart-your-users-input.py b/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-pick-apart-your-users-input.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/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/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/5-challenge-track-your-investments.py b/ch06-functions-and-loops/5-challenge-track-your-investments.py index 1ecabaf..0041382 100644 --- a/ch06-functions-and-loops/5-challenge-track-your-investments.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/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/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 157536d..250144b 100644 --- a/ch08-conditional-logic/6-recover-from-errors.py +++ b/ch08-conditional-logic/6-recover-from-errors.py @@ -15,7 +15,7 @@ # Exercise 2 -# Print character and specifid index in string +# Print character and specified index in string input_string = input("Enter a string: ") 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 29de41f..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,7 +6,7 @@ # Exercise 1 -# Write a function that simulatee the roll of a die. +# Write a function that simulates the roll of a die. def roll(): """Return random integer between 1 and 6""" return randint(1, 6) 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 index c8cf9e5..04a3cb5 100644 --- a/ch08-conditional-logic/8a-challenge-simulate-a-coin-toss-experiment.py +++ b/ch08-conditional-logic/8a-challenge-simulate-a-coin-toss-experiment.py @@ -42,7 +42,7 @@ def coin_flip(): # coin_flip() returned "tails" on the first flip. # Increment the number of flips by 1 flips = flips + 1 - while coin_flip == "tails": + while coin_flip() == "tails": # Keep incrementing the total number of flips # until "heads" is returned by coin_flip() flips = flips + 1 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 index a01a8ce..9f3effe 100644 --- a/ch08-conditional-logic/8b-challenge-simulate-a-coin-toss-experiment.py +++ b/ch08-conditional-logic/8b-challenge-simulate-a-coin-toss-experiment.py @@ -31,7 +31,7 @@ def coin_flip(): first_flip = coin_flip() flips = flips + 1 # Continue flipping the coin and updating the tally until - # a different result is returned by coin_flips() + # 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 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 index a652fbb..753e91d 100644 --- a/ch08-conditional-logic/8c-challenge-simulate-a-coin-toss-experiment.py +++ b/ch08-conditional-logic/8c-challenge-simulate-a-coin-toss-experiment.py @@ -12,26 +12,38 @@ # 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 +import random def single_trial(): - toss = randint(0, 1) - total_flips = 1 + """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. - while toss == randint(0, 1): - total_flips += 1 - toss = randint(0, 1) + # 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 - total_flips += 1 - return total_flips + # 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 += single_trial() + total = total + single_trial() return total / num_trials -print(f"The average number of coin flips was {flip_trial_avg(10000)}") +print(f"The average number of coin flips was {flip_trial_avg(10_000)}") diff --git a/ch08-conditional-logic/9-challenge-simulate-an-election.py b/ch08-conditional-logic/9-challenge-simulate-an-election.py deleted file mode 100644 index 6a295ab..0000000 --- a/ch08-conditional-logic/9-challenge-simulate-an-election.py +++ /dev/null @@ -1,35 +0,0 @@ -# 8.9 - 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/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/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/ch09-lists-and-dictionaries/4-challenge-list-of-lists.py b/ch09-lists-and-dictionaries/4-challenge-list-of-lists.py deleted file mode 100644 index 893dc7f..0000000 --- a/ch09-lists-and-dictionaries/4-challenge-list-of-lists.py +++ /dev/null @@ -1,58 +0,0 @@ -# 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(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/6-store-relationships-in-dictionaries.py b/ch09-lists-and-dictionaries/6-store-relationships-in-dictionaries.py deleted file mode 100644 index 3f8eb52..0000000 --- a/ch09-lists-and-dictionaries/6-store-relationships-in-dictionaries.py +++ /dev/null @@ -1,50 +0,0 @@ -# 9.6 - 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/7-challenge-capital-city-loop.py b/ch09-lists-and-dictionaries/7-challenge-capital-city-loop.py deleted file mode 100644 index ed87f91..0000000 --- a/ch09-lists-and-dictionaries/7-challenge-capital-city-loop.py +++ /dev/null @@ -1,23 +0,0 @@ -# 9.7 - 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/8a-challenge-cats-with-hats.py b/ch09-lists-and-dictionaries/8a-challenge-cats-with-hats.py deleted file mode 100644 index 6b268b0..0000000 --- a/ch09-lists-and-dictionaries/8a-challenge-cats-with-hats.py +++ /dev/null @@ -1,21 +0,0 @@ -# 9.8 - 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-and-dictionaries/1-tuples-are-immutable-sequences.py b/ch09-lists-tuples-and-dictionaries/1-tuples-are-immutable-sequences.py similarity index 90% rename from ch09-lists-and-dictionaries/1-tuples-are-immutable-sequences.py rename to ch09-lists-tuples-and-dictionaries/1-tuples-are-immutable-sequences.py index 96d6ad8..1b0bcf0 100644 --- a/ch09-lists-and-dictionaries/1-tuples-are-immutable-sequences.py +++ b/ch09-lists-tuples-and-dictionaries/1-tuples-are-immutable-sequences.py @@ -13,7 +13,7 @@ # Exercise 3 -# unpack the tuple into three string and display them +# Unpack the tuple into three strings and display them position1, position2, position3 = cardinal_numbers print(position1) print(position2) @@ -23,7 +23,7 @@ # Create a tuple containing the letters of your name from a string my_name = tuple("David") -# Exercide 5 +# Exercise 5 # Check whether or not x is in my_name print("x" in my_name) diff --git a/ch09-lists-and-dictionaries/2-lists-are-mutable-sequences.py b/ch09-lists-tuples-and-dictionaries/2-lists-are-mutable-sequences.py similarity index 72% rename from ch09-lists-and-dictionaries/2-lists-are-mutable-sequences.py rename to ch09-lists-tuples-and-dictionaries/2-lists-are-mutable-sequences.py index e62f33d..6151b2b 100644 --- a/ch09-lists-and-dictionaries/2-lists-are-mutable-sequences.py +++ b/ch09-lists-tuples-and-dictionaries/2-lists-are-mutable-sequences.py @@ -8,8 +8,8 @@ # Exercise 2 -# Append the string "broccolo" to the food list using .append() -food.append("brocolli") +# Append the string "broccoli" to the food list using .append() +food.append("broccoli") # Exercise 3 @@ -38,3 +38,11 @@ # 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-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 similarity index 77% rename from ch09-lists-and-dictionaries/3-nesting-sorting-and-copying-lists-and-tuples.py rename to ch09-lists-tuples-and-dictionaries/3-nesting-sorting-and-copying-lists-and-tuples.py index f9a3121..a7604b3 100644 --- a/ch09-lists-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 @@ -8,9 +8,11 @@ # Exercise 2 -# Loop over data a print the sum of each nested tuple -for i in range(len(data)): - print(f"Row {i+1} sum: {data[i][0] + data[i][1]}") +# 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 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/5-challenge-wax-poetic.py b/ch09-lists-tuples-and-dictionaries/5-challenge-wax-poetic.py similarity index 56% rename from ch09-lists-and-dictionaries/5-challenge-wax-poetic.py rename to ch09-lists-tuples-and-dictionaries/5-challenge-wax-poetic.py index b6384a8..7b9ed8a 100644 --- a/ch09-lists-and-dictionaries/5-challenge-wax-poetic.py +++ b/ch09-lists-tuples-and-dictionaries/5-challenge-wax-poetic.py @@ -4,7 +4,7 @@ # 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 9fcc469..8eca70b 100644 --- a/ch09-lists-and-dictionaries/capitals.py +++ b/ch09-lists-tuples-and-dictionaries/7-challenge-capital-city-loop.py @@ -1,8 +1,7 @@ -# 10.6 - Challenge: Capital City Loop - -# This is the capitals.py module to be used in the solution to the -# Capital City Loop challenge. +# 9.7 - Challenge: Capital City Loop +# Solution to challenge +import random capitals_dict = { "Alabama": "Montgomery", @@ -56,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/8b-challenge-cats-with-hats.py b/ch09-lists-tuples-and-dictionaries/9b-challenge-cats-with-hats.py similarity index 93% rename from ch09-lists-and-dictionaries/8b-challenge-cats-with-hats.py rename to ch09-lists-tuples-and-dictionaries/9b-challenge-cats-with-hats.py index 3b0d0da..fad7176 100644 --- a/ch09-lists-and-dictionaries/8b-challenge-cats-with-hats.py +++ b/ch09-lists-tuples-and-dictionaries/9b-challenge-cats-with-hats.py @@ -1,4 +1,4 @@ -# 9.8 - Challenge: Cats With Hats +# 9.9 - Challenge: Cats With Hats # Alternative solution to challenge diff --git a/ch09-lists-and-dictionaries/8c-challenge-cats-with-hats.py b/ch09-lists-tuples-and-dictionaries/9c-challenge-cats-with-hats.py similarity index 60% rename from ch09-lists-and-dictionaries/8c-challenge-cats-with-hats.py rename to ch09-lists-tuples-and-dictionaries/9c-challenge-cats-with-hats.py index dff65fd..1e4c885 100644 --- a/ch09-lists-and-dictionaries/8c-challenge-cats-with-hats.py +++ b/ch09-lists-tuples-and-dictionaries/9c-challenge-cats-with-hats.py @@ -1,20 +1,27 @@ -# 9.8 - 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 fc9a395..36efe49 100644 --- a/ch10-primer-on-oop/3-inherit-from-other-classes.py +++ b/ch10-primer-on-oop/3-inherit-from-other-classes.py @@ -39,5 +39,9 @@ class Square(Rectangle): def __init__(self, side_length): super().__init__(side_length, side_length) + square = Square(4) -print(square.area()) +print(square.area()) # 16 + +square.width = 5 # Modifies .width but not .length +print(square.area()) # 20 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-use-pattern-matching-to-delete-files.py b/ch11-file-input-and-output/3-challenge-use-pattern-matching-to-delete-files.py deleted file mode 100644 index 815a1c1..0000000 --- a/ch11-file-input-and-output/3-challenge-use-pattern-matching-to-delete-files.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-create-a-high-scores-list-from-csv-data.py b/ch11-file-input-and-output/5-challenge-create-a-high-scores-list-from-csv-data.py deleted file mode 100644 index be2aace..0000000 --- a/ch11-file-input-and-output/5-challenge-create-a-high-scores-list-from-csv-data.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-split-a-csv-file.py b/ch11-file-input-and-output/6-challenge-split-a-csv-file.py deleted file mode 100644 index c040899..0000000 --- a/ch11-file-input-and-output/6-challenge-split-a-csv-file.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-add-a-cover-sheet-to-a-pdf-file.py b/ch13-interact-with-pdf-files/3-challenge-add-a-cover-sheet-to-a-pdf-file.py deleted file mode 100644 index 3cbae00..0000000 --- a/ch13-interact-with-pdf-files/3-challenge-add-a-cover-sheet-to-a-pdf-file.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 95% 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 8b70919..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: 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 92% 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 92f778b..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 = [] 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-return-of-the-poet.py b/ch18-graphical-user-interfaces/10-challenge-return-of-the-poet.py similarity index 99% rename from ch17-graphical-user-interfaces/6-challenge-return-of-the-poet.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-return-of-the-poet.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-use-gui-elements-to-help-a-user-modify-files.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-use-gui-elements-to-help-a-user-modify-files.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-use-gui-elements-to-help-a-user-modify-files.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()