├── .flake8 ├── .gitignore ├── README.md ├── ch03-first-python-program ├── 2-screw-things-up.py └── 3-create-a-variable.py ├── ch04-strings-and-string-methods ├── 1-what-is-a-string.py ├── 2-concatenation-indexing-and-slicing.py ├── 3-manipulate-strings-with-methods.py ├── 4-interact-with-user-input.py ├── 5-challenge-pick-apart-your-users-input.py ├── 6-working-with-strings-and-numbers.py ├── 7-streamline-your-prints.py ├── 8-find-a-string-in-a-string.py └── 9-challenge-turn-your-user-into-a-l33t-h4x0r.py ├── ch05-numbers-in-python ├── 1-integers-and-floating-point-numbers.py ├── 3-challenge-perform-calculations-on-user-input.py ├── 5-math-functions-and-number-methods.py └── 6-print-numbers-in-style.py ├── ch06-functions-and-loops ├── 2-write-your-own-functions.py ├── 3-challenge-convert-temperatures.py ├── 4-run-in-circles.py └── 5-challenge-track-your-investments.py ├── ch08-conditional-logic ├── 1-compare-values.py ├── 2-add-some-logic.py ├── 3-control-the-flow-of-your-program.py ├── 4-challenge-find-the-factors-of-a-number.py ├── 5-break-out-of-the-pattern.py ├── 6-recover-from-errors.py ├── 7-simulate-events-and-calculate-probabilities.py ├── 8a-challenge-simulate-a-coin-toss-experiment.py ├── 8b-challenge-simulate-a-coin-toss-experiment.py ├── 8c-challenge-simulate-a-coin-toss-experiment.py ├── 9a-challenge-simulate-an-election.py └── 9b-challenge-simulate-an-election.py ├── ch09-lists-tuples-and-dictionaries ├── 1-tuples-are-immutable-sequences.py ├── 2-lists-are-mutable-sequences.py ├── 3-nesting-sorting-and-copying-lists-and-tuples.py ├── 4-challenge-list-of-lists.py ├── 5-challenge-wax-poetic.py ├── 6-store-relationships-in-dictionaries.py ├── 7-challenge-capital-city-loop.py ├── 9a-challenge-cats-with-hats.py ├── 9b-challenge-cats-with-hats.py └── 9c-challenge-cats-with-hats.py ├── ch10-primer-on-oop ├── 2-instantiate-an-object.py ├── 3-inherit-from-other-classes.py └── 4-challenge-model-a-farm.py ├── ch11-modules-and-packages ├── 1-working-with-modules │ ├── greeter.py │ └── main.py └── 2-working-with-packages │ └── packages_exercises │ ├── helpers │ ├── __init__.py │ ├── math.py │ └── string.py │ └── main.py ├── ch12-file-input-and-output ├── 2-working-with-file-paths-in-python.py ├── 3-common-file-system-operations.py ├── 4-challenge-move-all-image-files-to-a-new-directory.py ├── 5-reading-and-writing-files.py ├── 6-read-and-write-csv-data.py ├── 7-challenge-create-a-high-scores-list.py └── practice_files │ ├── documents │ ├── files │ │ ├── additional files │ │ │ └── image3.png │ │ ├── dad.txt │ │ └── stuff.csv │ ├── image1.png │ └── more_files │ │ ├── even_more_files │ │ ├── image4.jpg │ │ └── the_answer.txt │ │ ├── image2.gif │ │ └── mom.txt │ └── scores.csv ├── ch14-interact-with-pdf-files ├── 1-extract-text-from-a-pdf.py ├── 2-extract-pages-from-a-pdf.py ├── 3-challenge-PdfFileSplitter-class.py ├── 4-concatenating-and-merging-pdfs.py ├── 5-rotating-and-cropping-pdf-pages.py ├── 6-encrypting-and-decrypting-pdfs.py ├── 7-challenge-unscramble-a-pdf.py └── practice_files │ ├── Emperor cover sheet.pdf │ ├── Pride_and_Prejudice.pdf │ ├── The Emperor.pdf │ ├── expense_reports │ ├── Expense report 1.pdf │ ├── Expense report 2.pdf │ └── Expense report 3.pdf │ ├── half_and_half.pdf │ ├── merge1.pdf │ ├── merge2.pdf │ ├── merge3.pdf │ ├── newsletter.pdf │ ├── quarterly_report │ ├── full_report.pdf │ ├── report.pdf │ └── toc.pdf │ ├── scrambled.pdf │ ├── split_and_rotate.pdf │ ├── top_secret.pdf │ ├── ugly.pdf │ └── zen.pdf ├── ch15-sql-database-connections └── 1-use-sqlite.py ├── ch16-interacting-with-the-web ├── 1-scrape-and-parse-text-from-websites.py ├── 2-use-an-html-parser-to-scrape-websites.py ├── 3-interact-with-html-forms.py └── 4-interact-with-websites-in-realtime.py ├── ch17-scientific-computing-and-graphing ├── 1-use-numpy-for-matrix-manipulation.py ├── 2-use-matplotlib-for-plotting-graphs.py └── practice_files │ └── pirates.csv ├── ch18-graphical-user-interfaces ├── 1-add-gui-elements-with-easygui.py ├── 10-challenge-return-of-the-poet.py ├── 2-example-app-pdf-page-rotator.py ├── 3-challenge-use-gui-elements-to-help-a-user-modify-files.py ├── 4-introduction-to-tkinter.py ├── 5-working-with-widgets.py ├── 6-control-layout-with-geometry-managers.py └── 7-make-your-applications-interactive.py └── pyproject.toml /.flake8: -------------------------------------------------------------------------------- 1 | # flake8 linter configuration 2 | # This file is for internal use only and is not a solution to any exercise 3 | 4 | [flake8] 5 | ignore = 6 | # Whitespace before `:` (black artifact) 7 | E203 8 | # Module level import not at top of file (ignored to keep imports grouped 9 | # logically in files with solutions for multiple exercises) 10 | E402 11 | # Line too long (> 79 chars, line length handled by black) 12 | E501 13 | # Line break before binary operatory (black artifact) 14 | W503 15 | exclude = 16 | .git 17 | __pychache__ 18 | venv 19 | ch03-first-python-program -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | py3venv 2 | env 3 | *.pyc 4 | __pycache__/ 5 | .DS_Store 6 | .vscode 7 | venv 8 | .venv 9 | .idea/* 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Exercise Solutions for [Real Python's *"Python Basics: A Practical Introduction to Python 3"*](https://realpython.com/products/python-basics-book/) Book 2 | 3 | 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. 4 | 5 | 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! 6 | 7 | ## Get the Book 8 | 9 | [» Click here to learn more about the book and get your copy](https://realpython.com/products/python-basics-book/) 10 | 11 | ## Downloading the Files 12 | 13 | ### With `git` 14 | 15 | If you have [`git` installed](https://realpython.com/python-git-github-intro/), the easiest way to access these files is to clone the repository to the directory of your choice: 16 | 17 | ```console 18 | $ git clone https://github.com/realpython/python-basics-exercises.git 19 | ``` 20 | 21 | ### Without `git` 22 | 23 | Alternatively, you can download the entire repository as a `.zip` file from the repository's [homepage](https://github.com/realpython/python-basics-exercises) using the green "Clone or download" button in the top right hand corner, or by [clicking here](https://github.com/realpython/python-basics-exercises/archive/master.zip). 24 | 25 | ## Running the Solutions & Code Examples 26 | 27 | To run the solution code and code examples, first make sure you have Python 3 installed on your machine. If you need help installing Python 3, check out our [Python 3 Installation & Setup Guide](https://realpython.com/installing-python/). 28 | 29 | Example: To run the `3-store-a-variable.py` exercise, type `python3 3-store-a-variable.py` into your terminal. Here's how that looks, with output: 30 | 31 | ```console 32 | $ python3 ch03-first-python-program/3-store-a-variable.py 33 | hello 34 | hi 35 | ``` 36 | 37 | > **Note:** Depending on your installation, you may need to type `python3.9` or `python39` to run the examples. 38 | -------------------------------------------------------------------------------- /ch03-first-python-program/2-screw-things-up.py: -------------------------------------------------------------------------------- 1 | # 3.2 - Screw Things Up 2 | # Solutions to review exercies 3 | 4 | 5 | # Exercise 1 6 | # The following line won't run because of a syntax error 7 | print("hi) 8 | 9 | # We didn't close the double quotes at the end of the string. 10 | # The line above needed to have been: 11 | # print("hi") 12 | 13 | 14 | # Exercise 2 15 | ''' The following lines won't run properly, 16 | even if the syntax error in the line above is corrected, 17 | because of a run-time error ''' 18 | print(hello) 19 | 20 | # We meant to print the string "hello"; 21 | # a variable named 'hello' doesn't exist yet. 22 | # 23 | # This line could have been: 24 | # 25 | # my_string = "hello" 26 | # print(my_string) 27 | -------------------------------------------------------------------------------- /ch03-first-python-program/3-create-a-variable.py: -------------------------------------------------------------------------------- 1 | # 3.3 - Create a Variable 2 | # Solutions to review exercies 3 | 4 | 5 | # Exercise 3 (exercises 1 and 2 are done in interactive window) 6 | # This solution works for Exercises 1 and 2 by typing the same lines into the 7 | # interactive window. 8 | 9 | # Display a string directly 10 | print("hello") 11 | 12 | 13 | # Display the contents of a string variable 14 | my_string = "hi" 15 | print(my_string) 16 | -------------------------------------------------------------------------------- /ch04-strings-and-string-methods/1-what-is-a-string.py: -------------------------------------------------------------------------------- 1 | # 4.1 - What is a String? 2 | # Solutions to review exercies 3 | 4 | 5 | # Exercise 1 6 | print('There are "double quotes" in this string.') 7 | 8 | 9 | # Exercise 2 10 | print("This string's got an apostrophe.") 11 | 12 | 13 | # Exercise 3 14 | print( 15 | """This string was written on multiple lines, 16 | and it displays across multiple lines""" 17 | ) 18 | 19 | 20 | # Exercise 4 21 | print( 22 | "This one-line string was written out \ 23 | using multiple lines" 24 | ) 25 | -------------------------------------------------------------------------------- /ch04-strings-and-string-methods/2-concatenation-indexing-and-slicing.py: -------------------------------------------------------------------------------- 1 | # 4.2 - Concatenation, Indexing, and Slicing 2 | # Solutions to review exercies 3 | 4 | 5 | # Exercise 1 6 | # Display the number of letters in the string 7 | my_word = "antidisestablishmentarianism" 8 | print(len(my_word)) 9 | 10 | 11 | # Exercise 2 12 | # Concatenate two strings together 13 | string_left = "bat" 14 | string_right = "man" 15 | print(string_left + string_right) 16 | 17 | 18 | # Exercise 3 19 | # Display two strings together, with a space in between 20 | string_one = "heebie" 21 | string_two = "jeebies" 22 | print(string_one + " " + string_two) 23 | 24 | 25 | # Exercise 4 26 | # Use subscripting to display part of a string 27 | print("bazinga"[2:6]) 28 | -------------------------------------------------------------------------------- /ch04-strings-and-string-methods/3-manipulate-strings-with-methods.py: -------------------------------------------------------------------------------- 1 | # 4.3 - Use String Methods 2 | # Solutions to review exercies 3 | 4 | 5 | # Exercise 1 6 | string1 = "Animals" 7 | string2 = "Badger" 8 | string3 = "Honey Bee" 9 | string4 = "Honeybadger" 10 | 11 | print(string1.lower()) 12 | print(string2.lower()) 13 | print(string3.lower()) 14 | print(string4.lower()) 15 | 16 | 17 | # Exercise 2 18 | print(string1.upper()) 19 | print(string2.upper()) 20 | print(string3.upper()) 21 | print(string4.upper()) 22 | 23 | 24 | # Exercise 3 25 | string1 = " Filet Mignon" 26 | string2 = "Brisket " 27 | string3 = " Cheeseburger " 28 | 29 | print(string1.strip()) # Could also use .lstrip() 30 | print(string2.strip()) # Could also use .rstrip() 31 | print(string3.strip()) 32 | 33 | 34 | # Exercise 4 35 | string1 = "Becomes" 36 | string2 = "becomes" 37 | string3 = "BEAR" 38 | string4 = " bEautiful" 39 | 40 | print(string1.startswith("be")) 41 | print(string2.startswith("be")) 42 | print(string3.startswith("be")) 43 | print(string4.startswith("be")) 44 | 45 | 46 | # Exercise 5 47 | string1 = string1.lower() 48 | # (string2 will pass unmodified) 49 | string3 = string3.lower() 50 | string4 = string4.strip().lower() 51 | 52 | print(string1.startswith("be")) 53 | print(string2.startswith("be")) 54 | print(string3.startswith("be")) 55 | print(string4.startswith("be")) 56 | -------------------------------------------------------------------------------- /ch04-strings-and-string-methods/4-interact-with-user-input.py: -------------------------------------------------------------------------------- 1 | # 4.4 - Interact With User Input 2 | # Solutions to review exercies 3 | 4 | 5 | # Exercise 1 6 | # Take input from the user and display that input back 7 | my_input = input("Type something: ") 8 | print(my_input) 9 | 10 | 11 | # Exercise 2 12 | # Display the input string converted to lower-case letters 13 | print(my_input.lower()) 14 | 15 | 16 | # Exercise 3 17 | # Take user input and display the number of input characters. 18 | input_string = input("Type something else: ") 19 | print(len(input_string)) 20 | -------------------------------------------------------------------------------- /ch04-strings-and-string-methods/5-challenge-pick-apart-your-users-input.py: -------------------------------------------------------------------------------- 1 | # 4.5 - Challenge: Pick Apart Your User's Input 2 | # Solution to code challenge 3 | 4 | 5 | # Return the upper-case first letter entered by the user 6 | 7 | user_input = input("Tell me your password: ") 8 | first_letter = user_input[0] 9 | print("The first letter you entered was: " + first_letter.upper()) 10 | -------------------------------------------------------------------------------- /ch04-strings-and-string-methods/6-working-with-strings-and-numbers.py: -------------------------------------------------------------------------------- 1 | # 4.6 - Working with Strings and Numbers 2 | # Solutions to review exercies 3 | 4 | 5 | # Exercise 1 6 | # Store an integer as a string 7 | my_integer_string = "6" 8 | 9 | # Convert the 'integer' string into an int object using int() 10 | # Multiply the integer by 7 and display the result 11 | print(int(my_integer_string) * 7) 12 | 13 | 14 | # Exercise 2 15 | # Store a floating-point number as a string 16 | my_float_string = "6.01" 17 | 18 | # Convert the 'float' string into a number using float() 19 | # Multiply the number by 7 and display the result 20 | print(float(my_float_string) * 7) 21 | 22 | 23 | # Exercise 3 24 | # Create a string and an int object, then display them together 25 | my_string = "mp" 26 | my_int = 3 27 | print(my_string + str(my_int)) 28 | 29 | 30 | # Exercise 4 31 | # Get two numbers from the user, multiply them, 32 | # and display the result 33 | a = input("Enter a number: ") 34 | b = input("Enter another number: ") 35 | product = float(a) * float(b) 36 | print("The product of " + a + " and " + b + " is " + str(product) + ".") 37 | -------------------------------------------------------------------------------- /ch04-strings-and-string-methods/7-streamline-your-prints.py: -------------------------------------------------------------------------------- 1 | # 4.7 - Streamline Your Prints 2 | # Solutions to review exercies 3 | 4 | 5 | # Exercise 1 6 | weight = 0.2 7 | animal = "newt" 8 | 9 | # Concatenate a number and a string in one print call 10 | print(str(weight) + " kg is the weight of the " + animal + ".") 11 | 12 | 13 | # Exercise 2 14 | # Use format() to print a number and a string inside of another string 15 | print("{} kg is the weight of the {}.".format(weight, animal)) 16 | 17 | 18 | # Exercise 3 19 | # Use formatted string literal (f-string) to reference objects inside a string 20 | print(f"{weight} kg is the weight of the {animal}.") 21 | -------------------------------------------------------------------------------- /ch04-strings-and-string-methods/8-find-a-string-in-a-string.py: -------------------------------------------------------------------------------- 1 | # 4.8 - Find a String in a String 2 | # Solutions to review exercies 3 | 4 | 5 | # Exercise 1 6 | # Cannot find the string "a" in the string "AAA": 7 | print("AAA".find("a")) 8 | 9 | 10 | # Exercise 2 11 | # Replace every occurrence of the character `"s"` 12 | # with the character `"x"` 13 | phrase = "Somebody said something to Samantha." 14 | phrase = phrase.replace("s", "x") 15 | print(phrase) 16 | # NOTE: This leaves the capital "S" unchanged, so the 17 | # output will be "Somebody xaid xomething to Samantha." 18 | 19 | 20 | # Exercise 3 21 | # Try to find an upper-case "X" in user input: 22 | my_input = input("Type something: ") 23 | print(my_input.find("X")) 24 | -------------------------------------------------------------------------------- /ch04-strings-and-string-methods/9-challenge-turn-your-user-into-a-l33t-h4x0r.py: -------------------------------------------------------------------------------- 1 | # 4.9 - Challenge: Turn Your User Into a L33t H4x0r 2 | # Solution to challenge 3 | 4 | 5 | # Turn a user's input into leetspeak 6 | 7 | my_text = input("Enter some text: ") 8 | 9 | my_text = my_text.replace("a", "4") 10 | my_text = my_text.replace("b", "8") 11 | my_text = my_text.replace("e", "3") 12 | my_text = my_text.replace("l", "1") 13 | my_text = my_text.replace("o", "0") 14 | my_text = my_text.replace("s", "5") 15 | my_text = my_text.replace("t", "7") 16 | 17 | print(my_text) 18 | -------------------------------------------------------------------------------- /ch05-numbers-in-python/1-integers-and-floating-point-numbers.py: -------------------------------------------------------------------------------- 1 | # 5.1 - Integers and Floating-Point Numbers 2 | # Solutions to Review Exercises 3 | 4 | 5 | # Exercise 1 6 | num1 = 25_000_000 7 | num2 = 25000000 8 | print(num1) 9 | print(num2) 10 | 11 | # Exercise 2 12 | num = 1.75e5 13 | print(num) 14 | 15 | # Exercise 3 16 | # NOTE: Your solution may vary! 17 | print(2e308) 18 | -------------------------------------------------------------------------------- /ch05-numbers-in-python/3-challenge-perform-calculations-on-user-input.py: -------------------------------------------------------------------------------- 1 | # 5.3 - Challenge: Perform Calculations on User Input 2 | # Solution to challenge 3 | 4 | 5 | # Receive two input numbers and calculate their power 6 | 7 | base = input("Enter a base: ") 8 | exponent = input("Enter an exponent: ") 9 | result = float(base) ** float(exponent) 10 | print(f"{base} to the power of {exponent} = {result}") 11 | -------------------------------------------------------------------------------- /ch05-numbers-in-python/5-math-functions-and-number-methods.py: -------------------------------------------------------------------------------- 1 | # 5.5 - Math Functions and Number Methods 2 | # Solutions to Review Exercises 3 | 4 | 5 | # Exercise 1 6 | user_input = input("Enter a number: ") 7 | num = float(user_input) 8 | print(f"{num} rounded to 2 decimal places is {round(num, 2)}") 9 | 10 | # Exercise 2 11 | user_input = input("Enter a number: ") 12 | num = float(user_input) 13 | print(f"The absolute value of {num} is {abs(num)}") 14 | 15 | # Exercise 3 16 | num1 = float(input("Enter a number: ")) 17 | num2 = float(input("Enter another number: ")) 18 | print( 19 | f"The difference between {num1} and {num2} is an integer? " 20 | f"{(num1 - num2).is_integer()}!" 21 | ) 22 | -------------------------------------------------------------------------------- /ch05-numbers-in-python/6-print-numbers-in-style.py: -------------------------------------------------------------------------------- 1 | # 5.6 - Print Numbers in Style 2 | # Solutions to Review Exercises 3 | 4 | # Exercise 1 5 | print(f"{3 ** .125:.3f}") 6 | 7 | # Exercise 2 8 | print(f"${150000:,.2f}") 9 | 10 | # Exercise 3 11 | print(f"{2 / 10:.0%}") 12 | -------------------------------------------------------------------------------- /ch06-functions-and-loops/2-write-your-own-functions.py: -------------------------------------------------------------------------------- 1 | # 6.2 - Write Your Own Functions 2 | # Solutions to review exercises 3 | 4 | 5 | # Exercise 1 6 | def cube(num): 7 | """Return the cube of the input number.""" 8 | cube_num = num**3 # Could also use pow(num, 3) 9 | return cube_num 10 | 11 | 12 | print(f"0 cubed is {cube(0)}") 13 | print(f"2 cubed is {cube(2)}") 14 | 15 | 16 | # Exercise 2 17 | def greet(name): 18 | """Display a greeting.""" 19 | print(f"Hello {name}!") 20 | 21 | 22 | greet("Guido") 23 | -------------------------------------------------------------------------------- /ch06-functions-and-loops/3-challenge-convert-temperatures.py: -------------------------------------------------------------------------------- 1 | # 6.3 - Challenge: Convert temperatures 2 | # Solution to challenge 3 | 4 | 5 | def convert_cel_to_far(temp_cel): 6 | """Return the Celsius temperature temp_cel converted to Fahrenheit.""" 7 | temp_far = temp_cel * (9 / 5) + 32 8 | return temp_far 9 | 10 | 11 | def convert_far_to_cel(temp_far): 12 | """Return the Fahrenheit temperature temp_far converted to Celsius.""" 13 | temp_cel = (temp_far - 32) * (5 / 9) 14 | return temp_cel 15 | 16 | 17 | # Prompt the user to input a Fahrenheit temperature. 18 | temp_far = input("Enter a temperature in degrees F: ") 19 | 20 | # Convert the temperature to Celsius. 21 | # Note that `temp_far` must be converted to a `float` 22 | # since `input()` returns a string. 23 | temp_cel = convert_far_to_cel(float(temp_far)) 24 | 25 | # Display the converted temperature 26 | print(f"{temp_far} degrees F = {temp_cel:.2f} degrees C") 27 | 28 | # You could also use `round()` instead of the formatting mini-language: 29 | # print(f"{temp_far} degrees F = {round(temp_cel, 2)} degrees C"") 30 | 31 | # Prompt the user to input a Celsius temperature. 32 | temp_cel = input("\nEnter a temperature in degrees C: ") 33 | 34 | # Convert the temperature to Fahrenheit. 35 | temp_far = convert_cel_to_far(float(temp_cel)) 36 | 37 | # Display the converted temperature 38 | print(f"{temp_cel} degrees C = {temp_far:.2f} degrees F") 39 | -------------------------------------------------------------------------------- /ch06-functions-and-loops/4-run-in-circles.py: -------------------------------------------------------------------------------- 1 | # 6.4 - Run in Circles 2 | # Solutions to review exercises 3 | 4 | 5 | # Exercise 1 6 | # print the integer 2 through 10 using a "for" loop 7 | for i in range(2, 11): 8 | print(i) 9 | 10 | 11 | # Exercise 2 12 | # print the integer 2 through 10 using a "while" loop 13 | i = 2 14 | while i < 11: 15 | print(i) 16 | i = i + 1 17 | 18 | 19 | # Exercise 3 20 | def doubles(num): 21 | """Return the result of multiplying an input number by 2.""" 22 | return num * 2 23 | 24 | 25 | # Call doubles() to double the number 2 three times 26 | my_num = 2 27 | for i in range(0, 3): 28 | my_num = doubles(my_num) 29 | print(my_num) 30 | -------------------------------------------------------------------------------- /ch06-functions-and-loops/5-challenge-track-your-investments.py: -------------------------------------------------------------------------------- 1 | # 6.5 - Challenge: Track Your Investments 2 | # Solution to challenge 3 | 4 | 5 | # Calculate interest to track the growth of an investment 6 | 7 | 8 | def invest(amount, rate, years): 9 | """Display year on year growth of an initial investment""" 10 | for year in range(1, years + 1): 11 | amount = amount * (1 + rate) 12 | print(f"year {year}: ${amount:,.2f}") 13 | 14 | 15 | amount = float(input("Enter a principal amount: ")) 16 | rate = float(input("Enter an anual rate of return: ")) 17 | years = int(input("Enter a number of years: ")) 18 | 19 | invest(amount, rate, years) 20 | -------------------------------------------------------------------------------- /ch08-conditional-logic/1-compare-values.py: -------------------------------------------------------------------------------- 1 | # 8.1 - Compare Values 2 | # Solutions to review exercises 3 | 4 | 5 | # Exercise 1 6 | # Test whether these expressions are True or False 7 | 8 | print(1 <= 1) 9 | print(1 != 1) 10 | print(1 != 2) 11 | print("good" != "bad") 12 | print("good" != "Good") 13 | print(123 == "123") 14 | 15 | 16 | # Exercise 2 17 | # Fill in the blank so that each of the following expressions are True 18 | 19 | # 3 __ 4 20 | # Any of the following: 21 | 3 < 4 22 | 3 <= 4 23 | 3 != 4 24 | 25 | # 10 __ 5 26 | # Any of the following: 27 | 10 > 5 28 | 10 >= 5 29 | 10 != 5 30 | 31 | # "jack" __ "jill" 32 | # Any of the following: 33 | "jack" < "jill" 34 | "jack" <= "jill" 35 | "jack" != "jill" 36 | 37 | # 42 __ "42" 38 | 42 != "42" 39 | -------------------------------------------------------------------------------- /ch08-conditional-logic/2-add-some-logic.py: -------------------------------------------------------------------------------- 1 | # 8.2 - Add Some Logic 2 | # Solutions to review exercises 3 | 4 | # Exercise 1 5 | # Test whether these expressions are True or False 6 | print((1 <= 1) and (1 != 1)) 7 | print(not (1 != 2)) 8 | print(("good" != "bad") or False) 9 | print(("good" != "Good") and not (1 == 1)) 10 | 11 | # Exercise 2 12 | # Add parentheses so that the following expressions all 13 | # evaluate to True 14 | 15 | # False == not True 16 | print(False == (not True)) 17 | # True and False == True and False 18 | print((True and False) == (True and False)) 19 | # not True and "A" == "B" 20 | print(not (True and "A" == "B")) 21 | -------------------------------------------------------------------------------- /ch08-conditional-logic/3-control-the-flow-of-your-program.py: -------------------------------------------------------------------------------- 1 | # 8.3 - Control the Flow of Your Program 2 | # Solutions to review exercises 3 | 4 | 5 | # Exercise 1 6 | # Display whether the length of user input is <, > or = 5 characters 7 | 8 | my_input = input("Type something: ") 9 | 10 | if len(my_input) < 5: 11 | print("Your input is less than 5 characters long.") 12 | elif len(my_input) > 5: 13 | print("Your input is greater than 5 characters long.") 14 | else: 15 | print("Your input is 5 characters long.") 16 | 17 | 18 | # Exercise 2 19 | # Number guessing program ("guess" the number 3) 20 | 21 | print("I'm thinking of a number between 1 and 10. Guess which one.") 22 | my_guess = input("Type in your guess: ") 23 | 24 | if my_guess == "3": 25 | print("You win!") 26 | else: 27 | print("You lose.") 28 | -------------------------------------------------------------------------------- /ch08-conditional-logic/4-challenge-find-the-factors-of-a-number.py: -------------------------------------------------------------------------------- 1 | # 8.4 - Challenge: Find the Factors of a Number 2 | # Solution to challenge 3 | 4 | 5 | # Display all the factors of a number chosen by the user 6 | 7 | num = int(input("Enter a positive integer: ")) 8 | for divisor in range(1, num + 1): 9 | if num % divisor == 0: 10 | print(f"{divisor} is a factor of {num}") 11 | -------------------------------------------------------------------------------- /ch08-conditional-logic/5-break-out-of-the-pattern.py: -------------------------------------------------------------------------------- 1 | # 8.5 - Break Out of the Pattern 2 | # Solutions to review exercises 3 | 4 | 5 | # Exercise 1 6 | # Run in an infinite loop until the user types "q" or "Q" 7 | while True: 8 | user_input = input('Type "q" or "Q" to quit: ') 9 | if user_input.upper() == "Q": 10 | break 11 | 12 | 13 | # Exercise 2 14 | # Display every number from 1 through 50 except multiples of 3 15 | for i in range(1, 51): 16 | if i % 3 == 0: 17 | continue 18 | print(i) 19 | -------------------------------------------------------------------------------- /ch08-conditional-logic/6-recover-from-errors.py: -------------------------------------------------------------------------------- 1 | # 8.6 - Recover From Errors 2 | # Solution to review exercises 3 | 4 | 5 | # Exercise 1 6 | # Ask the user to enter an integer. 7 | # Repeat the process if the user hasn't entered an integer. 8 | while True: 9 | try: 10 | my_input = input("Type an integer: ") 11 | print(int(my_input)) 12 | break 13 | except ValueError: 14 | print("try again") 15 | 16 | 17 | # Exercise 2 18 | # Print character and specified index in string 19 | 20 | input_string = input("Enter a string: ") 21 | 22 | try: 23 | index = int(input("Enter an integer: ")) 24 | print(input_string[index]) 25 | except ValueError: 26 | print("Invalid number") 27 | except IndexError: 28 | print("Index is out of bounds") 29 | -------------------------------------------------------------------------------- /ch08-conditional-logic/7-simulate-events-and-calculate-probabilities.py: -------------------------------------------------------------------------------- 1 | # 8.7 - Simulate Events and Calculate Probabilities 2 | # Solutions to review exercises 3 | 4 | 5 | from random import randint 6 | 7 | 8 | # Exercise 1 9 | # Write a function that simulates the roll of a die. 10 | def roll(): 11 | """Return random integer between 1 and 6""" 12 | return randint(1, 6) 13 | 14 | 15 | # Exercise 2 16 | # Simulate 10,000 rolls of a die and display the average number rolled. 17 | num_rolls = 10_000 18 | total = 0 19 | 20 | for trial in range(num_rolls): 21 | total = total + roll() 22 | 23 | avg_roll = total / num_rolls 24 | 25 | print(f"The average result of {num_rolls} rolls is {avg_roll}") 26 | -------------------------------------------------------------------------------- /ch08-conditional-logic/8a-challenge-simulate-a-coin-toss-experiment.py: -------------------------------------------------------------------------------- 1 | # 8.8 - Challenge: Simulate a Coin Toss Experiment 2 | # Solution to challenge 3 | 4 | 5 | # Simulate the results of a series of coin tosses and track the results 6 | 7 | # This one is tricky to structure correctly. Try writing out the logic before 8 | # you start coding. Some additional pointers if you're stuck: 9 | # 1. You will need to use a `for` loop over a range of trials. 10 | # 2. For each trial, first you should check the outcome of the first flip. 11 | # 3. Make sure you add the first flip to the total number of flips. 12 | # 4. After the first toss, you'll need another loop to keep flipping while you 13 | # get the same result as the first flip. 14 | 15 | import random 16 | 17 | 18 | def coin_flip(): 19 | """Randomly return 'heads' or 'tails'.""" 20 | if random.randint(0, 1) == 0: 21 | return "heads" 22 | else: 23 | return "tails" 24 | 25 | 26 | flips = 0 27 | num_trials = 10_000 28 | 29 | for trial in range(num_trials): 30 | if coin_flip() == "heads": 31 | # Increment the number of flips by 1 32 | flips = flips + 1 33 | while coin_flip() == "heads": 34 | # Keep incrementing the total number of flips 35 | # until "tails" is returned by coin_flip() 36 | flips = flips + 1 37 | # Once coin_flip() return "tails", the loop will exit, 38 | # but we need to add one more to flips to track the 39 | # last flip that generated "tails" 40 | flips = flips + 1 41 | else: 42 | # coin_flip() returned "tails" on the first flip. 43 | # Increment the number of flips by 1 44 | flips = flips + 1 45 | while coin_flip() == "tails": 46 | # Keep incrementing the total number of flips 47 | # until "heads" is returned by coin_flip() 48 | flips = flips + 1 49 | # Once coin_flip() returns "heads", the loop will exit, 50 | # but we need to add one more to flips to track the 51 | # last flip that generated "heads" 52 | flips = flips + 1 53 | 54 | avg_flips_per_trial = flips / num_trials 55 | print(f"The average number of flips per trial is {avg_flips_per_trial}.") 56 | -------------------------------------------------------------------------------- /ch08-conditional-logic/8b-challenge-simulate-a-coin-toss-experiment.py: -------------------------------------------------------------------------------- 1 | # 8.8 - Challenge: Simulate a Coin Toss Experiement 2 | # Alternative solution to challenge 3 | 4 | 5 | # Simulate the results of a series of coin tosses and track the results 6 | 7 | # This one is tricky to structure correctly. Try writing out the logic before 8 | # you start coding. Some additional pointers if you're stuck: 9 | # 1. You will need to use a `for` loop over a range of trials. 10 | # 2. For each trial, first you should check the outcome of the first flip. 11 | # 3. Make sure you add the first flip to the total number of flips. 12 | # 4. After the first toss, you'll need another loop to keep flipping while you 13 | # get the same result as the first flip. 14 | 15 | import random 16 | 17 | 18 | def coin_flip(): 19 | """Randomly return 'heads' or 'tails'.""" 20 | if random.randint(0, 1) == 0: 21 | return "heads" 22 | else: 23 | return "tails" 24 | 25 | 26 | flips = 0 27 | num_trials = 10_000 28 | 29 | for trial in range(num_trials): 30 | # Flip the coin once and increment the flips tally by 1 31 | first_flip = coin_flip() 32 | flips = flips + 1 33 | # Continue flipping the coin and updating the tally until 34 | # a different result is returned by coin_flip() 35 | while coin_flip() == first_flip: 36 | flips = flips + 1 37 | # Increment the flip tally once more to account for the 38 | # final flip with a different result 39 | flips = flips + 1 40 | 41 | avg_flips_per_trial = flips / num_trials 42 | print(f"The average number of flips per trial is {avg_flips_per_trial}.") 43 | -------------------------------------------------------------------------------- /ch08-conditional-logic/8c-challenge-simulate-a-coin-toss-experiment.py: -------------------------------------------------------------------------------- 1 | # 8.8 - Challenge: Simulate a Coin Toss Experiement 2 | # Alternative solution to challenge using functions 3 | 4 | 5 | # Simulate the results of a series of coin tosses and track the results 6 | 7 | # This one is tricky to structure correctly. Try writing out the logic before 8 | # you start coding. Some additional pointers if you're stuck: 9 | # 1. You will need to use a `for` loop over a range of trials. 10 | # 2. For each trial, first you should check the outcome of the first flip. 11 | # 3. Make sure you add the first flip to the total number of flips. 12 | # 4. After the first toss, you'll need another loop to keep flipping while you 13 | # get the same result as the first flip. 14 | 15 | import random 16 | 17 | 18 | def single_trial(): 19 | """Simulate repeatedly flipping a coin until both heads and tails are seen.""" 20 | # This function uses random.randint() to simulate a single coin toss. 21 | # randint(0, 1) randomly returns 0 or 1 with equal probability. We can 22 | # use 0 to represent heads and 1 to represent tails. 23 | 24 | # Flip the coin the first time 25 | flip_result = random.randint(0, 1) 26 | # Keep a tally of how many times the coin has been flipped. We've only 27 | # flipped once so the initial count is 1. 28 | flip_count = 1 29 | 30 | # Continue to flip the coin until randint(0, 1) returns something 31 | # different than the original flip_result 32 | while flip_result == random.randint(0, 1): 33 | flip_count = flip_count + 1 34 | 35 | # The last step in the loop flipped the coin but didn't update the tally, 36 | # so we need to increase the flip_count by 1 37 | flip_count = flip_count + 1 38 | return flip_count 39 | 40 | 41 | def flip_trial_avg(num_trials): 42 | """Calculate the average number of flips per trial over num_trials total trials.""" 43 | total = 0 44 | for trial in range(num_trials): 45 | total = total + single_trial() 46 | return total / num_trials 47 | 48 | 49 | print(f"The average number of coin flips was {flip_trial_avg(10_000)}") 50 | -------------------------------------------------------------------------------- /ch08-conditional-logic/9a-challenge-simulate-an-election.py: -------------------------------------------------------------------------------- 1 | # 8.9 - Challenge: Simulate an Election 2 | # Solution to challenge 3 | 4 | 5 | # Simulate the results of an election using a Monte Carlo simulation 6 | 7 | from random import random 8 | 9 | num_times_A_wins = 0 10 | num_times_B_wins = 0 11 | 12 | num_trials = 10_000 13 | for trial in range(0, num_trials): 14 | votes_for_A = 0 15 | votes_for_B = 0 16 | 17 | # Determine who wins the 1st region 18 | if random() < 0.87: 19 | votes_for_A = votes_for_A + 1 20 | else: 21 | votes_for_B = votes_for_B + 1 22 | 23 | # Determine who wins the 2nd region 24 | if random() < 0.65: 25 | votes_for_A = votes_for_A + 1 26 | else: 27 | votes_for_B = votes_for_B + 1 28 | 29 | # Determine who wins the 3rd region 30 | if random() < 0.17: 31 | votes_for_A = votes_for_A + 1 32 | else: 33 | votes_for_B = votes_for_B + 1 34 | 35 | # Determine overall election outcome 36 | if votes_for_A > votes_for_B: 37 | num_times_A_wins = num_times_A_wins + 1 38 | else: 39 | num_times_B_wins = num_times_B_wins + 1 40 | 41 | print(f"Probability A wins: {num_times_A_wins / num_trials}") 42 | print(f"Probability B wins: {num_times_B_wins / num_trials}") 43 | -------------------------------------------------------------------------------- /ch08-conditional-logic/9b-challenge-simulate-an-election.py: -------------------------------------------------------------------------------- 1 | # 8.9 - Challenge: Simulate an Election 2 | # Alternate solution to challenge 3 | 4 | 5 | # Simulate the results of an election using a Monte Carlo simulation 6 | 7 | from random import random 8 | 9 | 10 | def run_regional_election(chance_A_wins): 11 | """Return the result of a regional election, either "A" or "B". 12 | 13 | The chances of "A" winning are determined by chance_A_wins. 14 | """ 15 | if random() < chance_A_wins: 16 | return "A" 17 | else: 18 | return "B" 19 | 20 | 21 | def run_election(regional_chances): 22 | """Return the result of an election, either "A" or "B". 23 | 24 | regional_chances is a list or tuple of floats representing the 25 | chances that candidate "A" will win in each region. 26 | 27 | For example, run_election([.2, .5, .7]) will run an election with 28 | three regions, where candidate "A" has a 20% chance to win in the 29 | first region, 50% in the second, and 70% in the third. 30 | """ 31 | num_regions_won_by_A = 0 32 | for chance_A_wins in regional_chances: 33 | if run_regional_election(chance_A_wins) == "A": 34 | num_regions_won_by_A = num_regions_won_by_A + 1 35 | 36 | # Return the results. Note that the number of regions won by candidate 37 | # "B" is the total number of regions minus the number of regions won by 38 | # candidate "A". The total number of regions is the same as the length 39 | # of the regional_chances list. 40 | num_regions_won_by_B = len(regional_chances) - num_regions_won_by_A 41 | if num_regions_won_by_A > num_regions_won_by_B: 42 | return "A" 43 | else: 44 | return "B" 45 | 46 | 47 | CHANCES_A_WINS_BY_REGION = [0.87, 0.65, 0.17] 48 | NUM_TRIALS = 10_000 49 | 50 | # Run the Monte-Carlo simulation 51 | num_times_A_wins = 0 52 | for trial in range(NUM_TRIALS): 53 | if run_election(CHANCES_A_WINS_BY_REGION) == "A": 54 | num_times_A_wins = num_times_A_wins + 1 55 | 56 | # Display the probabilities that candidate A or candidate B wins the 57 | # election. Note the probability that B wins can be calculated by 58 | # subtracting the probability that A wins from 1. 59 | print(f"Probability A wins: {num_times_A_wins / NUM_TRIALS}") 60 | print(f"Probability B wins: {1 - (num_times_A_wins / NUM_TRIALS)}") 61 | -------------------------------------------------------------------------------- /ch09-lists-tuples-and-dictionaries/1-tuples-are-immutable-sequences.py: -------------------------------------------------------------------------------- 1 | # 9.1 - Tuples are Immutable Sequences 2 | # Solutions to review exercises 3 | 4 | 5 | # Exercise 1 6 | # Create a tuple "cardinal_numbers" with "first", "second" and "third" 7 | cardinal_numbers = ("first", "second", "third") 8 | 9 | 10 | # Exercise 2 11 | # Display the second object in the tuple 12 | print(cardinal_numbers[1]) 13 | 14 | 15 | # Exercise 3 16 | # Unpack the tuple into three strings and display them 17 | position1, position2, position3 = cardinal_numbers 18 | print(position1) 19 | print(position2) 20 | print(position3) 21 | 22 | # Exercise 4 23 | # Create a tuple containing the letters of your name from a string 24 | my_name = tuple("David") 25 | 26 | # Exercise 5 27 | # Check whether or not x is in my_name 28 | print("x" in my_name) 29 | 30 | # Exercise 6 31 | # Create tuple containing all but the first letter in my_name 32 | print(my_name[1:]) 33 | -------------------------------------------------------------------------------- /ch09-lists-tuples-and-dictionaries/2-lists-are-mutable-sequences.py: -------------------------------------------------------------------------------- 1 | # 9.2 - Lists are Mutable Sequences 2 | # Solutions to review exercises 3 | 4 | 5 | # Exercise 1 6 | # Create a list named food with two elements "rice" and "beans". 7 | food = ["rice", "beans"] 8 | 9 | 10 | # Exercise 2 11 | # Append the string "broccoli" to the food list using .append() 12 | food.append("broccoli") 13 | 14 | 15 | # Exercise 3 16 | # Add the strings "bread" and "pizza" to food using .extend() 17 | food.extend(["bread", "pizza"]) 18 | 19 | 20 | # Exercise 4 21 | # Print the first two items in food using slicing notation 22 | print(food[:2]) 23 | 24 | # NOTE: The following is also acceptable 25 | print(food[0:2]) 26 | 27 | 28 | # Exercise 5 29 | # Print the last item in food using index notation 30 | print(food[-1]) 31 | 32 | 33 | # Exercise 6 34 | # Create a list called breakfast from the string "eggs, fruit, orange juice" 35 | breakfast = "eggs, fruit, orange juice".split(", ") 36 | 37 | 38 | # Exercise 7 39 | # Verify that breakfast has three items using len() 40 | print(len(breakfast) == 3) 41 | 42 | 43 | # Exercise 8 44 | # Create a new list called `lengths` using a list 45 | # comprehension that contains the lengths of each 46 | # string in the `breakfast` list. 47 | lengths = [len(item) for item in breakfast] 48 | print(lengths) 49 | -------------------------------------------------------------------------------- /ch09-lists-tuples-and-dictionaries/3-nesting-sorting-and-copying-lists-and-tuples.py: -------------------------------------------------------------------------------- 1 | # 9.3 - Nesting, Copying, and Sorting Lists and Tuples 2 | # Solutions to review exercises 3 | 4 | 5 | # Exercise 1 6 | # Create a tuple called data with two values, (1, 2) and (3, 4) 7 | data = ((1, 2), (3, 4)) 8 | 9 | 10 | # Exercise 2 11 | # Loop over data and print the sum of each nested tuple 12 | index = 1 13 | for row in data: 14 | print(f"Row {index} sum: {sum(row)}") 15 | index += 1 16 | 17 | 18 | # Exercise 3 19 | # Create the list [4, 3, 2, 1] and assign it to variable numbers 20 | numbers = [4, 3, 2, 1] 21 | 22 | 23 | # Exercise 4 24 | # Create a copy of the number list using [:] 25 | numbers_copy = numbers[:] 26 | 27 | 28 | # Exercise 5 29 | # Sort the numbers list in numerical order 30 | numbers.sort() 31 | print(numbers) 32 | -------------------------------------------------------------------------------- /ch09-lists-tuples-and-dictionaries/4-challenge-list-of-lists.py: -------------------------------------------------------------------------------- 1 | # 9.4 - Challenge: List of Lists 2 | # Solution to challenge 3 | 4 | 5 | def enrollment_stats(list_of_universities): 6 | # Variables 7 | total_students = [] 8 | total_tuition = [] 9 | 10 | # Iterate through lists, adding values 11 | for university in list_of_universities: 12 | total_students.append(university[1]) 13 | total_tuition.append(university[2]) 14 | 15 | # Return variables 16 | return total_students, total_tuition 17 | 18 | 19 | def mean(values): 20 | """Return the mean value in the list `values`""" 21 | return sum(values) / len(values) 22 | 23 | 24 | def median(values): 25 | """Return the median value of the list `values`""" 26 | values.sort() 27 | # If the number of values is odd, 28 | # return the middle value of the list 29 | if len(values) % 2 == 1: 30 | # The value at the center of the list is the value 31 | # at whose index is half of the length of the list, 32 | # rounded down 33 | center_index = int(len(values) / 2) 34 | return values[center_index] 35 | # Otherwise, if the length of the list is even, return 36 | # the mean of the two center values 37 | else: 38 | left_center_index = (len(values) - 1) // 2 39 | right_center_index = (len(values) + 1) // 2 40 | return mean([values[left_center_index], values[right_center_index]]) 41 | 42 | 43 | universities = [ 44 | ["California Institute of Technology", 2175, 37704], 45 | ["Harvard", 19627, 39849], 46 | ["Massachusetts Institute of Technology", 10566, 40732], 47 | ["Princeton", 7802, 37000], 48 | ["Rice", 5879, 35551], 49 | ["Stanford", 19535, 40569], 50 | ["Yale", 11701, 40500], 51 | ] 52 | 53 | totals = enrollment_stats(universities) 54 | 55 | print("\n") 56 | print("*****" * 6) 57 | print(f"Total students: {sum(totals[0]):,}") 58 | print(f"Total tuition: $ {sum(totals[1]):,}") 59 | print(f"\nStudent mean: {mean(totals[0]):,.2f}") 60 | print(f"Student median: {median(totals[0]):,}") 61 | print(f"\nTuition mean: $ {mean(totals[1]):,.2f}") 62 | print(f"Tuition median: $ {median(totals[1]):,}") 63 | print("*****" * 6) 64 | print("\n") 65 | -------------------------------------------------------------------------------- /ch09-lists-tuples-and-dictionaries/5-challenge-wax-poetic.py: -------------------------------------------------------------------------------- 1 | # 9.5 - Challenge: Wax Poetic 2 | # Solution to challenge 3 | 4 | 5 | # Generate a random poem based on a set structure 6 | 7 | import random 8 | 9 | noun = [ 10 | "fossil", 11 | "horse", 12 | "aardvark", 13 | "judge", 14 | "chef", 15 | "mango", 16 | "extrovert", 17 | "gorilla", 18 | ] 19 | verb = [ 20 | "kicks", 21 | "jingles", 22 | "bounces", 23 | "slurps", 24 | "meows", 25 | "explodes", 26 | "curdles", 27 | ] 28 | adjective = [ 29 | "furry", 30 | "balding", 31 | "incredulous", 32 | "fragrant", 33 | "exuberant", 34 | "glistening", 35 | ] 36 | preposition = [ 37 | "against", 38 | "after", 39 | "into", 40 | "beneath", 41 | "upon", 42 | "for", 43 | "in", 44 | "like", 45 | "over", 46 | "within", 47 | ] 48 | adverb = [ 49 | "curiously", 50 | "extravagantly", 51 | "tantalizingly", 52 | "furiously", 53 | "sensuously", 54 | ] 55 | 56 | 57 | def make_poem(): 58 | """Create a randomly generated poem, returned as a multi-line string.""" 59 | # Pull three nouns randomly 60 | n1 = random.choice(noun) 61 | n2 = random.choice(noun) 62 | n3 = random.choice(noun) 63 | # Make sure that all the nouns are different 64 | while n1 == n2: 65 | n2 = random.choice(noun) 66 | while n1 == n3 or n2 == n3: 67 | n3 = random.choice(noun) 68 | 69 | # Pull three different verbs 70 | v1 = random.choice(verb) 71 | v2 = random.choice(verb) 72 | v3 = random.choice(verb) 73 | while v1 == v2: 74 | v2 = random.choice(verb) 75 | while v1 == v3 or v2 == v3: 76 | v3 = random.choice(verb) 77 | 78 | # Pull three different adjectives 79 | adj1 = random.choice(adjective) 80 | adj2 = random.choice(adjective) 81 | adj3 = random.choice(adjective) 82 | while adj1 == adj2: 83 | adj2 = random.choice(adjective) 84 | while adj1 == adj3 or adj2 == adj3: 85 | adj3 = random.choice(adjective) 86 | 87 | # Pull two different prepositions 88 | prep1 = random.choice(preposition) 89 | prep2 = random.choice(preposition) 90 | while prep1 == prep2: 91 | prep2 = random.choice(preposition) 92 | 93 | # Pull one adverb 94 | adv1 = random.choice(adverb) 95 | 96 | if "aeiou".find(adj1[0]) != -1: # First letter is a vowel 97 | article = "An" 98 | else: 99 | article = "A" 100 | 101 | # Create the poem 102 | poem = ( 103 | f"{article} {adj1} {n1}\n\n" 104 | f"{article} {adj1} {n1} {v1} {prep1} the {adj2} {n2}\n" 105 | f"{adv1}, the {n1} {v2}\n" 106 | f"the {n2} {v3} {prep2} a {adj3} {n3}" 107 | ) 108 | 109 | return poem 110 | 111 | 112 | poem = make_poem() 113 | print(poem) 114 | -------------------------------------------------------------------------------- /ch09-lists-tuples-and-dictionaries/6-store-relationships-in-dictionaries.py: -------------------------------------------------------------------------------- 1 | # 9.6 - Store Relationships in Dictionaries 2 | # Solutions to review exercises 3 | 4 | 5 | # Exercise 1 6 | # Create an empty dictionary 7 | captains = {} 8 | 9 | 10 | # Exercise 2 11 | # Add some key-value pairs to the dictionary 12 | captains["Enterprise"] = "Picard" 13 | captains["Voyager"] = "Janeway" 14 | captains["Defiant"] = "Sisko" 15 | 16 | 17 | # Exercise 3 18 | # Check if "Enterprise" and "Discovery" exist; if not, add them 19 | if "Enterprise" not in captains: 20 | captains["Enterprise"] = "unknown" 21 | if "Discovery" not in captains: 22 | captains["Discovery"] = "unknown" 23 | 24 | # Bonus points: you could instead loop over a list of names to check 25 | # for ship in ["Enterprise", "Discovery"]: 26 | # if not ship in captains: 27 | # captains[ship] = "unknown" 28 | 29 | 30 | # Exercise 4 31 | # Display the contents of the dictionary, one pair at a time 32 | for ship, captain in captains.items(): 33 | print(f"The {ship} is captained by {captain}.") 34 | 35 | 36 | # Exercise 5 37 | # Remove "Discovery" 38 | del captains["Discovery"] 39 | 40 | 41 | # Exercise 6 (Bonus) 42 | # Create dictionary by passing a list to dict() 43 | captains = dict( 44 | [ 45 | ("Enterprise", "Picard"), 46 | ("Voyager", "Janeway"), 47 | ("Defiant", "Sisko"), 48 | ] 49 | ) 50 | -------------------------------------------------------------------------------- /ch09-lists-tuples-and-dictionaries/7-challenge-capital-city-loop.py: -------------------------------------------------------------------------------- 1 | # 9.7 - Challenge: Capital City Loop 2 | # Solution to challenge 3 | 4 | import random 5 | 6 | capitals_dict = { 7 | "Alabama": "Montgomery", 8 | "Alaska": "Juneau", 9 | "Arizona": "Phoenix", 10 | "Arkansas": "Little Rock", 11 | "California": "Sacramento", 12 | "Colorado": "Denver", 13 | "Connecticut": "Hartford", 14 | "Delaware": "Dover", 15 | "Florida": "Tallahassee", 16 | "Georgia": "Atlanta", 17 | "Hawaii": "Honolulu", 18 | "Idaho": "Boise", 19 | "Illinois": "Springfield", 20 | "Indiana": "Indianapolis", 21 | "Iowa": "Des Moines", 22 | "Kansas": "Topeka", 23 | "Kentucky": "Frankfort", 24 | "Louisiana": "Baton Rouge", 25 | "Maine": "Augusta", 26 | "Maryland": "Annapolis", 27 | "Massachusetts": "Boston", 28 | "Michigan": "Lansing", 29 | "Minnesota": "Saint Paul", 30 | "Mississippi": "Jackson", 31 | "Missouri": "Jefferson City", 32 | "Montana": "Helena", 33 | "Nebraska": "Lincoln", 34 | "Nevada": "Carson City", 35 | "New Hampshire": "Concord", 36 | "New Jersey": "Trenton", 37 | "New Mexico": "Santa Fe", 38 | "New York": "Albany", 39 | "North Carolina": "Raleigh", 40 | "North Dakota": "Bismarck", 41 | "Ohio": "Columbus", 42 | "Oklahoma": "Oklahoma City", 43 | "Oregon": "Salem", 44 | "Pennsylvania": "Harrisburg", 45 | "Rhode Island": "Providence", 46 | "South Carolina": "Columbia", 47 | "South Dakota": "Pierre", 48 | "Tennessee": "Nashville", 49 | "Texas": "Austin", 50 | "Utah": "Salt Lake City", 51 | "Vermont": "Montpelier", 52 | "Virginia": "Richmond", 53 | "Washington": "Olympia", 54 | "West Virginia": "Charleston", 55 | "Wisconsin": "Madison", 56 | "Wyoming": "Cheyenne", 57 | } 58 | 59 | # Pull random state and capital pair from the dict by casting to list of tuples 60 | state, capital = random.choice(list(capitals_dict.items())) 61 | 62 | # Game loop continues until the user inputs "exit" 63 | # or guesses the correct capital 64 | while True: 65 | guess = input(f"What is the capital of '{state}'? ").lower() 66 | if guess == "exit": 67 | print(f"The capital of '{state}' is '{capital}'.") 68 | print("Goodbye") 69 | break 70 | elif guess == capital.lower(): 71 | print("Correct! Nice job.") 72 | break 73 | -------------------------------------------------------------------------------- /ch09-lists-tuples-and-dictionaries/9a-challenge-cats-with-hats.py: -------------------------------------------------------------------------------- 1 | # 9.9 - Challenge: Cats With Hats 2 | # Solution to challenge 3 | 4 | 5 | def get_cats_with_hats(array_of_cats): 6 | cats_with_hats_on = [] 7 | # We want to walk around the circle 100 times 8 | for num in range(1, 100 + 1): 9 | # Each time we walk around, we visit 100 cats 10 | for cat in range(1, 100 + 1): 11 | # Determine whether to visit the cat 12 | # Use modulo operator to visit every 2nd, 3rd, 4th,... etc. 13 | if cat % num == 0: 14 | # Remove or add hat depending on 15 | # whether the cat already has one 16 | if array_of_cats[cat] is True: 17 | array_of_cats[cat] = False 18 | else: 19 | array_of_cats[cat] = True 20 | 21 | # Add all number of each cat with a hat to list 22 | for cat in range(1, 100 + 1): 23 | if array_of_cats[cat] is True: 24 | cats_with_hats_on.append(cat) 25 | 26 | # Return the resulting list 27 | return cats_with_hats_on 28 | 29 | 30 | # Cats contains whether each cat already has a hat on, 31 | # by default all are set to false since none have been visited 32 | cats = [False] * (100 + 1) 33 | print(get_cats_with_hats(cats)) 34 | -------------------------------------------------------------------------------- /ch09-lists-tuples-and-dictionaries/9b-challenge-cats-with-hats.py: -------------------------------------------------------------------------------- 1 | # 9.9 - Challenge: Cats With Hats 2 | # Alternative solution to challenge 3 | 4 | 5 | number_of_cats = 100 6 | cats_with_hats = [] 7 | number_of_laps = 100 8 | 9 | # We want the laps to be from 1 to 100 instead of 0 to 99 10 | for lap in range(1, number_of_laps + 1): 11 | for cat in range(1, number_of_cats + 1): 12 | 13 | # Only look at cats that are divisible by the lap 14 | if cat % lap == 0: 15 | if cat in cats_with_hats: 16 | cats_with_hats.remove(cat) 17 | else: 18 | cats_with_hats.append(cat) 19 | 20 | print(cats_with_hats) 21 | -------------------------------------------------------------------------------- /ch09-lists-tuples-and-dictionaries/9c-challenge-cats-with-hats.py: -------------------------------------------------------------------------------- 1 | # 9.9 - Challenge: Cats With Hats 2 | # Alternative solution to challenge using dictionaries 3 | 4 | 5 | theCats = {} 6 | 7 | # By default, no cats have been visited 8 | # so we set every cat's number to False 9 | for i in range(1, 101): 10 | theCats[i] = False 11 | 12 | # Walk around the circle 100 times 13 | for i in range(1, 101): 14 | # Visit all cats each time we do a lap 15 | for cats, hats in theCats.items(): 16 | # Determine whether or not we visit a cat 17 | if cats % i == 0: 18 | # Add or remove the hat 19 | if theCats[cats]: 20 | theCats[cats] = False 21 | else: 22 | theCats[cats] = True 23 | 24 | # Print whether each cat has a hat 25 | for cats, hats in theCats.items(): 26 | if theCats[cats]: 27 | print(f"Cat {cats} has a hat.") 28 | else: 29 | print(f"Cat {cats} is hatless!") 30 | -------------------------------------------------------------------------------- /ch10-primer-on-oop/2-instantiate-an-object.py: -------------------------------------------------------------------------------- 1 | # 10.2 - Instantiate an Object 2 | # Solutions to review exercises 3 | 4 | 5 | # Exercise 1 6 | class Dog: 7 | 8 | species = "Canis familiaris" 9 | 10 | def __init__(self, name, age, coat_color): 11 | self.name = name 12 | self.age = age 13 | self.coat_color = coat_color 14 | 15 | def __str__(self): 16 | return f"{self.name} is {self.age} years old" 17 | 18 | def speak(self, sound): 19 | return f"{self.name} says {sound}" 20 | 21 | 22 | # The value for `age` can vary in your solution 23 | philo = Dog("Philo", 5, "brown") 24 | print(f"{philo.name}'s coat is {philo.coat_color}.") 25 | 26 | 27 | # Exercise 2 28 | class Car: 29 | def __init__(self, color, mileage): 30 | self.color = color 31 | self.mileage = mileage 32 | 33 | 34 | blue_car = Car("blue", 20000) 35 | red_car = Car("red", 30000) 36 | 37 | for car in (blue_car, red_car): 38 | print(f"The {car.color} car has {car.mileage:,} miles") 39 | 40 | 41 | # Exercise 3 42 | class Car: 43 | def __init__(self, color, mileage): 44 | self.color = color 45 | self.mileage = mileage 46 | 47 | def drive(self, miles): 48 | self.mileage = self.mileage + miles 49 | 50 | 51 | blue_car = Car("blue", 0) 52 | blue_car.drive(100) 53 | print(blue_car.mileage) 54 | -------------------------------------------------------------------------------- /ch10-primer-on-oop/3-inherit-from-other-classes.py: -------------------------------------------------------------------------------- 1 | # 10.3 - Inherit From Other Classes 2 | # Solutions to review exercises 3 | 4 | 5 | # Exercise 1 6 | # The parent `Dog` class (given in exercise) 7 | class Dog: 8 | species = "Canis familiaris" 9 | 10 | def __init__(self, name, age): 11 | self.name = name 12 | self.age = age 13 | 14 | def __str__(self): 15 | return f"{self.name} is {self.age} years old" 16 | 17 | def speak(self, sound): 18 | return f"{self.name} says {sound}" 19 | 20 | 21 | # The GoldenRetriever class that solves the exercise 22 | class GoldenRetriever(Dog): 23 | def speak(self, sound="Bark"): 24 | return super().speak(sound) 25 | 26 | 27 | # Exercise 2 28 | # Rectangle and Square classes 29 | class Rectangle: 30 | def __init__(self, length, width): 31 | self.length = length 32 | self.width = width 33 | 34 | def area(self): 35 | return self.length * self.width 36 | 37 | 38 | class Square(Rectangle): 39 | def __init__(self, side_length): 40 | super().__init__(side_length, side_length) 41 | 42 | 43 | square = Square(4) 44 | print(square.area()) # 16 45 | 46 | square.width = 5 # Modifies .width but not .length 47 | print(square.area()) # 20 48 | -------------------------------------------------------------------------------- /ch10-primer-on-oop/4-challenge-model-a-farm.py: -------------------------------------------------------------------------------- 1 | # 10.4 - Challenge: Model a Farm 2 | # Solutions to challenge 3 | 4 | 5 | class Animal: 6 | 7 | # Class attributes 8 | stuff_in_belly = 0 9 | position = 0 10 | 11 | # Initializer 12 | def __init__(self, name, color): 13 | self.name = name 14 | self.color = color 15 | 16 | # Instance methods 17 | def talk(self, sound=None): 18 | """Return the string " says " 19 | 20 | If `sound` is left out, returns "Hello, I'm " 21 | """ 22 | if sound is None: 23 | return f"Hello. I'm {self.name}!" 24 | return f"{self.name} says {sound}" 25 | 26 | def walk(self, walk_increment): 27 | self.position = self.position + walk_increment 28 | return self.position 29 | 30 | def run(self, run_increment): 31 | self.position = self.position + run_increment 32 | return self.position 33 | 34 | def feed(self): 35 | self.stuff_in_belly = self.stuff_in_belly + 1 36 | if self.stuff_in_belly > 3: 37 | return self.poop() 38 | else: 39 | return f"{self.name} is eating." 40 | 41 | def is_hungry(self): 42 | if self.stuff_in_belly < 2: 43 | return f"{self.name} is hungry" 44 | else: 45 | return f"{self.name} is not hungry" 46 | 47 | def poop(self): 48 | self.stuff_in_belly = 0 49 | return "Ate too much ... need to find a bathroom" 50 | 51 | 52 | class Dog(Animal): 53 | def talk(self, sound="Bark Bark!"): 54 | return super().talk(sound) 55 | 56 | def fetch(self): 57 | return f"{self.name} is fetching." 58 | 59 | 60 | class Sheep(Animal): 61 | def talk(self, sound="Baaa Baaa"): 62 | return super().talk(sound) 63 | 64 | 65 | class Pig(Animal): 66 | def talk(self, sound="Oink Oink"): 67 | return super().talk(sound) 68 | 69 | 70 | # The following code illustrates how to use the classes defined above. 71 | # It is not necesarrily a part of the solution, and is included for 72 | # illustration purposes only. 73 | 74 | # Create a dog 75 | dog = Dog("Blitzer", "yellow") 76 | 77 | # Output the dog's attributes 78 | print(f"Our dog's name is {dog.name}.") 79 | print(f"And he's {dog.color}.") 80 | 81 | # Output some behavior 82 | print(f"Say something, {dog.name}.") 83 | print(dog.talk()) 84 | print("Go fetch!") 85 | print(dog.fetch()) 86 | 87 | # Walk the dog 88 | print(f"{dog.name} is at position {dog.walk(2)}.") 89 | 90 | # Run the dog 91 | print(f"{dog.name} is now at position {dog.run(4)}") 92 | 93 | # Feed the dog 94 | print(dog.feed()) 95 | 96 | # Is the dog hungry? 97 | print(dog.is_hungry()) 98 | 99 | # Feed the dog more 100 | print(dog.feed()) 101 | print(dog.feed()) 102 | print(dog.is_hungry()) 103 | print(dog.feed()) 104 | 105 | print("\n") 106 | 107 | # Create a sheep 108 | sheep = Sheep("Shaun", "white") 109 | 110 | # The sheep talks! 111 | print(sheep.talk()) 112 | 113 | # When the sheep runs, the distance is returned 114 | print(sheep.run(2)) 115 | print(sheep.run(2)) 116 | 117 | print("\n") 118 | 119 | # Create a pig 120 | pig = Pig("Carl", "pink") 121 | 122 | # Pigs love to oink! 123 | print(pig.talk()) 124 | -------------------------------------------------------------------------------- /ch11-modules-and-packages/1-working-with-modules/greeter.py: -------------------------------------------------------------------------------- 1 | # Ch 11.1 - Modules and Packages 2 | # Solution to Exercise 1 3 | 4 | 5 | def greet(name): 6 | print(f"Hello {name}!") 7 | -------------------------------------------------------------------------------- /ch11-modules-and-packages/1-working-with-modules/main.py: -------------------------------------------------------------------------------- 1 | # Ch 11.1 - Modules and Packages 2 | # Solution to Exercise 2 3 | 4 | import greeter 5 | 6 | 7 | greeter.greet("Real Python") 8 | -------------------------------------------------------------------------------- /ch11-modules-and-packages/2-working-with-packages/packages_exercises/helpers/__init__.py: -------------------------------------------------------------------------------- 1 | # Ch 11.2 - Working With Packages 2 | # __init__.py - Part of solution to Exercise 1 3 | -------------------------------------------------------------------------------- /ch11-modules-and-packages/2-working-with-packages/packages_exercises/helpers/math.py: -------------------------------------------------------------------------------- 1 | # Ch 11.2 - Working With Packages 2 | # helpers/math.py - Part of solution to Exercise 1 3 | 4 | 5 | def area(length, width): 6 | return length * width 7 | -------------------------------------------------------------------------------- /ch11-modules-and-packages/2-working-with-packages/packages_exercises/helpers/string.py: -------------------------------------------------------------------------------- 1 | # Ch 11.2 - Working With Packages 2 | # helpers/string.py - Part of solution to Exercise 1 3 | 4 | 5 | def shout(string): 6 | return string.upper() 7 | -------------------------------------------------------------------------------- /ch11-modules-and-packages/2-working-with-packages/packages_exercises/main.py: -------------------------------------------------------------------------------- 1 | # Ch 11.2 - Working With Packages 2 | # main.py - Solution to Exercise 2 3 | 4 | from helpers.string import shout 5 | from helpers.math import area 6 | 7 | 8 | length = 5 9 | width = 8 10 | message = f"The area of a {length}-by-{width} rectangle is {area(length, width)}" 11 | print(shout(message)) 12 | -------------------------------------------------------------------------------- /ch12-file-input-and-output/2-working-with-file-paths-in-python.py: -------------------------------------------------------------------------------- 1 | # 12.2 - Working With File Paths in Python 2 | # Solutions to review exercises 3 | 4 | 5 | # Exercise 1 6 | from pathlib import Path 7 | 8 | file_path = Path.home() / "my_folder" / "my_file.txt" 9 | 10 | 11 | # Exercise 2 12 | print(file_path.exists()) 13 | 14 | 15 | # Exercise 3 16 | print(file_path.name) 17 | 18 | 19 | # Exercise 4 20 | print(file_path.parent.name) 21 | -------------------------------------------------------------------------------- /ch12-file-input-and-output/3-common-file-system-operations.py: -------------------------------------------------------------------------------- 1 | # 12.3 Common File System Operations 2 | # Solutions to Exercises 3 | 4 | 5 | # Exercise 1 6 | from pathlib import Path 7 | 8 | new_dir = Path.home() / "my_folder" 9 | new_dir.mkdir() 10 | 11 | 12 | # Exercise 2 13 | file1 = new_dir / "file1.txt" 14 | file2 = new_dir / "file2.txt" 15 | image1 = new_dir / "image1.png" 16 | 17 | file1.touch() 18 | file2.touch() 19 | image1.touch() 20 | 21 | 22 | # Exercise 3 23 | images_dir = new_dir / "images" 24 | images_dir.mkdir() 25 | image1.replace(images_dir / "image1.png") 26 | 27 | 28 | # Exercise 4 29 | file1.unlink() 30 | 31 | 32 | # Exercise 5 33 | import shutil 34 | 35 | shutil.rmtree(new_dir) 36 | -------------------------------------------------------------------------------- /ch12-file-input-and-output/4-challenge-move-all-image-files-to-a-new-directory.py: -------------------------------------------------------------------------------- 1 | # 12.4 Challenge: Move All Image Files To a New Directory 2 | # Solution to Challenge 3 | 4 | from pathlib import Path 5 | 6 | # Change this path to match the location on your computer 7 | documents_dir = Path.cwd() / "practice_files" / "documents" 8 | 9 | # Create an images/ directory in your home directory 10 | images_dir = Path.home() / "images" 11 | images_dir.mkdir(exist_ok=True) 12 | 13 | # Search for image files in the documents directory and move 14 | # them to the images/ directory 15 | for path in documents_dir.rglob("*.*"): 16 | if path.suffix.lower() in [".png", ".jpg", ".gif"]: 17 | path.replace(images_dir / path.name) 18 | -------------------------------------------------------------------------------- /ch12-file-input-and-output/5-reading-and-writing-files.py: -------------------------------------------------------------------------------- 1 | # 12.5 - Reading and Writing Files 2 | # Solutions to Exercises 3 | 4 | 5 | # Exercise 1 6 | from pathlib import Path 7 | 8 | starships = ["Discovery\n", "Enterprise\n", "Defiant\n", "Voyager"] 9 | 10 | file_path = Path.home() / "starships.txt" 11 | with file_path.open(mode="w", encoding="utf-8") as file: 12 | file.writelines(starships) 13 | 14 | 15 | # Exercise 2 16 | with file_path.open(mode="r", encoding="utf-8") as file: 17 | for starship in file.readlines(): 18 | print(starship, end="") 19 | 20 | 21 | # Exercise 3 22 | with file_path.open(mode="r", encoding="utf-8") as file: 23 | for starship in file.readlines(): 24 | if starship.startswith("D"): 25 | print(starship, end="") 26 | -------------------------------------------------------------------------------- /ch12-file-input-and-output/6-read-and-write-csv-data.py: -------------------------------------------------------------------------------- 1 | # 12.5 Read and Write CSV Data 2 | # Solutions to Exercises 3 | 4 | 5 | # Exercise 1 6 | import csv 7 | from pathlib import Path 8 | 9 | numbers = [ 10 | [1, 2, 3, 4, 5], 11 | [6, 7, 8, 9, 10], 12 | [11, 12, 13, 14, 15], 13 | ] 14 | 15 | file_path = Path.home() / "numbers.csv" 16 | 17 | with file_path.open(mode="w", encoding="utf-8") as file: 18 | writer = csv.writer(file) 19 | writer.writerows(numbers) 20 | 21 | 22 | # Exercise 2 23 | numbers = [] 24 | 25 | with file_path.open(mode="r", encoding="utf-8") as file: 26 | reader = csv.reader(file) 27 | for row in reader: 28 | int_row = [int(num) for num in row] 29 | numbers.append(int_row) 30 | 31 | print(numbers) 32 | 33 | 34 | # Exercise 3 35 | favorite_colors = [ 36 | {"name": "Joe", "favorite_color": "blue"}, 37 | {"name": "Anne", "favorite_color": "green"}, 38 | {"name": "Bailey", "favorite_color": "red"}, 39 | ] 40 | 41 | file_path = Path.home() / "favorite_colors.csv" 42 | 43 | with file_path.open(mode="w", encoding="utf-8") as file: 44 | writer = csv.DictWriter(file, fieldnames=["name", "favorite_color"]) 45 | writer.writeheader() 46 | writer.writerows(favorite_colors) 47 | 48 | 49 | # Exercise 4 50 | favorite_colors = [] 51 | 52 | with file_path.open(mode="r", encoding="utf-8") as file: 53 | reader = csv.DictReader(file) 54 | for row in reader: 55 | favorite_colors.append(row) 56 | 57 | print(favorite_colors) 58 | -------------------------------------------------------------------------------- /ch12-file-input-and-output/7-challenge-create-a-high-scores-list.py: -------------------------------------------------------------------------------- 1 | # 12.7 Challenge: Create a High Scores List 2 | # Solution to Challenge 3 | 4 | import csv 5 | from pathlib import Path 6 | 7 | # Change the path below to match the location on your computer 8 | scores_csv_path = ( 9 | Path.cwd() 10 | / "practice_files" 11 | / "scores.csv" 12 | ) 13 | 14 | with scores_csv_path.open(mode="r", encoding="utf-8") as file: 15 | reader = csv.DictReader(file) 16 | scores = [row for row in reader] 17 | 18 | high_scores = {} 19 | for item in scores: 20 | name = item["name"] 21 | score = int(item["score"]) 22 | # If the name has not been added to the high_score dictionary, then 23 | # create a new key with the name and set its value to the score 24 | if name not in high_scores: 25 | high_scores[name] = score 26 | # Otherwise, check to see if score is greater than the score currently 27 | # assigned to high_scores[name] and replace it if it is 28 | else: 29 | if score > high_scores[name]: 30 | high_scores[name] = score 31 | 32 | # The high_scores dictionary now contains one key for each name that was 33 | # in the scores.csv file, and each value is that player's highest score. 34 | 35 | output_csv_file = Path.cwd() / "high_scores.csv" 36 | with output_csv_file.open(mode="w", encoding="utf-8") as file: 37 | writer = csv.DictWriter(file, fieldnames=["name", "high_score"]) 38 | writer.writeheader() 39 | for name in high_scores: 40 | row_dict = {"name": name, "high_score": high_scores[name]} 41 | writer.writerow(row_dict) 42 | -------------------------------------------------------------------------------- /ch12-file-input-and-output/practice_files/documents/files/additional files/image3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/python-basics-exercises/fe1ec67dd6aa5202b09c2fa4d30b531f168e753f/ch12-file-input-and-output/practice_files/documents/files/additional files/image3.png -------------------------------------------------------------------------------- /ch12-file-input-and-output/practice_files/documents/files/dad.txt: -------------------------------------------------------------------------------- 1 | Yo dad, what's up? 2 | -------------------------------------------------------------------------------- /ch12-file-input-and-output/practice_files/documents/files/stuff.csv: -------------------------------------------------------------------------------- 1 | 1,2,3,4,5 2 | a,b,c,d,e 3 | -------------------------------------------------------------------------------- /ch12-file-input-and-output/practice_files/documents/image1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/python-basics-exercises/fe1ec67dd6aa5202b09c2fa4d30b531f168e753f/ch12-file-input-and-output/practice_files/documents/image1.png -------------------------------------------------------------------------------- /ch12-file-input-and-output/practice_files/documents/more_files/even_more_files/image4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/python-basics-exercises/fe1ec67dd6aa5202b09c2fa4d30b531f168e753f/ch12-file-input-and-output/practice_files/documents/more_files/even_more_files/image4.jpg -------------------------------------------------------------------------------- /ch12-file-input-and-output/practice_files/documents/more_files/even_more_files/the_answer.txt: -------------------------------------------------------------------------------- 1 | 42 2 | -------------------------------------------------------------------------------- /ch12-file-input-and-output/practice_files/documents/more_files/image2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/python-basics-exercises/fe1ec67dd6aa5202b09c2fa4d30b531f168e753f/ch12-file-input-and-output/practice_files/documents/more_files/image2.gif -------------------------------------------------------------------------------- /ch12-file-input-and-output/practice_files/documents/more_files/mom.txt: -------------------------------------------------------------------------------- 1 | hi mom -------------------------------------------------------------------------------- /ch12-file-input-and-output/practice_files/scores.csv: -------------------------------------------------------------------------------- 1 | name,score 2 | LLCoolDave,23 3 | LLCoolDave,27 4 | red,12 5 | LLCoolDave,26 6 | tom123,26 7 | O_O,7 8 | Misha46,24 9 | O_O,14 10 | Empiro,18 11 | Empiro,18 12 | MaxxT,25 13 | L33tH4x,42 14 | Misha46,25 15 | johnsmith,30 16 | Empiro,23 17 | O_O,22 18 | MaxxT,25 19 | Misha46,24 20 | -------------------------------------------------------------------------------- /ch14-interact-with-pdf-files/1-extract-text-from-a-pdf.py: -------------------------------------------------------------------------------- 1 | # 14.1 - Extract Text From a PDF 2 | # Solutions to review exercises 3 | 4 | 5 | # *********** 6 | # Exercise 1 7 | # 8 | # In the Chapter 13 Practice Files directory there is a PDF file called 9 | # `zen.pdf`. Create a `PdfFileReader` from this PDF. 10 | # *********** 11 | 12 | # Before you can do anything, you need to import the right objects from 13 | # the PyPDF2 and pathlib libraries 14 | from pathlib import Path 15 | from PyPDF2 import PdfFileReader 16 | 17 | # To create a PdfFileReader instance, you need to path to the PDF file. 18 | # We'll assume you downloaded the solutions folder and are running this 19 | # program from the solutions folder. If this is not the case, you'll 20 | # need to update the path below. 21 | pdf_path = Path.cwd() / "practice_files" / "zen.pdf" 22 | 23 | # Now you can create the PdfFileReader instance. Remember that 24 | # PdfFileReader objects can only be instantiated with path strings, not 25 | # Path objects! 26 | pdf_reader = PdfFileReader(str(pdf_path)) 27 | 28 | 29 | # *********** 30 | # Exercise 2 31 | # 32 | # Using the `PdfFileReader` instance from Exercise 1, print the total 33 | # number of pages in the PDF. 34 | # *********** 35 | 36 | # Use .getNumPages() to get the number of pages, then print the result 37 | # using the print() built-in 38 | num_pages = pdf_reader.getNumPages() 39 | print(num_pages) 40 | 41 | 42 | # *********** 43 | # Exercise 3 44 | # 45 | # Print the text from the first page of the PDF in Exercise 1. 46 | # *********** 47 | 48 | # Use .getPage() to get the first page. Remember pages are indexed 49 | # starting with 0! 50 | first_page = pdf_reader.getPage(0) 51 | 52 | # Then use .extractText() to extract the text 53 | text = first_page.extractText() 54 | 55 | # Finally, print the text 56 | print(text) 57 | 58 | 59 | # **NOTE**: The text in zen.pdf is from "The Zen Of Python" written by 60 | # Tim Peters in 1999. The Zen is a collection of 19 guiding principles 61 | # for developing with Python. The story goes that there are actually 20 62 | # such principles, but only 19 were written down! 63 | # 64 | # You can see the original submission for The Zen of Python in PEP20: 65 | # https://www.python.org/dev/peps/pep-0020/ 66 | # 67 | # For some historical context surrounding The Zen, see: 68 | # https://mail.python.org/pipermail/python-list/1999-June/001951.html 69 | # 70 | # Author Al Seigart has an interpretation of The Zen on his blog: 71 | # https://inventwithpython.com/blog/2018/08/17/the-zen-of-python-explained/ 72 | # 73 | # Moshe Zadka has another great article on The Zen: 74 | # https://orbifold.xyz/zen-of-python.html 75 | -------------------------------------------------------------------------------- /ch14-interact-with-pdf-files/2-extract-pages-from-a-pdf.py: -------------------------------------------------------------------------------- 1 | # 14.2 - Extract Pages From a PDF 2 | # Solutions to review exercises 3 | 4 | # *********** 5 | # Exercise 1 6 | # 7 | # Extract the last page from the `Pride_and_Prejudice.pdf` file and 8 | # save it to a new file called `last_page.pdf` in your home directory. 9 | # *********** 10 | 11 | # First import the classes and libraries needed 12 | from pathlib import Path 13 | from PyPDF2 import PdfFileReader, PdfFileWriter 14 | 15 | # Get the path to the `Pride_and_Prejudice.pdf` file. We'll assume you 16 | # downloaded the solutions folder and extracted it into the home 17 | # directory on your computer. If this is not the case, you'll need to 18 | # update the path below. 19 | pdf_path = Path.home() / "python-basics-exercises/ch14-interact-with-pdf-files/practice_files/Pride_and_Prejudice.pdf" 20 | 21 | # Now you can create the PdfFileReader instance. Remember that 22 | # PdfFileReader objects can only be instantiated with path strings, not 23 | # Path objects! 24 | pdf_reader = PdfFileReader(str(pdf_path)) 25 | 26 | # Use the .pages attribute to get an iterable over all pages in the 27 | # PDF. The last page can be accessed with the index -1. 28 | last_page = pdf_reader.pages[-1] 29 | 30 | # Now you can create a PdfFileWriter instance and add the last page to it. 31 | pdf_writer = PdfFileWriter() 32 | pdf_writer.addPage(last_page) 33 | 34 | # Finally, write the contents of pdf_writer to the file `last_page.pdf` 35 | # in your home directory. 36 | output_path = Path.home() / "last_page.pdf" 37 | with output_path.open(mode="wb") as output_file: 38 | pdf_writer.write(output_file) 39 | 40 | 41 | # *********** 42 | # Exercise 2 43 | # 44 | # Extract all pages with even numbered _indices_ from the 45 | # `Pride_and_Prejudice.pdf` and save them to a new file called 46 | # `every_other_page.pdf` in your home directory. 47 | # *********** 48 | 49 | # There are several ways to extract pages with even numbered indices 50 | # so we'll cover a few of them here. 51 | 52 | # Solution A: Using a `for` loop 53 | # ------------------------------ 54 | 55 | # One way to do it is with a `for` loop. We'll create a new PdfFileWriter 56 | # instance, then loop over the numbers 0 up to the number of pages in the 57 | # PDF, and add the pages with even indices to the PdfFileWriter instance. 58 | pdf_writer = PdfFileWriter() 59 | num_pages = pdf_reader.getNumPages() 60 | 61 | for idx in range(num_pages): # NOTE: idx is a common short name for "index" 62 | if idx % 2 == 0: # Check that the index is even 63 | page = pdf_reader.getPage(idx) # Get the page at the index 64 | pdf_writer.addPage(page) # Add the page to `pdf_writer` 65 | 66 | # Now write the contents of `pdf_writer` the the file `every_other_page.pdf` 67 | # in your home directory 68 | output_path = Path.home() / "every_other_page.pdf" 69 | with output_path.open(mode="wb") as output_file: 70 | pdf_writer.write(output_file) 71 | 72 | # Solution B: Slicing .`pages` with steps 73 | # ------------------------------ 74 | 75 | # A more succinct, alghouth possibly more difficult to understand, 76 | # solution involves slicing the `.pages` iterable. The indices start 77 | # with 0 and every even index can be obtained by iterating over 78 | # `.pages` in steps of size 2, so `.pages[::2]` is an iterable 79 | # containing just the pages with even indices. 80 | pdf_writer = PdfFileWriter() 81 | 82 | for page in pdf_reader.pages[::2]: 83 | pdf_writer.addPage(page) 84 | 85 | # Now write the contents of `pdf_writer` the the file 86 | # `every_other_page.pdf` in your home directory. 87 | output_path = Path.home() / "every_other_page.pdf" 88 | with output_path.open(mode="wb") as output_file: 89 | pdf_writer.write(output_file) 90 | 91 | 92 | # *********** 93 | # Exercise 3 94 | # 95 | # Split the `Pride_and_Prejudice.pdf` file into two new PDF files. The 96 | # first file should contain the first 150 pages, and the second file 97 | # should contain the remaining pages. Save both files in your home 98 | # directory as `part_1.pdf` and `part_2.pdf`. 99 | # *********** 100 | 101 | # Start by creating two new PdfFileWriter instances. 102 | part1_writer = PdfFileWriter() 103 | part2_writer = PdfFileWriter() 104 | 105 | # Next, create two new iterables containing the correct pages. 106 | part1_pages = pdf_reader.pages[:150] # Contains pages 0 - 149 107 | part2_pages = pdf_reader.pages[150:] # Contains pages 150 - last page 108 | 109 | # Add the pages to their corresponding writers. 110 | for page in part1_pages: 111 | part1_writer.addPage(page) 112 | 113 | for page in part2_pages: 114 | part2_writer.addPage(page) 115 | 116 | # Now write the contents of each writer to the files `part_1.pdf` and 117 | # `part_2.pdf` in your home directory. 118 | part1_output_path = Path.home() / "part_1.pdf" 119 | with part1_output_path.open(mode="wb") as part1_output_file: 120 | part1_writer.write(part1_output_file) 121 | 122 | part2_output_path = Path.home() / "part_2.pdf" 123 | with part2_output_path.open(mode="wb") as part2_output_file: 124 | part2_writer.write(part2_output_file) 125 | -------------------------------------------------------------------------------- /ch14-interact-with-pdf-files/3-challenge-PdfFileSplitter-class.py: -------------------------------------------------------------------------------- 1 | # 14.5 - Challenge: PdfFileSplitter Class 2 | # Solution to challenge 3 | 4 | from pathlib import Path 5 | 6 | from PyPDF2 import PdfFileReader, PdfFileWriter 7 | 8 | 9 | class PdfFileSplitter: 10 | """Class for splitting a PDF into two files.""" 11 | 12 | def __init__(self, pdf_path): 13 | # Open the PDF file with a new PdfFileReader instance 14 | self.pdf_reader = PdfFileReader(pdf_path) 15 | # Initialize the .writer1 and .writer2 attributes to None 16 | self.writer1 = None 17 | self.writer2 = None 18 | 19 | def split(self, breakpoint): 20 | """Split the PDF into two PdfFileWriter instances""" 21 | # Set .writer1 and .writer2 to new PdfFileWriter intances 22 | self.writer1 = PdfFileWriter() 23 | self.writer2 = PdfFileWriter() 24 | # Add all pages up to, but not including, the breakpoint 25 | # to writer1 26 | for page in self.pdf_reader.pages[:breakpoint]: 27 | self.writer1.addPage(page) 28 | # Add all the remaining pages to writer2 29 | for page in self.pdf_reader.pages[breakpoint:]: 30 | self.writer2.addPage(page) 31 | 32 | def write(self, filename): 33 | """Write both PdfFileWriter instances to files""" 34 | # Write the first file to _1.pdf 35 | with Path(filename + "_1.pdf").open(mode="wb") as output_file: 36 | self.writer1.write(output_file) 37 | # Write the second file to _2.pdf 38 | with Path(filename + "_2.pdf").open(mode="wb") as output_file: 39 | self.writer2.write(output_file) 40 | 41 | 42 | # Split the Pride_and_Prejudice.pdf file into two PDFs, the first 43 | # containing the first 150 pages, and the second containing the 44 | # remaining pages. 45 | pdf_splitter = PdfFileSplitter("ch14-interact-with-pdf-files/practice_files/Pride_and_Prejudice.pdf") 46 | pdf_splitter.split(breakpoint=150) 47 | pdf_splitter.write("pride_split") 48 | -------------------------------------------------------------------------------- /ch14-interact-with-pdf-files/4-concatenating-and-merging-pdfs.py: -------------------------------------------------------------------------------- 1 | # 14.4 - Concatenating and Merging PDFs 2 | # Solutions to review exercises 3 | 4 | # *********** 5 | # Exercise 1 6 | # 7 | # In the Chapter 13 Practice Files directory there are three PDFs called 8 | # `merge1.pdf`, `merge2.pdf`, and `merge3.pdf`. Using a `PdfFileMerger` 9 | # instance, concatenate the two files `merge1.pdf` and `merge2.pdf` 10 | # using`.append()`. 11 | # *********** 12 | 13 | from pathlib import Path 14 | 15 | from PyPDF2 import PdfFileMerger 16 | 17 | 18 | BASE_PATH = Path.cwd() / "practice_files" 19 | 20 | pdf_paths = [BASE_PATH / "merge1.pdf", BASE_PATH / "merge2.pdf"] 21 | pdf_merger = PdfFileMerger() 22 | 23 | for path in pdf_paths: 24 | pdf_merger.append(str(path)) 25 | 26 | output_path = Path.home() / "concatenated.pdf" 27 | with output_path.open(mode="wb") as output_file: 28 | pdf_merger.write(output_file) 29 | 30 | 31 | # *********** 32 | # Exercise 2 33 | # 34 | # Using the same `PdfFileMerger` instance from exercise 1, merge the 35 | # file `merge3.pdf` in-between the pages from `merge1.pdf` and 36 | # `merge2.pdf` using `.merge()`. 37 | # 38 | # The final result should be a PDF with three pages. The first page 39 | # should have the number `1` on it, the second should have `2`, and the 40 | # third should have `3`. 41 | # *********** 42 | 43 | pdf_merger = PdfFileMerger() 44 | pdf_merger.append(str(output_path)) 45 | 46 | pdf_path = BASE_PATH / "merge3.pdf" 47 | pdf_merger.merge(1, str(pdf_path)) 48 | 49 | output_path = Path.home() / "merged.pdf" 50 | with output_path.open(mode="wb") as output_file: 51 | pdf_merger.write(output_file) 52 | -------------------------------------------------------------------------------- /ch14-interact-with-pdf-files/5-rotating-and-cropping-pdf-pages.py: -------------------------------------------------------------------------------- 1 | # 14.5 - Rotating and Cropping PDF pages 2 | # Solutions to review exercises 3 | 4 | # *********** 5 | # Exercise 1 6 | # 7 | # In the Chapter 13 Practice Files folder there is a PDF called 8 | # `split_and_rotate.pdf`. Create a new PDF called `rotated.pdf` in your 9 | # home directory containing the pages of `split_and_rotate.pdf` rotated 10 | # counter-clockwise 90 degrees. 11 | # *********** 12 | 13 | from pathlib import Path 14 | 15 | from PyPDF2 import PdfFileReader, PdfFileWriter 16 | 17 | 18 | pdf_path = Path.cwd() / "practice_files" / "split_and_rotate.pdf" 19 | 20 | pdf_reader = PdfFileReader(str(pdf_path)) 21 | pdf_writer = PdfFileWriter() 22 | 23 | for page in pdf_reader.pages: 24 | rotated_page = page.rotateCounterClockwise(90) 25 | pdf_writer.addPage(rotated_page) 26 | 27 | output_path = Path.home() / "rotated.pdf" 28 | with output_path.open(mode="wb") as output_file: 29 | pdf_writer.write(output_file) 30 | 31 | 32 | # *********** 33 | # Exercise 2 34 | # 35 | # Using the `rotated.pdf` file you created in exercise 1, split each 36 | # page of the PDF vertically in the middle. Create a new PDF called 37 | # `split.pdf` in your home directory containing all of the split pages. 38 | # 39 | # `split.pdf` should have four pages with the numbers `1`, `2`, `3`, 40 | # and `4`, in order. 41 | # *********** 42 | import copy 43 | 44 | pdf_path = Path.home() / "rotated.pdf" 45 | 46 | pdf_reader = PdfFileReader(str(pdf_path)) 47 | pdf_writer = PdfFileWriter() 48 | 49 | for page in pdf_reader.pages: 50 | # Calculate the coordinates at the top center of the page 51 | upper_right_coords = page.mediaBox.upperRight 52 | center_coords = (upper_right_coords[0] / 2, upper_right_coords[1]) 53 | # Create two copies of the page, one for the left side and one for 54 | # the right side 55 | left_page = copy.deepcopy(page) 56 | right_page = copy.deepcopy(page) 57 | # Crop the pages by setting the upper right corner coordinates 58 | # of the left hand page and the upper left corner coordinates of 59 | # the right hand page to the top center coordinates 60 | left_page.mediaBox.upperRight = center_coords 61 | right_page.mediaBox.upperLeft = center_coords 62 | # Add the cropped pages to the PDF writer 63 | pdf_writer.addPage(left_page) 64 | pdf_writer.addPage(right_page) 65 | 66 | output_path = Path.home() / "split.pdf" 67 | with output_path.open(mode="wb") as output_file: 68 | pdf_writer.write(output_file) 69 | -------------------------------------------------------------------------------- /ch14-interact-with-pdf-files/6-encrypting-and-decrypting-pdfs.py: -------------------------------------------------------------------------------- 1 | # 14.6 - Encrypting and Decrypting PDFs 2 | # Solutions to review exercises 3 | 4 | # *********** 5 | # Exercise 1 6 | # 7 | # In the Chapter 13 Practice Files folder there is a PDF file called 8 | # `top_secret.pdf`. Encrypt the file with the user password 9 | # `Unguessable`. Save the encrypted file as in your home directory as 10 | # `top_secret_encrypted.pdf`. 11 | # *********** 12 | 13 | from pathlib import Path 14 | 15 | from PyPDF2 import PdfFileReader, PdfFileWriter 16 | 17 | 18 | pdf_path = Path.cwd() / "practice_files" / "top_secret.pdf" 19 | 20 | pdf_reader = PdfFileReader(str(pdf_path)) 21 | pdf_writer = PdfFileWriter() 22 | 23 | pdf_writer.appendPagesFromReader(pdf_reader) 24 | pdf_writer.encrypt(user_pwd="Unguessable") 25 | 26 | output_path = Path.home() / "top_secret_encrypted.pdf" 27 | with output_path.open(mode="wb") as output_file: 28 | pdf_writer.write(output_file) 29 | 30 | 31 | # *********** 32 | # Exercise 2 33 | # 34 | # Open the `top_secret_encrpyted.pdf` file you created in exercise 1, 35 | # decrypt it, and print the text on the first page of the PDF. 36 | # *********** 37 | 38 | pdf_path = Path.home() / "top_secret_encrypted.pdf" 39 | pdf_reader = PdfFileReader(str(pdf_path)) 40 | 41 | pdf_reader.decrypt("Unguessable") 42 | 43 | first_page = pdf_reader.getPage(0) 44 | print(first_page.extractText()) 45 | -------------------------------------------------------------------------------- /ch14-interact-with-pdf-files/7-challenge-unscramble-a-pdf.py: -------------------------------------------------------------------------------- 1 | # 14.7 - Challenge: Unscramble a PDF 2 | # Solution to challenge 3 | 4 | from pathlib import Path 5 | 6 | from PyPDF2 import PdfFileReader, PdfFileWriter 7 | 8 | 9 | def get_page_text(page): 10 | return page.extractText() 11 | 12 | 13 | pdf_path = Path.cwd() / "practice_files" / "scrambled.pdf" 14 | 15 | pdf_reader = PdfFileReader(str(pdf_path)) 16 | pdf_writer = PdfFileWriter() 17 | 18 | pages = list(pdf_reader.pages) 19 | pages.sort(key=get_page_text) 20 | 21 | for page in pages: 22 | rotation_degrees = page["/Rotate"] 23 | if rotation_degrees != 0: 24 | page.rotateCounterClockwise(rotation_degrees) 25 | pdf_writer.addPage(page) 26 | 27 | output_path = Path.home() / "unscrambled.pdf" 28 | with output_path.open(mode="wb") as output_file: 29 | pdf_writer.write(output_file) 30 | -------------------------------------------------------------------------------- /ch14-interact-with-pdf-files/practice_files/Emperor cover sheet.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/python-basics-exercises/fe1ec67dd6aa5202b09c2fa4d30b531f168e753f/ch14-interact-with-pdf-files/practice_files/Emperor cover sheet.pdf -------------------------------------------------------------------------------- /ch14-interact-with-pdf-files/practice_files/Pride_and_Prejudice.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/python-basics-exercises/fe1ec67dd6aa5202b09c2fa4d30b531f168e753f/ch14-interact-with-pdf-files/practice_files/Pride_and_Prejudice.pdf -------------------------------------------------------------------------------- /ch14-interact-with-pdf-files/practice_files/The Emperor.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/python-basics-exercises/fe1ec67dd6aa5202b09c2fa4d30b531f168e753f/ch14-interact-with-pdf-files/practice_files/The Emperor.pdf -------------------------------------------------------------------------------- /ch14-interact-with-pdf-files/practice_files/expense_reports/Expense report 1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/python-basics-exercises/fe1ec67dd6aa5202b09c2fa4d30b531f168e753f/ch14-interact-with-pdf-files/practice_files/expense_reports/Expense report 1.pdf -------------------------------------------------------------------------------- /ch14-interact-with-pdf-files/practice_files/expense_reports/Expense report 2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/python-basics-exercises/fe1ec67dd6aa5202b09c2fa4d30b531f168e753f/ch14-interact-with-pdf-files/practice_files/expense_reports/Expense report 2.pdf -------------------------------------------------------------------------------- /ch14-interact-with-pdf-files/practice_files/expense_reports/Expense report 3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/python-basics-exercises/fe1ec67dd6aa5202b09c2fa4d30b531f168e753f/ch14-interact-with-pdf-files/practice_files/expense_reports/Expense report 3.pdf -------------------------------------------------------------------------------- /ch14-interact-with-pdf-files/practice_files/half_and_half.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/python-basics-exercises/fe1ec67dd6aa5202b09c2fa4d30b531f168e753f/ch14-interact-with-pdf-files/practice_files/half_and_half.pdf -------------------------------------------------------------------------------- /ch14-interact-with-pdf-files/practice_files/merge1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/python-basics-exercises/fe1ec67dd6aa5202b09c2fa4d30b531f168e753f/ch14-interact-with-pdf-files/practice_files/merge1.pdf -------------------------------------------------------------------------------- /ch14-interact-with-pdf-files/practice_files/merge2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/python-basics-exercises/fe1ec67dd6aa5202b09c2fa4d30b531f168e753f/ch14-interact-with-pdf-files/practice_files/merge2.pdf -------------------------------------------------------------------------------- /ch14-interact-with-pdf-files/practice_files/merge3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/python-basics-exercises/fe1ec67dd6aa5202b09c2fa4d30b531f168e753f/ch14-interact-with-pdf-files/practice_files/merge3.pdf -------------------------------------------------------------------------------- /ch14-interact-with-pdf-files/practice_files/newsletter.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/python-basics-exercises/fe1ec67dd6aa5202b09c2fa4d30b531f168e753f/ch14-interact-with-pdf-files/practice_files/newsletter.pdf -------------------------------------------------------------------------------- /ch14-interact-with-pdf-files/practice_files/quarterly_report/full_report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/python-basics-exercises/fe1ec67dd6aa5202b09c2fa4d30b531f168e753f/ch14-interact-with-pdf-files/practice_files/quarterly_report/full_report.pdf -------------------------------------------------------------------------------- /ch14-interact-with-pdf-files/practice_files/quarterly_report/report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/python-basics-exercises/fe1ec67dd6aa5202b09c2fa4d30b531f168e753f/ch14-interact-with-pdf-files/practice_files/quarterly_report/report.pdf -------------------------------------------------------------------------------- /ch14-interact-with-pdf-files/practice_files/quarterly_report/toc.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/python-basics-exercises/fe1ec67dd6aa5202b09c2fa4d30b531f168e753f/ch14-interact-with-pdf-files/practice_files/quarterly_report/toc.pdf -------------------------------------------------------------------------------- /ch14-interact-with-pdf-files/practice_files/scrambled.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/python-basics-exercises/fe1ec67dd6aa5202b09c2fa4d30b531f168e753f/ch14-interact-with-pdf-files/practice_files/scrambled.pdf -------------------------------------------------------------------------------- /ch14-interact-with-pdf-files/practice_files/split_and_rotate.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/python-basics-exercises/fe1ec67dd6aa5202b09c2fa4d30b531f168e753f/ch14-interact-with-pdf-files/practice_files/split_and_rotate.pdf -------------------------------------------------------------------------------- /ch14-interact-with-pdf-files/practice_files/top_secret.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/python-basics-exercises/fe1ec67dd6aa5202b09c2fa4d30b531f168e753f/ch14-interact-with-pdf-files/practice_files/top_secret.pdf -------------------------------------------------------------------------------- /ch14-interact-with-pdf-files/practice_files/ugly.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/python-basics-exercises/fe1ec67dd6aa5202b09c2fa4d30b531f168e753f/ch14-interact-with-pdf-files/practice_files/ugly.pdf -------------------------------------------------------------------------------- /ch14-interact-with-pdf-files/practice_files/zen.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/python-basics-exercises/fe1ec67dd6aa5202b09c2fa4d30b531f168e753f/ch14-interact-with-pdf-files/practice_files/zen.pdf -------------------------------------------------------------------------------- /ch15-sql-database-connections/1-use-sqlite.py: -------------------------------------------------------------------------------- 1 | # 15.1 - Use SQLite 2 | # Solutions to review exercises 3 | 4 | import sqlite3 5 | 6 | # Create a temporary database connection in RAM 7 | with sqlite3.connect(":memory:") as connection: 8 | c = connection.cursor() 9 | 10 | # Exercise 1 11 | # Create a "Roster" table with Name, Species and Age fields 12 | c.execute("CREATE TABLE Roster(Name TEXT, Species TEXT, Age INT)") 13 | 14 | # Exercise 2 15 | # Add some data into the database 16 | roster_data = ( 17 | ("Benjamin Sisko", "Human", 40), 18 | ("Jadzia Dax", "Trill", 300), 19 | ("Kira Nerys", "Bajoran", 29), 20 | ) 21 | c.executemany("INSERT INTO Roster VALUES(?, ?, ?)", roster_data) 22 | 23 | # Exercise 3 24 | # Update the Name of Jadzia Dax to "Ezri Dax" 25 | c.execute( 26 | "UPDATE Roster SET Name=? WHERE Name=?", ("Ezri Dax", "Jadzia Dax") 27 | ) 28 | 29 | # Exercise 4 30 | # Display the names and ages of everyone classified as Bajoran 31 | c.execute("SELECT Name, Age FROM Roster WHERE Species = 'Bajoran'") 32 | for row in c.fetchall(): 33 | print(row) 34 | -------------------------------------------------------------------------------- /ch16-interacting-with-the-web/1-scrape-and-parse-text-from-websites.py: -------------------------------------------------------------------------------- 1 | # 16.1 - Scrape and Parse Text From Websites 2 | # Solutions to review exercises 3 | 4 | from urllib.request import urlopen 5 | 6 | # Exercise 1 7 | # Get the full HTML from the "dionysus" page 8 | url = "http://olympus.realpython.org/profiles/dionysus" 9 | html_page = urlopen(url) 10 | html_text = html_page.read().decode("utf-8") 11 | 12 | # Exercise 2 13 | # Get the "Name" and "Favorite Color" using .find() 14 | for tag in ["Name: ", "Favorite Color: "]: 15 | tag_start = html_text.find(tag) + len(tag) 16 | tag_end = html_text[tag_start:].find("<") 17 | # Remove extra spaces and newline padding 18 | print(html_text[tag_start : tag_start + tag_end].strip(" \n")) 19 | 20 | 21 | # Exercise 3 22 | # Get the "Name" and "Favorite Color" using regular expressions 23 | import re 24 | 25 | # Match anything up until a new line or HTML tag; non-greedy 26 | for tag in ["Name: .*?[\n<]", "Favorite Color: .*?[\n<]"]: 27 | match_results = re.search(tag, html_text) 28 | # Remove the "Name: " or "Favorite Color: " label from first result 29 | result = re.sub(".*: ", "", match_results.group()) 30 | # Remove extra spaces and newline padding along with opening HTML tag 31 | print(result.strip(" \n<")) 32 | -------------------------------------------------------------------------------- /ch16-interacting-with-the-web/2-use-an-html-parser-to-scrape-websites.py: -------------------------------------------------------------------------------- 1 | # 16.2 - Use an HTML Parser to Scrape Websites 2 | # Solutions to review exercises 3 | 4 | # Make sure BeautifulSoup is installed first with: 5 | # pip3 install beautifulsoup4 6 | 7 | from urllib.request import urlopen 8 | from bs4 import BeautifulSoup 9 | 10 | # Exercise 1 11 | # Get the full HTML from the "profiles" page 12 | base_URL = "http://olympus.realpython.org" 13 | address = base_URL + "/profiles" 14 | html_page = urlopen(address) 15 | html_text = html_page.read().decode("utf-8") 16 | soup = BeautifulSoup(html_text, "html.parser") 17 | 18 | # Exercise 2 19 | # Parse out all the values of the page links 20 | for anchor in soup.find_all("a"): 21 | # Could also have used urlparse.urljoin() to get absolute URL 22 | link_address = base_URL + anchor["href"] 23 | print(f"--- Fetching {link_address}:") 24 | 25 | # Exercise 3 26 | # Display the text in the HTML page of each link 27 | link_page = urlopen(link_address) 28 | link_text = link_page.read().decode("utf-8") 29 | link_soup = BeautifulSoup(link_text, "html.parser") 30 | print(link_soup.get_text()) 31 | -------------------------------------------------------------------------------- /ch16-interacting-with-the-web/3-interact-with-html-forms.py: -------------------------------------------------------------------------------- 1 | # 16.3 - Interact with HTML Forms 2 | # Solutions to review exercises 3 | 4 | # Make sure BeautifulSoup is installed first with: 5 | # pip3 install MechanicalSoup 6 | 7 | import mechanicalsoup 8 | 9 | 10 | # Exercise 1 11 | browser = mechanicalsoup.Browser() 12 | login_url = "http://olympus.realpython.org/login" 13 | login_page = browser.get(login_url) 14 | login_html = login_page.soup 15 | 16 | # select the form and fill in the input fields 17 | form = login_html.form 18 | form.select("input")[0]["value"] = "zeus" 19 | form.select("input")[1]["value"] = "ThunderDude" 20 | 21 | # submit form 22 | profiles_page = browser.submit(form, login_page.url) 23 | 24 | 25 | # Exercise 2 26 | # show profile page title 27 | title = profiles_page.soup.title 28 | print(f"Title: {title.text}") 29 | 30 | 31 | # Exercise 3 32 | # navigate back to login page and show title 33 | login_page = browser.get(login_url) 34 | login_title = login_page.soup.title 35 | print(f"Title: {login_title.text}") 36 | 37 | 38 | # Exercise 4 39 | # Submit form with incorrect values 40 | form = login_html.form 41 | form.select("input")[0]["value"] = "wrong" 42 | form.select("input")[1]["value"] = "password" 43 | error_page = browser.submit(form, login_page.url) 44 | 45 | # check for string 46 | if error_page.soup.text.find("Wrong username or password!") != -1: 47 | print("Login Failed.") 48 | else: 49 | print("Login Successful.") 50 | -------------------------------------------------------------------------------- /ch16-interacting-with-the-web/4-interact-with-websites-in-realtime.py: -------------------------------------------------------------------------------- 1 | # 16.4 - Interact With Websites in Real-Time 2 | # Solutions to review exercise 3 | 4 | # Make sure BeautifulSoup is installed first with: 5 | # pip3 install MechanicalSoup 6 | 7 | from time import sleep 8 | import mechanicalsoup 9 | 10 | my_browser = mechanicalsoup.Browser() 11 | 12 | # Obtain 1 dice roll result every 10 seconds for the next minute 13 | for i in range(0, 6): 14 | page = my_browser.get("http://olympus.realpython.org/dice") 15 | html_text = page.soup 16 | 17 | # Return a list of all the tags where the id is 'yfs_184_yhoo' 18 | dice_result_tag = html_text.select("#result") 19 | 20 | # Take the BeautifulSoup string out of the first tag 21 | dice_result = dice_result_tag[0].text 22 | 23 | # Grab the timestamp 24 | time_tag = page.soup.select("#time") 25 | time = time_tag[0].text 26 | time = time[: time.find(" - ")] # Trim string to just the time 27 | 28 | print(f"Rolled a '{dice_result}' on {time}") 29 | 30 | if i < 5: # Wait 10 seconds if this wasn't the last request 31 | sleep(10) 32 | -------------------------------------------------------------------------------- /ch17-scientific-computing-and-graphing/1-use-numpy-for-matrix-manipulation.py: -------------------------------------------------------------------------------- 1 | # 17.2 - Use matplotlib for Plotting Graphs 2 | # Solution to review exercise #2 3 | 4 | # Setup 5 | import numpy as np 6 | 7 | 8 | # Exercise 1 9 | # Create a 3x3 array of the number 3 through 11 using reshape() 10 | first_matrix = np.arange(3, 12).reshape(3, 3) 11 | 12 | # Exercise 2 13 | # Display the min, max and mean of all entries in the matrix 14 | print(f"Min is {first_matrix.min()}") 15 | print(f"Max is {first_matrix.max()}") 16 | print(f"Mean is {first_matrix.mean()}") 17 | 18 | # Exercise 3 19 | # Square every entry and save in a new matrix 20 | second_matrix = first_matrix**2 21 | 22 | # Exercise 4 23 | # Put first_matrix on top of second_matrix 24 | third_matrix = np.vstack([first_matrix, second_matrix]) 25 | 26 | # Exercise 5 27 | # Calculate the dot product of third_matrix by first_matrix 28 | print(third_matrix @ first_matrix) 29 | 30 | # Exercise 6 31 | # Reshape third_matrix into a 3x3x2 matrix 32 | third_matrix = third_matrix.reshape(3, 3, 2) 33 | print(third_matrix) 34 | -------------------------------------------------------------------------------- /ch17-scientific-computing-and-graphing/2-use-matplotlib-for-plotting-graphs.py: -------------------------------------------------------------------------------- 1 | # 17.3 - Use matplotlib for Plotting Graphs 2 | # Solution to review exercise #2 3 | 4 | # Graph pirates versus global warming 5 | 6 | from matplotlib import pyplot as plt 7 | import csv 8 | import os 9 | 10 | # Change `path` to actual path on your system 11 | path = "C:/Real Python/python-basics-exercises/ch16-scientific-computing-and-graphing/practice_files" 12 | 13 | years = [] 14 | temperatures = [] 15 | pirates = [] 16 | 17 | with open(os.path.join(path, "pirates.csv"), "r") as my_file: 18 | my_file_reader = csv.reader(my_file) 19 | next(my_file_reader) # skip header row 20 | for year, temperature, pirate_count in my_file_reader: 21 | years.append(int(year)) 22 | temperatures.append(float(temperature)) 23 | pirates.append(int(pirate_count)) 24 | 25 | plt.plot(pirates, temperatures, "r-o") 26 | 27 | plt.title("Global temperature as a function of pirate population") 28 | plt.xlabel("Total pirates") 29 | plt.ylabel("Average global temperature (Celsius)") 30 | plt.axis([-300, 48000, 14, 16]) 31 | 32 | # Annotate the plotted points with years. 33 | for i in range(0, len(years)): 34 | plt.annotate(str(years[i]), xy=(pirates[i], temperatures[i])) 35 | 36 | # Save and display graph. 37 | plt.savefig(os.path.join(path, "Output/pirates.png")) 38 | plt.show() 39 | -------------------------------------------------------------------------------- /ch17-scientific-computing-and-graphing/practice_files/pirates.csv: -------------------------------------------------------------------------------- 1 | Year,Temperature,Pirates 2 | 1820,14.25,45000 3 | 1860,14.4,35000 4 | 1880,14.55,20000 5 | 1920,14.9,15000 6 | 1940,15.25,5000 7 | 1980,15.6,400 8 | 2000,15.9,17 9 | -------------------------------------------------------------------------------- /ch18-graphical-user-interfaces/1-add-gui-elements-with-easygui.py: -------------------------------------------------------------------------------- 1 | # 18.1 - Add GUI Elements with EasyGUI 2 | # Review Exercises 3 | 4 | import easygui as gui 5 | 6 | 7 | # Exercise #1 8 | gui.msgbox(msg="Warning!", title="Watch out!", ok_button="I'll be careful") 9 | 10 | 11 | # Exercise #2 12 | gui.enterbox(msg="What is your name?") 13 | -------------------------------------------------------------------------------- /ch18-graphical-user-interfaces/10-challenge-return-of-the-poet.py: -------------------------------------------------------------------------------- 1 | # 18.10 - Challenge: Return of the Poet 2 | # Solution to challenge 3 | 4 | # Please note that there are many ways to solve this challenge. The code 5 | # contained in this solution is just one way. If your solution is different, 6 | # but works, then you did a great job! 7 | 8 | import tkinter as tk 9 | from tkinter import filedialog 10 | import random 11 | 12 | 13 | # Create the application window 14 | window = tk.Tk() 15 | window.title("Make your own poem!") 16 | 17 | # Application Header Frame 18 | header_frame = tk.Frame() 19 | header_label = tk.Label(master=header_frame) 20 | header_label["text"] = "Enter your favorite words, separated by commas." 21 | header_label.pack() 22 | header_frame.pack(padx=5, pady=5) 23 | 24 | # Application Input Frame 25 | input_frame = tk.Frame() 26 | # Label widgets for text entry boxes 27 | label_noun = tk.Label(input_frame, text="Nouns:") 28 | label_verb = tk.Label(input_frame, text="Verbs:") 29 | label_adj = tk.Label(input_frame, text="Adjectives:") 30 | label_prep = tk.Label(input_frame, text="Prepositions:") 31 | label_adv = tk.Label(input_frame, text="Adverbs:") 32 | # Entry widgets for entering nouns, verbs, etc. 33 | entry_noun = tk.Entry(input_frame, width=80) 34 | entry_verb = tk.Entry(input_frame, width=80) 35 | entry_adj = tk.Entry(input_frame, width=80) 36 | entry_prep = tk.Entry(input_frame, width=80) 37 | entry_adv = tk.Entry(input_frame, width=80) 38 | # Add each label and entry widget to the input_frame using the .grid() 39 | # geometry manager. 40 | label_noun.grid(row=2, column=1, sticky=tk.E) 41 | entry_noun.grid(row=2, column=2) 42 | label_verb.grid(row=3, column=1, sticky=tk.E) 43 | entry_verb.grid(row=3, column=2) 44 | label_adj.grid(row=4, column=1, sticky=tk.E) 45 | entry_adj.grid(row=4, column=2) 46 | label_prep.grid(row=5, column=1, sticky=tk.E) 47 | entry_prep.grid(row=5, column=2) 48 | label_adv.grid(row=6, column=1, sticky=tk.E) 49 | entry_adv.grid(row=6, column=2) 50 | input_frame.pack(padx=5, pady=5) 51 | # Button widget for generating a poem 52 | generate_frame = tk.Frame(master=window) 53 | generate_frame.pack(pady=10) 54 | generate_button = tk.Button(generate_frame, text="Generate") 55 | generate_button.pack() 56 | 57 | # Application Result Frame 58 | # Displays the generated poem 59 | result_frame = tk.Frame(master=window) 60 | result_frame["relief"] = tk.GROOVE 61 | result_frame["borderwidth"] = 5 62 | result_label = tk.Label(master=result_frame) 63 | result_label["text"] = "Your poem:" 64 | result_label.pack(pady=10) 65 | result_poem = tk.Label(result_frame) 66 | result_poem["text"] = "Press the 'Generate' button to display your poem." 67 | result_poem.pack(padx=5) 68 | save_button = tk.Button(result_frame, text="Save to file") 69 | save_button.pack(pady=10) 70 | result_frame.pack(fill=tk.X, padx=5, pady=5) 71 | 72 | 73 | # Button command functions 74 | def are_unique(word_list): 75 | """Check that all items in a list are unique and return True or False""" 76 | unique_words = [] 77 | for word in word_list: 78 | if word not in unique_words: 79 | unique_words.append(word) 80 | return len(word_list) == len(unique_words) 81 | 82 | 83 | def generate_poem(): 84 | """Generate a poem and assign it to the `result_poem` Label widget""" 85 | 86 | # Split the user input into lists 87 | noun = entry_noun.get().split(",") 88 | verb = entry_verb.get().split(",") 89 | adjective = entry_adj.get().split(",") 90 | adverb = entry_adv.get().split(",") 91 | preposition = entry_prep.get().split(",") 92 | 93 | # Make sure that all lists consist of unique words 94 | if not ( 95 | are_unique(noun) 96 | and are_unique(verb) 97 | and are_unique(adjective) 98 | and are_unique(adverb) 99 | and are_unique(preposition) 100 | ): 101 | result_poem["text"] = "Please do not enter duplicate words." 102 | return 103 | 104 | # Make sure that we got enough words from the user to make a poem 105 | if ( 106 | len(noun) < 3 107 | or len(verb) < 3 108 | or len(adjective) < 3 109 | or len(preposition) < 2 110 | or len(adverb) < 1 111 | ): 112 | result_poem["text"] = ( 113 | "There was a problem with your input!\n" 114 | "Enter at least three nouns, three verbs, three adjectives, " 115 | "two prepositions and an adverb!" 116 | ) 117 | return 118 | 119 | # Otherwise, we can go ahead with generating a poem: 120 | 121 | # Get three nouns randomly 122 | noun1 = random.choice(noun) 123 | noun2 = random.choice(noun) 124 | noun3 = random.choice(noun) 125 | 126 | # Make sure that all the nouns are different 127 | while noun1 == noun2: 128 | noun2 = random.choice(noun) 129 | while noun1 == noun3 or noun2 == noun3: 130 | noun3 = random.choice(noun) 131 | 132 | # Get three different verbs 133 | verb1 = random.choice(verb) 134 | verb2 = random.choice(verb) 135 | verb3 = random.choice(verb) 136 | while verb1 == verb2: 137 | verb2 = random.choice(verb) 138 | while verb1 == verb3 or verb2 == verb3: 139 | verb3 = random.choice(verb) 140 | 141 | # Get three different adjectives 142 | adj1 = random.choice(adjective) 143 | adj2 = random.choice(adjective) 144 | adj3 = random.choice(adjective) 145 | while adj1 == adj2: 146 | adj2 = random.choice(adjective) 147 | while adj1 == adj3 or adj2 == adj3: 148 | adj3 = random.choice(adjective) 149 | 150 | # Get two different prepositions 151 | prep1 = random.choice(preposition) 152 | prep2 = random.choice(preposition) 153 | while prep1 == prep2: 154 | prep2 = random.choice(preposition) 155 | 156 | # Get one adverb 157 | adv1 = random.choice(adverb) 158 | 159 | if adj1[0] in "aeiou": # First letter is a vowel 160 | article = "An" 161 | else: 162 | article = "A" 163 | 164 | # Put it all together into a poem 165 | poem = f"{article} {adj1} {noun1}\n\n" 166 | poem = ( 167 | poem + f"{article} {adj1} {noun1} {verb1} {prep1} the {adj2} {noun2}\n" 168 | ) 169 | poem = poem + f"{adv1}, the {noun1} {verb2}\n" 170 | poem = poem + f"the {noun2} {verb3} {prep2} a {adj3} {noun3}" 171 | 172 | # Place the resulting poem into the label 173 | result_poem["text"] = poem 174 | 175 | 176 | def save_poem_to_file(): 177 | """Save the poem displayed in the output box into a text file""" 178 | type_list = [("Text files", "*.txt")] 179 | file_name = filedialog.asksaveasfilename( 180 | filetypes=type_list, defaultextension="*.txt" 181 | ) 182 | # Save file if user entered a file name 183 | if file_name != "": 184 | with open(file_name, "w") as output_file: 185 | output_file.writelines(result_poem["text"]) 186 | 187 | 188 | # Assign the commands to the buttons 189 | generate_button["command"] = generate_poem 190 | save_button["command"] = save_poem_to_file 191 | 192 | # Start the application 193 | window.mainloop() 194 | -------------------------------------------------------------------------------- /ch18-graphical-user-interfaces/2-example-app-pdf-page-rotator.py: -------------------------------------------------------------------------------- 1 | # 18.2 - Example App: PDF Page Rotator 2 | # Review Exercise #1 3 | 4 | import easygui as gui 5 | from PyPDF2 import PdfFileReader, PdfFileWriter 6 | 7 | 8 | # Ask suser to select a PDF file 9 | open_title = "Select a PDF to rotate..." 10 | file_type = "*.pdf" 11 | input_path = gui.fileopenbox(title=open_title, default=file_type) 12 | 13 | # If nothing was returned by gui.fileopenbox(), the user either 14 | # hit cancel or closed the window so we should exit the program. 15 | if input_path is None: 16 | exit() 17 | 18 | # Ask the user by how many degrees each page should be rotated. 19 | # If the user dosn't select anything, keep displaying the 20 | # buttonbox() element until they do. 21 | choices = ("90", "180", "270") 22 | message = "Rotate the PDF clockwise by how many degrees?" 23 | degrees = None 24 | while degrees is None: 25 | degrees = gui.buttonbox(message, "Choose rotation...", choices) 26 | 27 | # Convert the chosen number of degrees to an integer 28 | degrees = int(degrees) 29 | 30 | # Ask the user what they would like to call the new PDF and where 31 | # it should be saved. 32 | save_title = "Save the rotated PDF as..." 33 | output_path = gui.filesavebox(title=save_title, default=file_type) 34 | 35 | # If the user tries to overwrite the file they originally selected 36 | # for rotation, warn them and ask them to select a new file path. 37 | # Keep doing this until a valid file path is chosen. 38 | warn_title = "Warning!" 39 | warn_message = "Cannot overwrite original file!" 40 | while input_path == output_path: 41 | gui.msgbox(warn_message, warn_title) 42 | output_path = gui.filesavebox(title=save_title, default=file_type) 43 | 44 | # If nothing was returned by gui.fileopenbox(), the user either hit 45 | # cancel or closed the window so we should exit the program. 46 | if output_path is None: 47 | exit() 48 | 49 | # Open the input PDF, rotate all of the pages, and add the rotated 50 | # pages to the output PDF. 51 | input_file = PdfFileReader(open(input_path, "rb")) 52 | output_pdf = PdfFileWriter() 53 | for page in input_file.pages: 54 | page = page.rotateClockwise(degrees) 55 | output_pdf.addPage(page) 56 | 57 | # Write the output PDF to the file path selected by the user 58 | with open(output_path, "wb") as output_file: 59 | output_pdf.write(output_file) 60 | -------------------------------------------------------------------------------- /ch18-graphical-user-interfaces/3-challenge-use-gui-elements-to-help-a-user-modify-files.py: -------------------------------------------------------------------------------- 1 | # 18.3 - Challenge: Use GUI Elements to Help a User Modify Files 2 | # Solution to challenge 3 | 4 | # save part of a PDF based on a user-supplied page range using a GUI 5 | 6 | import easygui as gui 7 | from PyPDF2 import PdfFileReader, PdfFileWriter 8 | 9 | # 1. Ask the user to select a PDF file to open. 10 | input_file_path = gui.fileopenbox("", "Select a PDF to trim...", "*.pdf") 11 | 12 | # 2. If no PDF file is chosen, exit the program. 13 | if input_file_path is None: 14 | exit() 15 | 16 | # 3. Ask for a starting page number. 17 | page_start = gui.enterbox( 18 | "Enter the number of the first page to use:", "Where to begin?" 19 | ) 20 | 21 | # 4. If the user does not enter a starting page number, exit the program. 22 | if page_start is None: 23 | exit() 24 | 25 | # 5. Valid page numbers are positive integers. If the user enters an invalid page number: 26 | # - Warn the user that the entry is invalid 27 | # - Return to step 3. 28 | # 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. 29 | input_file = PdfFileReader(input_file_path) # Open the PDF 30 | total_pages = input_file.getNumPages() # Get the total number of pages 31 | while ( 32 | not page_start.isdigit() 33 | or page_start == "0" 34 | or int(page_start) > total_pages 35 | ): 36 | gui.msgbox("Please provide a valid page number.", "Whoops!") 37 | page_start = gui.enterbox( 38 | "Enter the number of the first page to use:", "Where to begin?" 39 | ) 40 | if page_start is None: # exit on "Cancel" 41 | exit() 42 | 43 | # 6. Ask for an ending page number 44 | page_end = gui.enterbox( 45 | "Enter the number of the last page to use:", "Where to end?" 46 | ) 47 | 48 | # 7. If the user does not enter and ending page number, exit the program. 49 | if page_end is None: # exit on "Cancel" 50 | exit() 51 | 52 | # 8. If the user enters an invalid page number: 53 | # - Warn the user that the entry is invalid 54 | # - Return to step 6. 55 | # This solution also check that the ending page number is not less than the starting 56 | # page number, and that it is not greater than the last page in the PDF 57 | while ( 58 | not page_end.isdigit() 59 | or page_end == "0" 60 | or int(page_end) > total_pages 61 | or int(page_end) < int(page_start) 62 | ): 63 | gui.msgbox("Please provide a valid page number.", "Whoops!") 64 | page_end = gui.enterbox( 65 | "Enter the number of the last page to use:", "Where to end?" 66 | ) 67 | if page_end is None: # exit on "Cancel" 68 | exit() 69 | 70 | # 9. Ask for the location to save the extracted pages 71 | output_file_path = gui.filesavebox("", "Save the trimmed PDF as...", "*.pdf") 72 | 73 | # 10. If the user does not select a save location, exit the program. 74 | if output_file_path is None: 75 | exit() 76 | 77 | # 11. If the chosen save location is the same as the input file path: 78 | # - Warn the user that they can not overwrite the input file. 79 | # - Return the step 9. 80 | while input_file_path == output_file_path: # cannot use same file as input 81 | gui.msgbox( 82 | "Cannot overwrite original file!", "Please choose another file..." 83 | ) 84 | output_file_path = gui.filesavebox( 85 | "", "Save the trimmed PDF as...", "*.pdf" 86 | ) 87 | if output_file_path is None: 88 | exit() 89 | 90 | # 12. Perform the page extraction 91 | output_PDF = PdfFileWriter() 92 | 93 | for page_num in range(int(page_start) - 1, int(page_end)): 94 | page = input_file.getPage(page_num) 95 | output_PDF.addPage(page) 96 | 97 | with open(output_file_path, "wb") as output_file: 98 | output_PDF.write(output_file) 99 | -------------------------------------------------------------------------------- /ch18-graphical-user-interfaces/4-introduction-to-tkinter.py: -------------------------------------------------------------------------------- 1 | # 18.4 - Introduction to Tkinter 2 | # Review exercises 3 | 4 | import tkinter as tk 5 | 6 | # Exercise 1 7 | window = tk.Tk() 8 | label = tk.Label(text="GUIs are great!") 9 | label.pack() 10 | window.mainloop() 11 | 12 | 13 | # Exercise 2 14 | window = tk.Tk() 15 | label = tk.Label(text="Python rocks!") 16 | label.pack() 17 | window.mainloop() 18 | 19 | 20 | # Exercise 3 21 | window = tk.Tk() 22 | label = tk.Label(text="Engage!") 23 | label.pack() 24 | window.mainloop() 25 | -------------------------------------------------------------------------------- /ch18-graphical-user-interfaces/5-working-with-widgets.py: -------------------------------------------------------------------------------- 1 | # 18.4 - Introduction to Tkinter 2 | # Review exercises 3 | 4 | import tkinter as tk 5 | 6 | 7 | # Exercise 1 asks you to recreate all the windows in the chapter. 8 | # The source code for each window can be found in the chapter text. 9 | 10 | 11 | # Exercise 2 12 | window = tk.Tk() 13 | button = tk.Button( 14 | width=50, height=25, bg="white", fg="blue", text="Click here" 15 | ) 16 | button.pack() 17 | window.mainloop() 18 | 19 | 20 | # Exercise 3 21 | window = tk.Tk() 22 | entry = tk.Entry(width=40) 23 | entry.insert(0, "What is your name?") 24 | entry.pack() 25 | window.mainloop() 26 | -------------------------------------------------------------------------------- /ch18-graphical-user-interfaces/6-control-layout-with-geometry-managers.py: -------------------------------------------------------------------------------- 1 | # 18.6 - Control Layout With Geometry Managers 2 | # Review Exercise #2 3 | 4 | # NOTE: The first exercise in this section is instructional and does 5 | # not require a solution to be shown here. For this reason, only the 6 | # solution to the second exercise is presented. 7 | 8 | import tkinter as tk 9 | 10 | 11 | # Create a new window with the title "Address Entry Form" 12 | window = tk.Tk() 13 | window.title("Address Entry Form") 14 | 15 | # Create a new frame `frm_form` to contain the Label 16 | # and Entry widgets for entering address information. 17 | frm_form = tk.Frame(relief=tk.SUNKEN, borderwidth=3) 18 | # Pack the frame into the window 19 | frm_form.pack() 20 | 21 | # Create the Label and Entry widgets for "First Name" 22 | lbl_first_name = tk.Label(master=frm_form, text="First Name:") 23 | ent_first_name = tk.Entry(master=frm_form, width=50) 24 | # Use the grid geometry manager to place the Label and 25 | # Entry widgets in the first and second columns of the 26 | # first row of the grid 27 | lbl_first_name.grid(row=0, column=0, sticky="e") 28 | ent_first_name.grid(row=0, column=1) 29 | 30 | # Create the Label and Entry widgets for "Last Name" 31 | lbl_last_name = tk.Label(master=frm_form, text="Last Name:") 32 | ent_last_name = tk.Entry(master=frm_form, width=50) 33 | # Place the widgets in the second row of the grid 34 | lbl_last_name.grid(row=1, column=0, sticky="e") 35 | ent_last_name.grid(row=1, column=1) 36 | 37 | # Create the Label and Entry widgets for "Address Line 1" 38 | lbl_address1 = tk.Label(master=frm_form, text="Address Line 1:") 39 | ent_address1 = tk.Entry(master=frm_form, width=50) 40 | # Place the widgets in the third row of the grid 41 | lbl_address1.grid(row=2, column=0, sticky="e") 42 | ent_address1.grid(row=2, column=1) 43 | 44 | # Create the Label and Entry widgets for "Address Line 2" 45 | lbl_address2 = tk.Label(master=frm_form, text="Address Line 2:") 46 | ent_address2 = tk.Entry(master=frm_form, width=5) 47 | # Place the widgets in the fourth row of the grid 48 | lbl_address2.grid(row=3, column=0, sticky=tk.E) 49 | ent_address2.grid(row=3, column=1) 50 | 51 | # Create the Label and Entry widgets for "City" 52 | lbl_city = tk.Label(master=frm_form, text="City:") 53 | ent_city = tk.Entry(master=frm_form, width=50) 54 | # Place the widgets in the fifth row of the grid 55 | lbl_city.grid(row=4, column=0, sticky=tk.E) 56 | ent_city.grid(row=4, column=1) 57 | 58 | # Create the Label and Entry widgets for "State/Province" 59 | lbl_state = tk.Label(master=frm_form, text="State/Province:") 60 | ent_state = tk.Entry(master=frm_form, width=50) 61 | # Place the widgets in the sixth row of the grid 62 | lbl_state.grid(row=5, column=0, sticky=tk.E) 63 | ent_state.grid(row=5, column=1) 64 | 65 | # Create the Label and Entry widgets for "Postal Code" 66 | lbl_postal_code = tk.Label(master=frm_form, text="Postal Code:") 67 | ent_postal_code = tk.Entry(master=frm_form, width=50) 68 | # Place the widgets in the seventh row of the grid 69 | lbl_postal_code.grid(row=6, column=0, sticky=tk.E) 70 | ent_postal_code.grid(row=6, column=1) 71 | 72 | # Create the Label and Entry widgets for "Country" 73 | lbl_country = tk.Label(master=frm_form, text="Country:") 74 | ent_country = tk.Entry(master=frm_form, width=50) 75 | # Place the widgets in the eight row of the grid 76 | lbl_country.grid(row=7, column=0, sticky=tk.E) 77 | ent_country.grid(row=7, column=1) 78 | 79 | # Create a new frame `frm_buttons` to contain the 80 | # Submit and Clear buttons. This frame fills the 81 | # whole window in the horizontal direction and has 82 | # 5 pixels of horizontal and vertical padding. 83 | frm_buttons = tk.Frame() 84 | frm_buttons.pack(fill=tk.X, ipadx=5, ipady=5) 85 | 86 | # Create the "Submit" button and pack it to the 87 | # right side of `frm_buttons` 88 | btn_submit = tk.Button(master=frm_buttons, text="Submit") 89 | btn_submit.pack(side=tk.RIGHT, padx=10, ipadx=10) 90 | 91 | # Create the "Clear" button and pack it to the 92 | # right side of `frm_buttons` 93 | btn_clear = tk.Button(master=frm_buttons, text="Clear") 94 | btn_clear.pack(side=tk.RIGHT, ipadx=10) 95 | 96 | # Start the application 97 | window.mainloop() 98 | -------------------------------------------------------------------------------- /ch18-graphical-user-interfaces/7-make-your-applications-interactive.py: -------------------------------------------------------------------------------- 1 | # 18.7 - Make Your Applications Interactive 2 | # Review Exercises 3 | 4 | 5 | # Exercise 1 6 | import random 7 | import tkinter as tk 8 | 9 | 10 | def change_bg_color(): 11 | color = random.choice( 12 | ["red", "orange", "yellow", "blue", "green", "indigo", "violet"] 13 | ) 14 | button["bg"] = color 15 | 16 | 17 | window = tk.Tk() 18 | button = tk.Button(text="Click me", command=change_bg_color) 19 | button.pack() 20 | window.mainloop() 21 | 22 | 23 | # Exercise 2 24 | import random 25 | import tkinter as tk 26 | 27 | 28 | def roll(): 29 | lbl_result["text"] = str(random.randint(1, 6)) 30 | 31 | 32 | window = tk.Tk() 33 | window.columnconfigure(0, minsize=150) 34 | window.rowconfigure([0, 1], minsize=50) 35 | 36 | btn_roll = tk.Button(text="Roll", command=roll) 37 | lbl_result = tk.Label() 38 | 39 | btn_roll.grid(row=0, column=0, sticky="nsew") 40 | lbl_result.grid(row=1, column=0) 41 | 42 | window.mainloop() 43 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | # black formatter configuration 2 | # This file is for internal use only and is not a solution to any exercise 3 | 4 | [tool.black] 5 | line-length = 79 6 | py36 = true 7 | include = '\.pyi?$' 8 | exclude = ''' 9 | /( 10 | ch03-first-python-program 11 | | \.venv 12 | | venv 13 | )/ 14 | ''' --------------------------------------------------------------------------------