Python From Scratch - Solved Exercises
Python From Scratch - Solved Exercises
SOLVED EXERCISES
1�� edition - updated on Aug 2, 2025
h�ps://[Link]/
[Link]
Introduction
This document was created to provide all the solved exercises from the book in a
single file. The book’s website can be accessed at [Link] or
via the QR code below:
Chapter 02
Exercise 02-01
Convert the following mathematical expressions so that they can be calculated using
the Python interpreter.
10 + 20 × 30
42 ÷ 30
(94 + 2) × 6 - 1
Exercise 02-02
Type the following expression in the interpreter:
10 % 3 * 10 ** 2 + 1 - 10 * 4 / 2
Try to solve the same calculation using only pencil and paper. Notice how important
the priority of operations is.
Exercise 02-03
Make a program that displays your name on the screen.
Exercise 02-04
Write a program that displays the result of 2a × 3b, where a is 3 and b is 5.
a = 3
b = 5
print(2 * a * 3 * b)
Exercise 02-05
Write a program that calculates the sum of three variables and prints the result on the
screen
a = 2
b = 3
c = 4
print(a + b + c)
Exercise 02-06
Modify Program 2.2 so that it calculates a 15% increase for a salary of $750.
salary = 750
raise_percentage = 15
print(salary + (salary * raise_percentage / 100))
Exercise 02-07
Using the properties of division and multiplication, try to understand how these
results are the same:
Chapter 03
Exercise 03-01
Complete the following table, marking integer or floating-point depending on the
number presented.
# integer
# floating point
# floating point
# integer
# integer
# floating point
Exercise 03-02
Complete the following table, answering True or False. Consider a = 4, b = 10, c = 5.0,
d = 1, and f = 5.
# False (a==c)
# True (a<b)
# False (d>b)
# False (c!=f)
# False (a==b)
# False (c<d)
# True (b>a)
# True (c>=f)
# True (f>=c)
# True (c<=c)
# True (c<=f)
Exercise 03-03
Complete the following table using a = True, b = False, and c = True.
# True (a and a)
# False (b and b)
# False (not c)
# True (not b)
# False (not a)
# False (a and b)
# False (b and c)
# True (a or c)
# True (b or c)
# True (a or c)
# True (b or c)
# True (c or a)
# True (c or b)
# True (c or c)
# False (b or b)
Exercise 03-04
Write an expression to determine whether a person should pay tax. Consider that
people whose salary is greater than $1,200.00 pay taxes.
Exercise 03-05
Calculate the result of the expression A > B and C or D, using the values in the
following table.
A B C D Result
1 2 True False
10 3 False False
5 1 True True
# False
# False
# True
Exercise 03-06
Write an expression that will be used to decide whether a student is approved. To be
approved, all student averages (arithmetic mean) must be greater than or equal to 7
(consider 10 the maximum grade). Consider that the student only takes three subjects
and that the grade for each one is stored in the following variables: grade1, grade2,
and grade3.
Exercise 03-07
Make a program that asks for two integer numbers. Print the sum of these two
numbers on the screen.
Exercise 03-08
Write a program that reads a value in meters and displays it converted to millimeters.
Exercise 03-09
Write a program that reads the user’s number of days, hours, minutes, and seconds.
Calculate the total in seconds.
days = int(input("Days:"))
hours = int(input("Hours:"))
minutes = int(input("Minutes:"))
seconds = int(input("Seconds:"))
# One minute has 60 seconds
# One hour has 3600 (60 * 60) seconds
# One day has 24 hours, so 24 * 3600 seconds
total_in_seconds = days * 24 * 3600 + hours * 3600 + minutes * 60 +
seconds
print("Converted to seconds equals %10d seconds." % total_in_seconds)
Exercise 03-10
Make a program that calculates a pay raise. It must request the amount of the salary
and the percentage of the raise. Display the amount of the raise and the new salary.
Exercise 03-11
Make a program that asks for the price of a commodity and the discount percentage.
Display the discount amount and the price to pay.
Exercise 03-12
Write a program that calculates the time of a car trip. Ask the distance to cover and
the average speed expected for the trip.
Exercise 03-13
Write a program that converts a temperature entered in °C to °F. The formula for this
conversion is:
Exercise 03-14
Write a program that asks the number of kilometers traveled by a rental car and the
number of days the vehicle was rented. Calculate the price to pay, knowing that the
car costs $60 a day and $0.15 per km driven.
Exercise 03-15
Write a program to calculate a smoker’s lifespan reduction. Ask how many cigarettes
a day they smoke and how many years they have smoked. Consider that a smoker
loses 10 minutes of life with each cigarette and calculate how many days of life the
smoker will lose. Display the total in days.
Chapter 04
Exercise 04-01
Analyze Program 4.1. What happens if the first and second values are the same?
Explain.
Exercise 04-02
Write a program that asks the speed of a user’s car. If it exceeds 80 km/h, display a
message stating that the user has been fined. In this case, show the amount of the
fine, charging $5 per km above 80 km/h.
Exercise 04-03
Write a program that reads three numbers and prints the largest and the smallest.
Exercise 04-04
Write a program that asks for the employee’s salary and calculates the amount of the
rise. For salaries above $1,250, calculate a raise of 10%. For those equal or lower,
15%.
Exercise 04-05
Run Program 4.5 and try some values. Check that the results are the same as in
Program 4.2.
Exercise 04-06
Write a program that asks the distance a passenger wishes to cover in kilometers.
Calculate the ticket price, charging $0.50 per km for trips up to 200 km and $0.45 for
longer trips.
Exercise 04-07
Analyze Program 4.3. Does using else in that program make sense? Explain your
answer.
Exercise 04-08
Rewrite Program 4.4 and calculate the Bye operator account using else.
Exercise 04-09
Trace Program 4.8. Compare your result to that shown in Table 4.2.
Exercise 04-10
Write a program that reads two numbers and asks what operation you want to
perform. You must be able to calculate sum (+), subtraction (-), multiplication (*),
and division (/). Display the result of the requested operation.
a = float(input("First number:"))
b = float(input("Second number:"))
operation = input("Enter the operation to perform (+, -, * or /):")
if operation == "+":
result = a + b
elif operation == "-":
result = a - b
elif operation == "*":
result = a * b
elif operation == "/":
result = a / b
else:
print("Invalid operation!")
result = 0
print("Result: ", result)
Exercise 04-11
Write a program to approve a bank loan for the purchase of a home. The program
must ask the price of the house to buy, the salary, and the number of years to pay.
The amount of the monthly installment cannot exceed 30% of the salary. Calculate
the installment as the amount of the house to be purchased divided by the number of
months to pay.
Exercise 04-12
Write a program that calculates the price to pay for electricity. Ask the amount of
kWh consumed and the type of installation: R for residential, I for industrial, and C
for commercial. Calculate the price to pay according to the following table.
Exercise 04-13
In the following program, invert the if and else lines, negating the condition. Add the
necessary lines to make it work in Python.
if a > b:
print("a is greater than b")
else:
print("b is greater than a")
a = int(input("a: "))
b = int(input("b: "))
if a <= b:
print("b is greater than a")
else:
print("a is greater than b")
Exercise 04-14
Rewrite the following program with if-elif-else. Add the necessary lines to make it
work in Python.
if a < 10:
print("a is less than 10")
if a >= 10 and a < 20:
print("a is greater than 10 and less than 20")
if a >= 20:
print("a is greater than 20")
a = int(input("a: "))
if a < 10:
print("a is less than 10")
elif a < 20:
print("a is greater than or equal to 10 and less than 20")
else:
print("a is greater than or equal to 20")
Exercise 04-15
Rewrite the following program with if-elif-else.
Exercise 04-16
Correct the following program:
Chapter 05
Exercise 05-01
Modify the program to display numbers from 1 to 100.
x = 1
while x <= 100:
print(x)
x = x + 1
Exercise 05-02
Modify the program to display numbers from 50 to 100.
x = 50
while x <= 100:
print(x)
x = x + 1
Exercise 05-03
Make a program to write the countdown of a rocket launch. The program must print
10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, and Fire! on the screen.
x = 10
while x >= 0:
print(x)
x = x - 1
print("Fire!")
Exercise 05-04
Modify the previous program to print from 1 to the number entered by the user, this
time producing only odd numbers.
Exercise 05-05
Rewrite the previous program to write the first 10 multiples of 3.
end = 30
x = 3
while x <= end:
print(x)
x = x + 3
Exercise 05-06
Change the previous program to display the results in the same format as a
multiplication table: 2 x 1 = 2, 2 x 2 = 4,…
Exercise 05-07
Modify the previous program so that the user must input the beginning and end of
the multiplication table, instead of starting and ending with 1 and 10.
Exercise 05-08
Write a program that reads two numbers. Print the result of multiplying the first by
the second. Use only the addition and subtraction operators to calculate the result.
Remember that we can understand the multiplication of two numbers as successive
sums of one of them (e.g., 4 × 5 = 5 + 5 + 5 + 5 = 4 + 4 + 4 + 4 + 4).
Exercise 05-09
Write a program that reads two numbers. Print the result of dividing the first by the
second. Use only the addition and subtraction operators to calculate the result.
Remember that the quotient of dividing two numbers is the number of times we can
subtract the divisor from the dividend. For example, 20 ÷ 4 = 5 since we can subtract
4 five times from 20.
Exercise 05-10
Modify the previous program so it accepts answers with uppercase and lowercase
letters for all questions.
points = 0
question = 1
while question <= 3:
answer = input(f"Answer to question {question}: ")
if question == 1 and (answer == "b" or answer == "B"):
points = points + 1
if question == 2 and (answer == "a" or answer == "A"):
points = points + 1
if question == 3 and (answer == "d" or answer == "D"):
points = points + 1
question += 1
print(f"The student scored {points} point(s)")
Exercise 05-11
Write a program that asks for a savings account’s initial deposit and interest rate.
Display month-by-month values for the first 24 months. Write the total interest
earned for the period.
Exercise 05-12
Amend the previous program to ask for the monthly amount deposited. This amount
will be deposited at the beginning of each month. Include the deposit amount when
calculating interest for the following month.
Exercise 05-13
Write a program that asks for the initial amount of debt and the monthly interest.
Also, ask for the monthly amount that will be paid. Print the number of months it will
take for the debt to be repaid, the full amount that will be paid, and the total interest
that will be paid.
Exercise 05-14
Write a program that reads integers input by the user. The program must read the
numbers until the user enters 0 (zero). At the end of the execution, display how many
numbers were entered, their sum, and their arithmetic mean.
nsum = 0
quantity = 0
while True:
n = int(input("Enter an integer: "))
if n == 0:
break
nsum = nsum + n
quantity = quantity + 1
print("Number of integers entered:", quantity)
print("Sum: ", nsum)
print(f"Average: {nsum/quantity:10.2f}")
Exercise 05-15
Write a program to control a small cash register. You must ask the user to enter the
product code and the purchase quantity. Use the following code table to obtain the
price of each product:
Code Price
1 0.50
2 1.00
3 4.00
5 7.00
9 8.00
Your program should display the total of the purchases after the user enters 0. Any
other code should generate the “Invalid Code” error message.
to_pay = 0
while True:
code = int(input("Product code (0 to exit): "))
price = 0
if code == 0:
break
elif code == 1:
price = 0.50
elif code == 2:
price = 1.00
elif code == 3:
price = 4.00
elif code == 5:
price = 7.00
elif code == 9:
price = 8.00
else:
print("Invalid code!")
if price != 0:
quantity = int(input("Quantity: "))
to_pay = to_pay + (price * quantity)
print(f"Total to pay ${to_pay:8.2f}")
Exercise 05-16
Run Program 5.1 for the following values: 501, 745, 384, 2, 7, and 1.
# The program should work normally with the values requested by the
exercise.
Exercise 05-17
What happens if we enter 0 (zero) in the amount to be paid?
# The program stops right after printing the quantity of $50.00 bills
Exercise 05-18
Modify the program so it also works with $100 bills.
Exercise 05-19
Modify the program to accept decimal values and count coins of $0.01, $0.05, $0.10,
and $0.50.
current = 0.02
elif current == 0.02:
current = 0.01
bills = 0
Exercise 05-20
What happens if we type 0.001 in the previous program? If it doesn’t work, change
the program to fix the issue.
Exercise 05-21
Rewrite Program 5.1 to continue running until the value entered is 0. Use nested
loops.
while True:
value = int(input("Enter the amount to pay:"))
if value == 0:
break
bills = 0
current = 50
to_pay = value
while True:
if current <= to_pay:
to_pay -= current
bills += 1
else:
print(f"{bills} bill(s) of ${current}")
if to_pay == 0:
break
if current == 50:
current = 20
elif current == 20:
current = 10
elif current == 10:
current = 5
elif current == 5:
current = 1
bills = 0
Exercise 05-22
Write a program that displays a list of options (menu): addition, subtraction,
division, multiplication, and exit. Print the multiplication table for the chosen
operation. Repeat until the exit option is chosen.
while True:
print(
"""
Menu
\----
1 - Addition
2 - Subtraction
3 - Division
4 - Multiplication
5 - Exit
"""
)
option = int(input("Choose an option:"))
if option == 5:
break
elif option >= 1 and option < 5:
n = int(input("Multiplication table of:"))
x = 1
while x <= 10:
if option == 1:
print(f"{n} + {x} = {n + x}")
elif option == 2:
print(f"{n} - {x} = {n - x}")
elif option == 3:
print(f"{n} / {x} = {n / x:5.4f}")
elif option == 4:
print(f"{n} x {x} = {n * x}")
x = x + 1
else:
print("Invalid option!")
Exercise 05-23
Write a program that reads a number and checks whether it is a prime number. To
check, calculate the rest of the division of the number by 2 and then by all the odd
numbers up to the number read. If the remainder of one of these divisions equals
zero, the number is not prime. Note that 0 and 1 are not prime and that 2 is the only
even prime number.
n = int(input("Enter a number:"))
if n < 0:
print("Invalid number. Please enter only positive values")
if n == 0 or n == 1:
print(f"{n} is a special case.")
else:
if n == 2:
print("2 is prime")
elif n % 2 == 0:
print(f"{n} is not prime, as 2 is the only even prime number.")
else:
x = 3
while x < n:
if n % x == 0:
break
x = x + 2
if x == n:
print(f"{n} is prime")
else:
print(f"{n} is not prime, as it is divisible by {x}")
Exercise 05-24
Modify the previous program to read a number n. Print the first n prime numbers.
Exercise 05-25
Write a program that calculates the square root of a number, using Newton’s method
to get an approximate result. Since n is the number to obtain the square root,
consider the base b = 2. Calculate p using the formula p = (b + (n/b)) / 2. Now,
calculate the square of p. At each step, do b = p and recalculate p using the formula
presented. Stop when the absolute difference between n and the square of p is less
than 0.0001.
Exercise 05-26
Write a program that calculates the rest of the integer division between two numbers.
Use only the addition and subtraction operations to calculate the result.
Exercise 05-27-a
Write a program that checks whether a number is palindromic. A number is
palindromic if it remains the same if its digits are reversed. Examples include 454
and 10501.
Exercise 05-27-b
Write a program that checks whether a number is palindromic. A number is
palindromic if it remains the same if its digits are reversed. Examples include 454
and 10501.
# Exercise 5.27
# Alternative solution, using only integers
n = int(input("Enter the number to verify:"))
# Since n is an integer, we'll calculate its
# number of digits by finding the first
# power of 10 greater than n.
# Example: 341 - first power of 10 greater: 1000 = 10 ^ 4
# We'll use 4 and not 3 to allow handling numbers
# with a single digit. The adjustment is made in the formulas below
maximum_exponent_of_10 = 0
while 10**maximum_exponent_of_10 < n:
maximum_exponent_of_10 += 1
# Positions relative to the right and left of n
from_right = maximum_exponent_of_10
from_left = 0
# Here we copy n to number_from_left and number_from_right
number_from_left = number_from_right = n
# and make digit_from_right = digit_from_left (for special cases)
digit_from_right = digit_from_left = 0
while from_right > from_left:
digit_from_right = int(
number_from_right / (10 ** (from_right - 1))
) # Rightmost digit
digit_from_left = number_from_left % 10 # Leftmost digit
if digit_from_right != digit_from_left: # If they are different, we
exit
break
from_left = from_left + 1 # Move to the next digit on the left
from_right = from_right - 1 # Move to the next digit on the right
number_from_right = number_from_right - (
digit_from_right * (10**from_right)
) # Adjust ni to remove the previous digit
number_from_left = int(number_from_left / 10) # Adjust nf to remove
the last digit
if digit_from_right == digit_from_left:
print(f"{n} is a palindrome")
else:
print(f"{n} is not a palindrome")
Chapter 06
Exercise 06-01
Modify Program 6.2 to read seven grades instead of five.
Exercise 06-02
Make a program that reads two lists and generates a third one with the elements of
the first two lists.
first = []
second = []
while True:
element = int(input("Enter a value for the first list (0 to finish): "))
if element == 0:
break
[Link](element)
while True:
element = int(input("Enter a value for the second list (0 to finish):
"))
if element == 0:
break
[Link](element)
# Copies elements from the first list to the third list
third = first[:]
# Extends the third list with the elements of the second list
[Link](second)
x = 0
while x < len(third):
print(f"{x}: {third[x]}")
x += 1
Exercise 06-03
Make a program that goes through two lists and generates a third one without
repeated elements.
first = []
second = []
while True:
element = int(input("Enter a value for the first list (0 to finish):"))
if element == 0:
break
[Link](element)
while True:
element = int(input("Enter a value for the second list (0 to
finish):"))
if element == 0:
break
[Link](element)
third = []
# Here we will create another list with elements from the first
# and second lists. There are several ways to solve this exercise.
# In this solution, we will search for values to insert into the third
# list. If they don't exist, we'll add them to the third. Otherwise,
# we won't copy them, thus avoiding duplicates.
two_lists = first[:]
two_lists.extend(second)
x = 0
while x < len(two_lists):
y = 0
while y < len(third):
if two_lists[x] == third[y]:
break
y += 1
if y == len(third):
[Link](two_lists[x])
x += 1
x = 0
while x < len(third):
print(f"{x}: {third[x]}")
x += 1
Exercise 06-04
Modify the first example (Program 6.7) to perform the same task without using the
variable found. Tip: Look at the while exit condition.
Exercise 06-05
Modify the example to search for two values. Instead of just p, read another value v
that will also be searched. In the printout, indicate which of the two values was found
first.
Exercise 06-06
Modify the Exercise 6.5 program to search for p and v throughout the list and inform
the user of the position in which p and the position in which v were found.
Exercise 06-07
Modify Program 6.6 using for. Explain why every while cannot be turned into a for.
L = []
while True:
n = int(input("Enter a number (0 to exit):"))
if n == 0:
break
[Link](n)
for e in L:
print(e)
# The first while loop couldn't be converted to a for loop because
# the number of repetitions is unknown at the start.
Exercise 06-08
Change Program 6.9 to print the smallest element in the list.
L = [4, 2, 1, 7]
minimum = L[0]
for e in L:
if e < minimum:
minimum = e
print(minimum)
Exercise 06-09
The temperature for Mons, Belgium, was stored in the list T = [-10, -8, 0, 1, 2, 5, -2, -
4]. Make a program that prints the lowest, highest, and average temperatures.
Exercise 06-10
Modify Program 6.11 to show how many tickets were sold in each room. Use a list that
is the same size as the number of rooms and count the number of tickets sold in each
room using its elements as counters. Print the total sales at the end of the program on
the screen.
available_seats = [10, 2, 1, 3, 0]
sold = [0] * len(available_seats)
while True:
room = int(input("Room (0 to exit): "))
if room == 0:
print("End")
break
if room > len(available_seats) or room < 1:
print("Invalid room")
elif available_seats[room - 1] == 0:
print("Sorry, room is full!")
else:
seats = int(
input(
f"How many seats do you want ({available_seats[room - 1]}
available):"
)
)
if seats > available_seats[room - 1]:
print("That number of seats is not available.")
elif seats < 0:
print("Invalid number")
else:
available_seats[room - 1] -= seats
sold[room - 1] += seats
print(f"{seats} seats sold")
print("\nRoom utilization")
for room, available in enumerate(available_seats):
print(f"Room {room + 1} – {available} seat(s) available")
print("\nSales by room")
total_sold = 0
for room, sales in enumerate(sold):
print(f"Room {room + 1} – {sales} ticket(s) sold")
total_sold += sales
print(f"Total tickets sold: {total_sold}")
Exercise 06-11
Modify Program 6.11 to ask for the number of rooms and the number of available
seats in each.
Exercise 06-12
What happens when the list is already ordered? Trace Program 6.18 but with the list
L = [1, 2, 3, 4, 5].
Exercise 06-13
What happens when two values are the same? Trace Program 6.18 but with the list L
= [3, 3, 1, 5, 4].
Exercise 06-14
Modify Program 6.18 to sort the list in descending order. L = [1, 2, 3, 4, 5] must be
ordered as L = [5, 4, 3, 2, 1].
L = [1, 2, 3, 4, 5]
end = 5
while end > 1:
swapped = False
x = 0
while x < (end - 1):
if L[x] < L[x + 1]: # Only the verification condition was changed
swapped = True
temp = L[x]
L[x] = L[x + 1]
L[x + 1] = temp
x += 1
if not swapped:
break
end -= 1
for e in L:
print(e)
Exercise 06-15
What happens when we don’t check that the list is empty before calling the pop
method?
Exercise 06-16
Change Program 6.19 so that you can work with several commands entered at once.
Currently, only one command can be entered at a time. Please change it to consider
the operation as a string. For example, AAASSSX would mean three new customer
arrivals, three services, and finally, the exit from the program.
last = 10
queue = list(range(1, last + 1))
while True:
print(f"\nThere are {len(queue)} customers in the queue")
print("Current queue:", queue)
print("Enter F to add a customer to the end of the queue,")
print("or A to serve a customer. X to exit.")
operation = input("Operation (F, A or X):")
x = 0
exit = False
while x < len(operation):
if operation[x] == "A":
if len(queue) > 0:
served = [Link](0)
print(f"Customer {served} served")
else:
print("Empty queue! No one to serve.")
elif operation[x] == "F":
last += 1 # Increments the new customer's ticket
[Link](last)
elif operation[x] == "X":
exit = True
break
else:
print(
f"Invalid operation: {operation[x]} at position {x}! Enter
only F, A or X!"
)
x += 1
if exit:
break
Exercise 06-17
Modify the program to work with two lines. To make your job easier, consider
command S for serving line 1 and T for servicing line 2. The same for the arrival of
customers: A for line 1 and B for line 2.
last = 0
queue1 = []
queue2 = []
while True:
print(
f"\nThere are {len(queue1)} customers in queue 1 and {len(queue2)}
in queue 2."
)
print("Current queue 1:", queue1)
print("Current queue 2:", queue2)
print("Enter A to add a customer to the end of queue 1 (or B for queue
2),")
print("or S to serve queue 1 (or T for queue 2)")
print("X to exit.")
operation = input("Operation (A, B, S, T or X):")
x = 0
exit = False
while x < len(operation):
# Here we'll use queue as a reference to queue 1
# or queue 2, depending on the operation.
if operation[x] == "A" or operation[x] == "S":
queue = queue1
else:
queue = queue2
if operation[x] == "S" or operation[x] == "T":
if len(queue) > 0:
served = [Link](0)
print(f"Customer {served} served")
else:
print("Empty queue! No one to serve.")
elif operation[x] == "A" or operation[x] == "B":
last += 1 # Increments the new customer's ticket
[Link](last)
elif operation[x] == "X":
exit = True
break
else:
print(
f"Invalid operation: {operation[x]} at position {x}! Enter
only A, B, S, T or X!"
)
x += 1
if exit:
break
Exercise 06-18
Make a program that reads an expression with parentheses. Using stacks, verify that
the parentheses have been opened and closed in the correct order. Example:
(()) OK
()())( OK
()) Error
You can add elements to the stack whenever you find an open parenthesis and
unstack it whenever you find a closed one. When unstacking, make sure that the top
of the stack is an open parenthesis. If the expression is correct, your stack will be
empty at the end.
Exercise 06-19
Change Program 6.22 to request the product and quantity sold from the user. Check
if the product name entered exists in the dictionary and only then carry out the stock
operation.
inventory = {
"tomato": [1000, 2.30],
"lettuce": [500, 0.45],
"potato": [2001, 1.20],
"beans": [100, 1.50],
}
total = 0
print("Sales:\n")
while True:
product = input("Product name (end to exit):")
if product == "end":
break
if product in inventory:
quantity = int(input("Quantity:"))
if quantity <= inventory[product][0]:
price = inventory[product][1]
cost = price * quantity
print(f"{product:12s}: {quantity:3d} x {price:6.2f} =
{cost:6.2f}")
inventory[product][0] -= quantity
total += cost
else:
print("Requested quantity not available")
else:
print("Invalid product name")
print(f" Total cost: {total:21.2f}\n")
print("Inventory:\n")
for key, data in [Link]():
print("Description: ", key)
print("Quantity: ", data[0])
print(f"Price: {data[1]:6.2f}\n")
Exercise 06-20-a
Write a program that generates a dictionary, where each key is a character and its
value is the number of that character found in a sentence input by the user.
Example: “The mouse” → \{“T”: 1, “h”: 1, “e”: 2, ’ “: 1, ’m”: 1, “o”: 1, “u”: 1, “s”: 1}
Exercise 06-20-b
Write a program that generates a dictionary, where each key is a character and its
value is the number of that character found in a sentence input by the user.
Example: “The mouse” → \{“T”: 1, “h”: 1, “e”: 2, ’ “: 1, ’m”: 1, “o”: 1, “u”: 1, “s”: 1}
Exercise 06-21
Write a program that compares two lists. Using operations with sets, print:
L1 = [1, 2, 6, 8]
L2 = [3, 6, 8, 9]
print(f"List 1: {L1}")
print(f"List 2: {L2}")
set_1 = set(L1)
set_2 = set(L2)
# Repeated:
print("First list, without elements repeated in the second:", set_1 - set_
2)
Exercise 06-22
Write a program that compares two lists. Consider the first list as the initial version
and the second as the version after changes. Using operations with sets, your
program should print the list of modifications between these two versions, listing:
BEFORE = [1, 2, 5, 6, 9]
AFTER = [1, 2, 8, 10]
before_set = set(BEFORE)
after_set = set(AFTER)
Chapter 07
Exercise 07-01
Write a program that reads two strings. Check that the second occurs inside the first
one and print the starting position.
2nd string: BE
position = [Link](second)
if position == -1:
print(f"'{second}' not found in '{first}'")
else:
print(f"{second} found at position {position} in {first}")
Exercise 07-02
Write a program that reads two strings and generates a third with the characters
common to the two strings read.
Result: CBT
The order of the characters in the result string is not important, but it must contain
all the letters common to both.
third = ""
if third == "":
print("No common characters found.")
else:
print(f"Common characters: {third}")
Exercise 07-03
Write a program that reads two strings and generates a third one with the characters
that appear in only one string.
3rd string: BT
third = ""
if third == "":
print("No uncommon characters found.")
else:
print(f"Uncommon characters: {third}")
Exercise 07-04
Write a program that reads a string and prints how many times each character
appears in that string.
String: TTAAC
Result:
T: 2x
A: 2x
C: 1x
counter = {}
Exercise 07-05
Write a program that reads two strings and generates a third one in which the
characters of the second are removed from the first.
2nd string: TG
third = ""
if third == "":
print("All characters were removed.")
else:
print(f"The characters {second} were removed from {first}, resulting
in: {third}")
Exercise 07-06
Write a program that reads three strings. The first string is your source string. The
second one has the characters that will be replaced by the ones in the third string.
Your program should create a fourth string, the resulting string, which is the first
string with the characters of the second replaced by the ones in the third.
2nd string: TG
3rd string: AC
Result: AAAACCAA
if len(second) == len(third):
result = ""
for letter in first:
position = [Link](letter)
if position != -1:
result += third[position]
else:
result += letter
if result == "":
print("All characters were removed.")
else:
print(
f"The characters {second} were replaced by "
f"{third} in {first}, resulting in: {result}"
)
else:
print("ERROR: The second and third strings must have the same
length.")
Exercise 07-07
Write a program that asks the user to type a phrase and print out how many vowels it
contains. Don’t consider uppercase and lowercase letters to be different. Example: A
phrase like “The house” should print three “eou”.
vowels = "aeiou"
phrase = input("Enter a phrase: ")
lowercase_phrase = [Link]()
for vowel in vowels:
vowel_count = lowercase_phrase.count(vowel)
if vowel_count > 0:
print(f"{vowel} appears {vowel_count} time(s)")
Exercise 07-08
Write a program to display all the words in a sentence. Consider that a word ends
with a blank space or when the string ends. Example: “The mouse gnawed at the
clothes” should print 6.
Exercise 07-09
Modify the hangman game (Program 7.2) to write the secret word in case the player
loses.
Exercise 07-10
Modify Program 7.2 to use a list of words. At the beginning, ask for a number and
calculate the index of the word to be used using the formula: index = (number * 776)
% len(word_list).
words = [
"house",
"ball",
"hose",
"grape",
"okra",
"computer",
"snake",
"lentil",
"rice",
]
errors += 1
print("You missed!")
print("X==:==\nX : ")
print("X O " if errors >= 1 else "X")
line2 = ""
if errors == 2:
line2 = r" | "
elif errors == 3:
line2 = r" \| "
elif errors >= 4:
line2 = r" \|/ "
print(f"X{line2}")
line3 = ""
if errors == 5:
line3 += r" / "
elif errors >= 6:
line3 += r" / \ "
print(f"X{line3}")
print("X\n===========")
if errors == 6:
print("Hanged!")
print(f"The secret word was: {word}")
break
Exercise 07-11
Modify Program 7.2 to use lists of strings to draw the hangman doll. You can use a list
for each row and organize them into a list of lists. Instead of controlling when to print
each part, draw on those lists, replacing the element to be drawn.
Example:
>>> line = list(“X------”) >>> line [“X”, “-”, “-”, “-”, “-”, “-”, “-”]
>>> line[6] = “|” >>> line [“X”, “-”, “-”, “-”, “-”, “-”, “|”]
words = [
"house",
"ball",
"hose",
"grape",
"okra",
"computer",
"snake",
"lentil",
"rice",
]
lines_txt = """
X==:==
X :
X
X
X
X
=======
"""
lines = []
while True:
password = ""
for letter in word:
password += letter if letter in hits else "."
print(password)
if password == word:
print("You got it right!")
break
attempt = input("\nEnter a letter:").lower().strip()
if attempt in typed:
print("You already tried this letter!")
continue
else:
typed += attempt
if attempt in word:
hits += attempt
else:
errors += 1
print("You missed!")
if errors == 1:
lines[3][3] = "O"
elif errors == 2:
lines[4][3] = "|"
elif errors == 3:
lines[4][2] = "\\"
elif errors == 4:
lines[4][4] = "/"
elif errors == 5:
lines[5][2] = "/"
elif errors == 6:
lines[5][4] = "\\"
print("".join(line))
if errors == 6:
print("Hanged!")
print(f"The secret word was: {word}")
break
Exercise 07-12
Write a tic-tac-toe game for two players. The game should ask you where you want to
play and switch between players. With each move, check if the position is free. Also,
check when a player has won the match. A tic-tac-toe game can be seen as a list of
three elements, each element being another list with three elements.
Game example:
X | O |
---+---+---
| X | X
---+---+---
| | O
7 | 8 | 9
---+---+---
4 | 5 | 6
---+---+---
1 | 2 | 3
#
# Tic Tac Toe
#
# The board
board = """ Positions
| | 7 | 8 | 9
---+---+--- ---+---+---
| | 4 | 5 | 6
---+---+--- ---+---+---
| | 1 | 2 | 3
"""
# A list of positions (row and column) for each valid game position
# An extra element was added to facilitate index manipulation
# and so that they have the same value as the position
#
# 7 | 8 | 9
# ---+---+---
# 4 | 5 | 6
# ---+---+---
# 1 | 2 | 3
positions = [
None, # Element added to facilitate indices
(5, 1), # 1
(5, 5), # 2
(5, 9), # 3
(3, 1), # 4
(3, 5), # 5
(3, 9), # 6
(1, 1), # 7
(1, 5), # 8
(1, 9), # 9
]
print("".join(t))
if not playing: # End after printing the last board
break
if moves == 9: # If 9 moves were made, all positions have been filled
print("It's a draw! No one won.")
break
move = int(input(f"Enter position to play 1-9 (player {player}):"))
if move < 1 or move > 9:
print("Invalid position")
continue
# Check if the position is free
if board_grid[positions[move][0]][positions[move][1]] != " ":
print("Position occupied.")
continue
# Mark the move for the player
board_grid[positions[move][0]][positions[move][1]] = player
# Check if won
for p in winning:
for x in p:
if board_grid[positions[x][0]][positions[x][1]] != player:
break
else: # If the for loop ends without break, all positions in p
belong to the same player
print(f"Player {player} won ({p}): ")
playing = False
break
player = "X" if player == "O" else "O" # Switch player
moves += 1 # Move counter
Chapter 08
Exercise 08-01
Write a function that returns the greater of two numbers.
maximum(2, 1) == 2
maximum(7, 7) == 7
Exercise 08-02
Write a function that takes two numbers and returns True if the first number is a
multiple of the second.
multiple(7, 3) == False
multiple(5, 5) == True
Exercise 08-03
Write a function that takes the length of the side of a square and returns its area (A =
side2).
Expected values:
square_area(4) == 16
square_area(9) == 81
def square_area(side):
return side**2
Exercise 08-04
Write a function that takes the base and height of a triangle and returns its area (A =
(base x height) / 2).
Exercise 08-05
Rewrite the function of Program 8.1 to use the list search methods (seen in Chapter
7).
Exercise 08-06
Rewrite Program 8.2 to use for instead of while.
def sum(values):
total = 0
for e in values:
total += e
return total
L = [1, 7, 2, 9, 15]
print(sum(L))
print(sum([7, 9, 12, 3, 100, 20, 4]))
Exercise 08-07
Define a recursive function that calculates the greatest common divisor (G.C.D.)
between two numbers a and b, where a > b.
Where
l c m ( a , b ) = \{ | a × b | g c d ( a , b ) [.tml-eqn]##
Exercise 08-08
Using the gcd function defined in the previous exercise, define a function to calculate
the least common multiple (LCM) between two numbers.
Exercise 08-09
Trace Program 8.6 and compare your result with the one presented.
Exercise 08-10
Rewrite the function for calculating the Fibonacci sequence without recursion.
def fibonacci(n):
prev = 0
next = 1
while n > 0:
prev, next = next, next + prev
n -= 1
return prev
for x in range(10):
print(f"fibonacci({x}) = {fibonacci(x)}")
Exercise 08-11
Write a function to validate a string variable. This function takes the string, the
minimum and maximum number of characters, as parameters. Return True if the
string size is between the maximum and minimum values; otherwise, return False.
print(validate_string("", 1, 5))
print(validate_string("ABC", 2, 5))
print(validate_string("ABCEFG", 3, 5))
print(validate_string("ABCEFG", 1, 10))
Exercise 08-12
Write a function that takes a string and a list. The function must compare the string
passed with the elements of the list, which is also passed as a parameter. Return True
if the string is found within the list; otherwise, return False.
print(search_string("AB", L))
print(search_string("CD", L))
print(search_string("EF", L))
print(search_string("FG", L))
print(search_string("XYZ", L))
Exercise 08-13-a
Write a function that receives a string with the valid options to accept (each option is
a letter). Convert valid options to lowercase letters. Use input to read an option,
convert the value to lowercase letters, and verify that the option is valid. In the case of
an invalid option, the function must ask the user to re-enter another option.
Exercise 08-13-b
Write a function that receives a string with the valid options to accept (each option is
a letter). Convert valid options to lowercase letters. Use input to read an option,
convert the value to lowercase letters, and verify that the option is valid. In the case of
an invalid option, the function must ask the user to re-enter another option.
def validate_options(valid_options):
valid_options = valid_options.lower()
while True:
option = input("Enter an option:").lower()
if option in valid_options:
return option
print("Invalid option, please choose again.")
Exercise 08-14
Change Program 8.22 so that the user has three chances of getting the number right.
The program terminates if the user finds the right number or makes three mistakes.
import random
n = [Link](1, 10)
attempts = 0
while attempts < 3:
x = int(input("Choose a number between 1 and 10: "))
if x == n:
print("You got it right!")
break
else:
print("You got it wrong.")
attempts += 1
Exercise 08-15
Change Program 7.2, the hangman game. Choose the word to guess using random
numbers.
import random
words = [
"house",
"ball",
"hose",
"grape",
"okra",
"computer",
"snake",
"lentil",
"rice",
]
# Choose a random word
word = words[[Link](0, len(words) - 1)]
typed = []
hits = []
errors = 0
lines_txt = """
X==:==
X :
X
X
X
X
=======
"""
lines = []
while True:
password = ""
for letter in word:
password += letter if letter in hits else "."
print(password)
if password == word:
print("You got it right!")
break
attempt = input("\nType a letter:").lower().strip()
if attempt in typed:
print("You already tried this letter!")
continue
else:
typed += attempt
if attempt in word:
hits += attempt
else:
errors += 1
print("You missed!")
if errors == 1:
lines[3][3] = "O"
elif errors == 2:
lines[4][3] = "|"
elif errors == 3:
lines[4][2] = "\\"
elif errors == 4:
lines[4][4] = "/"
elif errors == 5:
lines[5][2] = "/"
elif errors == 6:
lines[5][4] = "\\"
Exercise 08-16
Modify the alien game. Create a variable that represents the player’s life, starting with
100 points. The game ends when you find the alien or you run out of life (<=0). With
each mistake, your life is decreased by a random value between 5 and 20 points,
representing an attack by the alien. You can remove the part of the game’s code that
limits the number of attempts and let only the player’s or alien’s life decide when the
match ends. Show how much life the player has left before guessing the next tree
number.
import random
player_health = 100
tree = [Link](1, 100)
print("An alien is hiding behind a tree")
print("Each tree has been numbered from 1 to 100.")
print("You have 3 attempts to guess which tree")
print("the alien is hiding behind.")
if player_health <= 0:
print("You didn't survive. The alien won.")
print(f"The alien was behind tree {tree}.")
Exercise 08-17
Improve the program from the previous exercise by asking the player for the desired
difficulty level. In easy mode, life starts at 100 points, and the alien can do between 5
and 20 points of damage. In normal mode, life begins at 80 points, and the alien can
cause damage between 10 and 25 points. In hard mode, on the other hand, life begins
at 75, and the alien causes damage between 20 and 30 points. Add messages and
special characters to make the game more fun.
import random
while True:
level = input("\nEnter level number (1-3): ")
if level in ["1", "2", "3"]:
break
print(" Invalid option! Choose 1, 2, or 3.")
if level == "1":
player_health = 100
min_damage, max_damage = 5, 20
elif level == "2":
player_health = 80
min_damage, max_damage = 10, 25
else:
player_health = 75
min_damage, max_damage = 20, 30
if player_health <= 0:
print("\n Game Over! You didn't survive.")
print(f" The alien was behind tree {tree}.")
Exercise 08-18
Modify Program 8.26 to receive two optional parameters. One is to indicate the
character to print before the number, with white space being the default value. The
second optional parameter is how many characters to add per level, with 2 as the
default value.
# Usage example:
# print_lists([1, 2, 3, [4, 5, 6, [7, 8, 9]], 10], character="*",
increment=4)
Exercise 08-19
Write a generator capable of generating the sequence of prime numbers.
def primes(n):
p = 1 # Position in sequence
yield 2 # 2 is the only even prime number
d = 3 # divisor starts with 3
b = 3 # dividend starts with 3, is the number we'll test if it's
prime
while p < n:
# print(d, b, d % b, p, n)
if b % d == 0: # If b is divisible by d, the remainder will be 0
if b == d: # If b equals d, all d values have been tested
yield b # b is prime
p += 1 # increment the sequence
b += 2 # Move to the next odd number
d = 3 # Start dividing by 3 again
elif d < b: # Continue trying?
d += 2 # Increment the divisor to the next odd number
else:
b += 2 # Try another odd number
Exercise 08-20
Write a generator capable of generating a sequence with the factorial from 1 to n,
where n is passed as a parameter to the generator.
def factorial_generator(n):
value = 1
for element in range(1, n + 1):
value *= element
yield value
# Usage example:
# Generate factorials from 1 to 5
for n, factorial in enumerate(factorial_generator(5), 1):
print(f"{n}! = {factorial}")
Exercise 08-21
Write a function that generates numbers like Python’s range function, but the last
number is included in the interval. This function takes three parameters, and its
behavior changes if we pass one, two, or three parameters. Call it a myrange.
You may have noticed that, unlike range, the myrange function considers the end of
the interval as closed; the last number is part of the range.
current = start
while current <= end: # Note the <= to include the last value
yield current
current += step
# Test cases
print(list(myrange(1))) # [0, 1]
print(list(myrange(1, 10))) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(list(myrange(0, 10, 2))) # [0, 2, 4, 6, 8, 10]
Exercise 08-22
Modify the calculator program that uses partial to support two more operations: root
for square root and power for exponentiation.
import math
import operator
from functools import partial
operations = {
"+": partial(execute, [Link], "+"),
"-": partial(execute, [Link], "-"),
"*": partial(execute, [Link], "×"),
"/": partial(execute, [Link], "÷"),
"sqrt": partial(execute_unary, [Link], "square root of "),
"power": partial(execute, [Link], "power"),
}
operand1 = input("Operand 1: ")
operation = input("Operation: ").strip().lower()
if operation in operations:
if operation == "sqrt": # Square root has only one operand
operations[operation](operand1)
else:
operand2 = input("Operand 2: ")
operations[operation](operand1, operand2)
else:
print("Invalid operation!")
Chapter 09
Exercise 09-01
Write a program that takes the name of a file from the command line and prints every
line in that file.
import sys
Exercise 09-02
Modify the program from Exercise 9.1 to receive two more parameters for printing:
the start line and the end line. The program must print only the lines between these
values (including the start and end lines).
import sys
Exercise 09-03
Create a program that reads the files [Link] and [Link] and creates a single file
[Link] with all the lines from the other two files in numerical order.
def read_number(file):
while True:
number = [Link]()
# Check if something was read
if number == "":
return None
# Ignore blank lines
if [Link]() != "":
return int(number)
even_odd.close()
[Link]()
[Link]()
Exercise 09-04
Create a program that takes the names of two files as command line parameters and
generates an output file with the lines from the first followed by the lines from the
second file. The name of the output file can also be passed as a parameter on the
command line.
import sys
[Link]()
[Link]()
[Link]()
Exercise 09-05
Create a program that reverses the order of the lines in the [Link] file. The first line
must contain the largest number, and the last line must contain the smallest number.
L = [Link]()
[Link]()
for line in L:
[Link](line)
[Link]()
[Link]()
Exercise 09-06
Modify Program 9.5 to print the = symbol 40 times if = is the first character in the
line. Also, add the option to stop printing until you press the Enter key each time a
line starts with . (dot).
WIDTH = 79
input_file = open("[Link]")
for line in input_file.readlines():
if line[0] == ";":
continue
elif line[0] == ">":
print(line[1:].rjust(WIDTH))
elif line[0] == "*":
print(line[1:].center(WIDTH))
elif line[0] == "=":
print("=" * 40)
elif line[0] == ".":
input("Press Enter to continue")
print()
else:
print(line)
input_file.close()
Exercise 09-07
Create a program that reads a text file and generates a paginated output file. Each line
must not contain more than 76 characters. Each page should have a maximum of 60
lines. The last line of each page should include the number of the current page and
the name of the original file.
page = 1
lines = 1
line = ""
for word in words:
word = [Link]()
if len(line) + len(word) + 1 > WIDTH:
lines, page = write(output, line, lines, page)
line = ""
line += word + " "
if line != "":
lines, page = write(output, line, lines, page)
input_file.close()
[Link]()
Exercise 09-08
Modify the program from Exercise 9.7 to receive the number of characters per line
and the number of lines per page from the command line.
import sys
if len([Link]) != 4:
print("\nUsage: [Link] file width lines\n\n")
[Link](1)
FILENAME = [Link][1]
WIDTH = int([Link][2])
LINES = int([Link][3])
page = 1
lines = 1
input_file.close()
[Link]()
Exercise 09-09
Create a program that prints a list of files. The file names will be passed in the
command line. You should open and print them one by one.
import sys
if len([Link]) < 2:
print("\nUsage: [Link] file1 [file2 file3 fileN]\n\n\n")
[Link](1)
Exercise 09-10
Create a program that receives a list of file names and generates one large output file
containing all other files.
import sys
if len([Link]) < 2:
print("\nUsage: [Link] file1 [file2 file3 fileN]\n\n\n")
[Link](1)
Exercise 09-11
Create a program that reads a file and creates a dictionary where each key is a word
and each value is the number of occurrences in the file.
import sys
if len([Link]) != 2:
print("\nUsage: [Link] file1\n\n\n")
[Link](1)
name = [Link][1]
counter = {}
Exercise 09-12
Modify the Exercise 9.11 program to also record the row and column of each
occurrence of the word in the file. To do this, use lists with the values of each word,
saving the row and column of each occurrence.
import sys
if len([Link]) != 2:
print("\nUsage: [Link] file1\n\n\n")
[Link](1)
name = [Link][1]
counter = {}
line_num = 1
column = 1
Exercise 09-13
Create a program that prints the lines of a file. This program must receive three
parameters via the command line: the file’s name, the starting line, and the last line
to print.
Exercise 09-14
Create a program that reads a text file and eliminates repeated spaces between words
and at the end of lines. The output file must also not have more than one repeated
blank line.
import sys
if len([Link]) != 3:
print("\nUsage: [Link] input output\n\n\n")
[Link](1)
input_file = [Link][1]
output_file = [Link][2]
[Link]()
[Link]()
Exercise 09-15
Refer to Program 7.2, the hangman game. Use a text editor to generate a file with a
word written on each line. Modify the program to load (read) the list of words from
the text file. Also, try asking for the player’s name and generating a file with the
number of correct answers for the five best players.
words = []
score = {}
def load_words():
file = open("[Link]", "r", encoding="utf-8")
for word in [Link]():
word = [Link]().lower()
if word != "":
[Link](word)
[Link]()
def load_score():
file = open("[Link]", "r", encoding="utf-8")
for line in [Link]():
line = [Link]()
if line != "":
user, counter = [Link](";")
score[user] = int(counter)
[Link]()
def save_score():
file = open("[Link]", "w", encoding="utf-8")
for user in [Link]():
[Link]("{user};{score[user]}\n")
[Link]()
def update_score(name):
if name in score:
score[name] += 1
else:
score[name] = 1
save_score()
def display_score():
sorted_score = []
for user, score_value in [Link]():
sorted_score.append([user, score_value])
sorted_score.sort(key=lambda score_value: score_value[1])
print("\n\nBest players by number of hits:")
sorted_score.reverse()
for up in sorted_score:
print(f"{up[0]:30s} {up[1]:10d}")
load_words()
load_score()
typed = []
hits = []
errors = 0
while True:
password = ""
for letter in word:
password += letter if letter in hits else "."
print(password)
if password == word:
print("You got it right!")
display_score()
Exercise 09-16
Explain how the name and phone fields are stored in the output file.
Exercise 09-17
Change Program 9.6 to display the phonebook size in the main menu. Consider the
number of names it contains as its size.
address_book = []
def ask_name():
return input("Name: ")
def ask_phone():
return input("Phone: ")
def ask_filename():
return input("File name: ")
def search(name):
mname = [Link]()
for p, e in enumerate(address_book):
if e[0].lower() == mname:
return p
return None
def new():
global address_book
name = ask_name()
phone = ask_phone()
address_book.append([name, phone])
def delete():
global address_book
name = ask_name()
p = search(name)
if p is not None:
del address_book[p]
else:
print("Name not found.")
def modify():
p = search(ask_name())
if p is not None:
name = address_book[p][0]
phone = address_book[p][1]
print("Found:")
show_data(name, phone)
name = ask_name()
phone = ask_phone()
address_book[p] = [name, phone]
else:
print("Name not found.")
def list_all():
print("\nAddress Book\n\n\------")
for e in address_book:
show_data(e[0], e[1])
print("\------\n")
def read():
global address_book
filename = ask_filename()
file = open(filename, "r", encoding="utf-8")
address_book = []
for l in [Link]():
name, phone = [Link]().split("#")
address_book.append([name, phone])
[Link]()
def save():
filename = ask_filename()
file = open(filename, "w", encoding="utf-8")
for e in address_book:
[Link](f"{e[0]}#{e[1]}\n")
[Link]()
def menu():
print(
"""
1 - New
2 - Modify
3 - Delete
4 - List
5 - Save
6 - Read
0 - Exit
"""
)
print(f"\nNames in address book: {len(address_book)}\n")
return validate_integer_range("Choose an option: ", 0, 6)
while True:
option = menu()
if option == 0:
break
elif option == 1:
new()
elif option == 2:
modify()
elif option == 3:
delete()
elif option == 4:
list_all()
elif option == 5:
save()
elif option == 6:
read()
Exercise 09-18
What happens if your name or phone number contains the character used as a
separator in your content? Explain the problem and propose a solution.
Exercise 09-19
Change the list_all function so that it also displays the position of each element.
address_book = []
def ask_name():
return input("Name: ")
def ask_phone():
return input("Phone: ")
def ask_filename():
return input("File name: ")
def search(name):
mname = [Link]()
for p, e in enumerate(address_book):
if e[0].lower() == mname:
return p
return None
def new():
global address_book
name = ask_name()
phone = ask_phone()
address_book.append([name, phone])
def delete():
global address_book
name = ask_name()
p = search(name)
if p is not None:
del address_book[p]
else:
print("Name not found.")
def modify():
p = search(ask_name())
if p is not None:
name = address_book[p][0]
phone = address_book[p][1]
print("Found:")
show_data(name, phone)
name = ask_name()
phone = ask_phone()
address_book[p] = [name, phone]
else:
print("Name not found.")
def list_all():
print("\nAddress Book\n\n\------")
# We use the enumerate function to get the position in the address
book
for position, e in enumerate(address_book):
# Print the position without line break
print(f"Position: {position}", end="")
show_data(e[0], e[1])
print("\------\n")
def read():
global address_book
filename = ask_filename()
file = open(filename, "r", encoding="utf-8")
address_book = []
for l in [Link]():
name, phone = [Link]().split("#")
address_book.append([name, phone])
[Link]()
def save():
filename = ask_filename()
file = open(filename, "w", encoding="utf-8")
for e in address_book:
[Link](f"{e[0]}#{e[1]}\n")
[Link]()
def menu():
print(
"""
1 - New
2 - Modify
3 - Delete
4 - List
5 - Save
6 - Read
0 - Exit
"""
)
print(f"\nNames in address book: {len(address_book)}\n")
return validate_integer_range("Choose an option: ", 0, 6)
while True:
option = menu()
if option == 0:
break
elif option == 1:
new()
elif option == 2:
modify()
elif option == 3:
delete()
elif option == 4:
list_all()
elif option == 5:
save()
elif option == 6:
read()
Exercise 09-20
Add the option to sort the list by name in the main menu.
address_book = []
def ask_name():
return input("Name: ")
def ask_phone():
return input("Phone: ")
def ask_filename():
return input("File name: ")
def search(name):
mname = [Link]()
for p, e in enumerate(address_book):
if e[0].lower() == mname:
return p
return None
def new():
global address_book
name = ask_name()
phone = ask_phone()
address_book.append([name, phone])
def delete():
global address_book
name = ask_name()
p = search(name)
if p is not None:
del address_book[p]
else:
print("Name not found.")
def modify():
p = search(ask_name())
if p is not None:
name = address_book[p][0]
phone = address_book[p][1]
print("Found:")
show_data(name, phone)
name = ask_name()
phone = ask_phone()
address_book[p] = [name, phone]
else:
print("Name not found.")
def list_all():
print("\nAddress Book\n\n\------")
# We use the enumerate function to get the position in the address
book
for position, e in enumerate(address_book):
# We print the position without line break
print(f"Position: {position} ", end="")
show_data(e[0], e[1])
print("\------\n")
def read():
global address_book
filename = ask_filename()
file = open(filename, "r", encoding="utf-8")
address_book = []
for l in [Link]():
name, phone = [Link]().split("#")
address_book.append([name, phone])
[Link]()
def sort():
# You can sort the list as shown in the book
# with the bubble sort method
# Or combine Python's sort method with lambdas to
# define the list key
# address_book.sort(key=lambda e: return e[0])
end = len(address_book)
while end > 1:
i = 0
swapped = False
while i < (end - 1):
if address_book[i] > address_book[i + 1]:
# Option: address_book[i], address_book[i+1] = address_
book[i+1], address_book[i]
temp = address_book[i + 1]
address_book[i + 1] = address_book[i]
address_book[i] = temp
swapped = True
i += 1
if not swapped:
break
def save():
filename = ask_filename()
file = open(filename, "w", encoding="utf-8")
for e in address_book:
[Link](f"{e[0]}#{e[1]}\n")
[Link]()
def menu():
print(
"""
1 - New
2 - Modify
3 - Delete
4 - List
5 - Save
6 - Read
7 - Sort by name
0 - Exit
"""
)
print(f"\nNames in address book: {len(address_book)}\n")
return validate_integer_range("Choose an option: ", 0, 7)
while True:
option = menu()
if option == 0:
break
elif option == 1:
new()
elif option == 2:
modify()
elif option == 3:
delete()
elif option == 4:
list_all()
elif option == 5:
save()
elif option == 6:
read()
elif option == 7:
sort()
Exercise 09-21
Using the update and delete functions, ask the user to confirm the change and
deletion of a name before performing the operation itself.
address_book = []
def ask_name():
return input("Name: ")
def ask_phone():
return input("Phone: ")
def ask_filename():
return input("File name: ")
def search(name):
mname = [Link]()
for p, e in enumerate(address_book):
if e[0].lower() == mname:
return p
return None
def new():
global address_book
name = ask_name()
phone = ask_phone()
address_book.append([name, phone])
def confirm(operation):
while True:
option = input(f"Confirm {operation} (Y/N)? ").upper()
if option in "YN":
return option
else:
print("Invalid response. Choose Y or N.")
def delete():
global address_book
name = ask_name()
p = search(name)
if p is not None:
if confirm("deletion") == "Y":
del address_book[p]
else:
print("Name not found.")
def modify():
p = search(ask_name())
if p is not None:
name = address_book[p][0]
phone = address_book[p][1]
print("Found:")
show_data(name, phone)
name = ask_name()
phone = ask_phone()
if confirm("modification") == "Y":
address_book[p] = [name, phone]
else:
print("Name not found.")
def list_all():
print("\nAddress Book\n\n\------")
# We use the enumerate function to get the position in the address
book
for position, e in enumerate(address_book):
# Print the position without line break
print(f"Position: {position} ", end="")
show_data(e[0], e[1])
print("\------\n")
def read():
global address_book
filename = ask_filename()
file = open(filename, "r", encoding="utf-8")
address_book = []
for l in [Link]():
name, phone = [Link]().split("#")
address_book.append([name, phone])
[Link]()
def sort():
# You can sort the list as shown in the book
# using the bubble sort method
# Or combine Python's sort method with lambdas to
# define the list key
# address_book.sort(key=lambda e: return e[0])
end = len(address_book)
while end > 1:
i = 0
swapped = False
while i < (end - 1):
if address_book[i] > address_book[i + 1]:
# Option: address_book[i], address_book[i+1] = address_
book[i+1], address_book[i]
temp = address_book[i + 1]
address_book[i + 1] = address_book[i]
address_book[i] = temp
swapped = True
i += 1
if not swapped:
break
def save():
filename = ask_filename()
file = open(filename, "w", encoding="utf-8")
for e in address_book:
[Link](f"{e[0]}#{e[1]}\n")
[Link]()
def menu():
print(
"""
1 - New
2 - Modify
3 - Delete
4 - List
5 - Save
6 - Read
7 - Sort by name
0 - Exit
"""
)
print(f"\nNames in address book: {len(address_book)}\n")
return validate_integer_range("Choose an option: ", 0, 7)
while True:
option = menu()
if option == 0:
break
elif option == 1:
new()
elif option == 2:
modify()
elif option == 3:
delete()
elif option == 4:
list_all()
elif option == 5:
save()
elif option == 6:
read()
elif option == 7:
sort()
Exercise 09-22
When reading or writing a new phonebook, verify that the current phonebook has
already been saved. You can use a variable to control when the phonebook was
changed (new, updated, deleted) and reset that value when loaded or saved.
address_book = []
def ask_name():
return input("Name: ")
def ask_phone():
return input("Phone: ")
def ask_filename():
return input("File name: ")
def search(name):
mname = [Link]()
for p, e in enumerate(address_book):
if e[0].lower() == mname:
return p
return None
def new():
global address_book, changed
name = ask_name()
phone = ask_phone()
address_book.append([name, phone])
changed = True
def confirm(operation):
while True:
option = input(f"Confirm {operation} (Y/N)? ").upper()
if option in "YN":
return option
else:
print("Invalid response. Choose Y or N.")
def delete():
global address_book, changed
name = ask_name()
p = search(name)
if p is not None:
if confirm("deletion") == "Y":
del address_book[p]
changed = True
else:
print("Name not found.")
def modify():
global changed
p = search(ask_name())
if p is not None:
name = address_book[p][0]
phone = address_book[p][1]
print("Found:")
show_data(name, phone)
name = ask_name()
phone = ask_phone()
if confirm("modification") == "Y":
address_book[p] = [name, phone]
changed = True
else:
print("Name not found.")
def list_all():
print("\nAddress Book\n\n\------")
def read():
global address_book, changed
if changed:
print(
"You haven't saved the list since the last change. Do you want
to save it now?"
)
if confirm("saving") == "Y":
save()
print("Read\n---")
filename = ask_filename()
file = open(filename, "r", encoding="utf-8")
address_book = []
for l in [Link]():
name, phone = [Link]().split("#")
address_book.append([name, phone])
[Link]()
changed = False
def sort():
global changed
# You can sort the list as shown in the book
# using the bubble sort method
# Or combine Python's sort method with lambdas to
# define the list key
# address_book.sort(key=lambda e: return e[0])
end = len(address_book)
while end > 1:
i = 0
swapped = False
while i < (end - 1):
if address_book[i] > address_book[i + 1]:
def save():
global changed
if not changed:
print("You haven't changed the list. Do you want to save it
anyway?")
if confirm("saving") == "N":
return
print("Save\n\------")
filename = ask_filename()
file = open(filename, "w", encoding="utf-8")
for e in address_book:
[Link](f"{e[0]}#{e[1]}\n")
[Link]()
changed = False
def menu():
print(
"""
1 - New
2 - Modify
3 - Delete
4 - List
5 - Save
6 - Read
7 - Sort by name
0 - Exit
"""
)
print(f"\nNames in address book: {len(address_book)} Changed:
{changed}\n")
return validate_integer_range("Choose an option: ", 0, 7)
while True:
option = menu()
if option == 0:
break
elif option == 1:
new()
elif option == 2:
modify()
elif option == 3:
delete()
elif option == 4:
list_all()
elif option == 5:
save()
elif option == 6:
read()
elif option == 7:
sort()
Exercise 09-23
Change the program to load the last phonebook when initializing. Tip: Use another
file to store the filename.
address_book = []
def ask_name():
return input("Name: ")
def ask_phone():
return input("Phone: ")
def ask_filename():
return input("File name: ")
def search(name):
mname = [Link]()
for p, e in enumerate(address_book):
if e[0].lower() == mname:
return p
return None
def new():
global address_book, changed
name = ask_name()
phone = ask_phone()
address_book.append([name, phone])
changed = True
def confirm(operation):
while True:
option = input(f"Confirm {operation} (Y/N)? ").upper()
if option in "YN":
return option
else:
print("Invalid response. Choose Y or N.")
def delete():
global address_book, changed
name = ask_name()
p = search(name)
if p is not None:
if confirm("deletion") == "Y":
del address_book[p]
changed = True
else:
print("Name not found.")
def modify():
global changed
p = search(ask_name())
if p is not None:
name = address_book[p][0]
phone = address_book[p][1]
print("Found:")
show_data(name, phone)
name = ask_name()
phone = ask_phone()
if confirm("modification") == "Y":
address_book[p] = [name, phone]
changed = True
else:
print("Name not found.")
def list_all():
print("\nAddress Book\n\n\------")
# We use the enumerate function to get the position in the address
book
for position, e in enumerate(address_book):
# Print the position without line break
print(f"Position: {position} ", end="")
show_data(e[0], e[1])
print("\------\n")
def read_last_saved_book():
last = last_book()
if last is not None:
read_file(last)
def last_book():
try:
file = open("last [Link]", "r", encoding="utf-8")
last = [Link]()[:-1]
[Link]()
except FileNotFoundError:
return None
return last
def update_last(name):
file = open("last [Link]", "w", encoding="utf-8")
[Link](f"{name}\n")
[Link]()
def read_file(filename):
global address_book, changed
file = open(filename, "r", encoding="utf-8")
address_book = []
for l in [Link]():
name, phone = [Link]().split("#")
address_book.append([name, phone])
[Link]()
changed = False
def read():
global changed
if changed:
print(
"You haven't saved the list since the last change. Do you want
to save it now?"
)
if confirm("save") == "Y":
save()
print("Read\n---")
filename = ask_filename()
read_file(filename)
update_last(filename)
def sort():
global changed
# You can sort the list as shown in the book
# using the bubble sort method
# Or combine Python's sort method with lambdas to
# define the list key
# address_book.sort(key=lambda e: return e[0])
end = len(address_book)
while end > 1:
i = 0
swapped = False
while i < (end - 1):
if address_book[i] > address_book[i + 1]:
# Option: address_book[i], address_book[i+1] = address_
book[i+1], address_book[i]
temp = address_book[i + 1]
address_book[i + 1] = address_book[i]
address_book[i] = temp
swapped = True
i += 1
if not swapped:
break
changed = True
def save():
global changed
if not changed:
def menu():
print(
"""
1 - New
2 - Modify
3 - Delete
4 - List
5 - Save
6 - Read
7 - Sort by name
0 - Exit
"""
)
print(f"\nNames in address book: {len(address_book)} Changed:
{changed}\n")
return validate_integer_range("Choose an option: ", 0, 7)
read_last_saved_book()
while True:
option = menu()
if option == 0:
break
elif option == 1:
new()
elif option == 2:
modify()
elif option == 3:
delete()
elif option == 4:
list_all()
elif option == 5:
save()
elif option == 6:
read()
elif option == 7:
sort()
Exercise 09-24
What happens to the phonebook if a loading or saving error occurs? Explain.
Exercise 09-25
Change the ask_name and ask_telephone functions to receive an optional parameter.
If this parameter is passed, use it as the value returned if the data entry is empty.
address_book = []
def ask_name(default=""):
name = input("Name: ")
if name == "":
name = default
return name
def ask_phone(default=""):
phone = input("Phone: ")
if phone == "":
phone = default
return phone
def ask_filename():
return input("File name: ")
def search(name):
mname = [Link]()
for p, e in enumerate(address_book):
if e[0].lower() == mname:
return p
return None
def new():
def confirm(operation):
while True:
option = input(f"Confirm {operation} (Y/N)? ").upper()
if option in "YN":
return option
else:
print("Invalid response. Choose Y or N.")
def delete():
global address_book, changed
name = ask_name()
p = search(name)
if p is not None:
if confirm("deletion") == "Y":
del address_book[p]
changed = True
else:
print("Name not found.")
def modify():
global changed
p = search(ask_name())
if p is not None:
name = address_book[p][0]
phone = address_book[p][1]
print("Found:")
show_data(name, phone)
name = ask_name(name) # If nothing is entered, keep the value
phone = ask_phone(phone)
if confirm("modification") == "Y":
address_book[p] = [name, phone]
changed = True
else:
def list_all():
print("\nAddress Book\n\n\------")
# We use the enumerate function to get the position in the address
book
for position, e in enumerate(address_book):
# Print the position without line break
print(f"Position: {position} ", end="")
show_data(e[0], e[1])
print("\------\n")
def read_last_saved_address_book():
last = last_address_book()
if last is not None:
read_file(last)
def last_address_book():
try:
file = open("last address [Link]", "r", encoding="utf-8")
last = [Link]()[:-1]
[Link]()
except FileNotFoundError:
return None
return last
def update_last(name):
file = open("last address [Link]", "w", encoding="utf-8")
[Link](f"{name}\n")
[Link]()
def read_file(filename):
global address_book, changed
file = open(filename, "r", encoding="utf-8")
address_book = []
for l in [Link]():
name, phone = [Link]().split("#")
address_book.append([name, phone])
[Link]()
changed = False
def read():
global changed
if changed:
print(
"You haven't saved the list since the last change. Do you want
to save it now?"
)
if confirm("saving") == "Y":
save()
print("Read\n---")
filename = ask_filename()
read_file(filename)
update_last(filename)
def sort():
global changed
# You can sort the list as shown in the book
# using the bubble sort method
# Or combine Python's sort method with lambdas to
# define the list key
# address_book.sort(key=lambda e: return e[0])
end = len(address_book)
while end > 1:
i = 0
swapped = False
while i < (end - 1):
if address_book[i] > address_book[i + 1]:
# Option: address_book[i], address_book[i+1] = address_
book[i+1], address_book[i]
temp = address_book[i + 1]
address_book[i + 1] = address_book[i]
address_book[i] = temp
swapped = True
i += 1
if not swapped:
break
changed = True
def save():
global changed
if not changed:
print("You haven't changed the list. Do you want to save it
anyway?")
if confirm("saving") == "N":
return
print("Save\n\------")
filename = ask_filename()
file = open(filename, "w", encoding="utf-8")
for e in address_book:
[Link](f"{e[0]}#{e[1]}\n")
[Link]()
update_last(filename)
changed = False
def menu():
print(
"""
1 - New
2 - Modify
3 - Delete
4 - List
5 - Save
6 - Read
7 - Sort by name
0 - Exit
"""
)
print(f"\nNames in address book: {len(address_book)} Changed:
{changed}\n")
return validate_integer_range("Choose an option: ", 0, 7)
read_last_saved_address_book()
while True:
option = menu()
if option == 0:
break
elif option == 1:
new()
elif option == 2:
modify()
elif option == 3:
delete()
elif option == 4:
list_all()
elif option == 5:
save()
elif option == 6:
read()
elif option == 7:
sort()
Exercise 09-26
Change the program to verify the repetition of names. Generate an error message to
avoid having two phonebook entries with the same name. You should check this
before adding or updating names.
address_book = []
def ask_name(default=""):
name = input("Name: ")
if name == "":
name = default
return name
def ask_phone(default=""):
phone = input("Phone: ")
if phone == "":
phone = default
return phone
def ask_filename():
return input("File name: ")
def search(name):
mname = [Link]()
for p, e in enumerate(address_book):
if e[0].lower() == mname:
return p
return None
def new():
global address_book, changed
name = ask_name()
if search(name) is not None:
print("Name already exists!")
return
phone = ask_phone()
address_book.append([name, phone])
changed = True
def confirm(operation):
while True:
option = input(f"Confirm {operation} (Y/N)? ").upper()
if option in "YN":
return option
else:
print("Invalid response. Choose Y or N.")
def delete():
global address_book, changed
name = ask_name()
p = search(name)
if p is not None:
if confirm("deletion") == "Y":
del address_book[p]
changed = True
else:
print("Name not found.")
def modify():
global changed
p = search(ask_name())
if p is not None:
name = address_book[p][0]
phone = address_book[p][1]
print("Found:")
show_data(name, phone)
name = ask_name(name) # If nothing is entered, keep the value
phone = ask_phone(phone)
if confirm("modification") == "Y":
address_book[p] = [name, phone]
changed = True
else:
print("Name not found.")
def list_all():
print("\nAddress Book\n\n\------")
# We use the enumerate function to get the position in the address
book
for position, e in enumerate(address_book):
# Print the position, without line break
print(f"Position: {position} ", end="")
show_data(e[0], e[1])
print("\------\n")
def read_last_saved_address_book():
last = last_address_book()
if last is not None:
read_file(last)
def last_address_book():
try:
file = open("last address [Link]", "r", encoding="utf-8")
last = [Link]()[:-1]
[Link]()
except FileNotFoundError:
return None
return last
def update_last(name):
file = open("last address [Link]", "w", encoding="utf-8")
[Link](f"{name}\n")
[Link]()
def read_file(filename):
global address_book, changed
def read():
global changed
if changed:
print(
"You haven't saved the list since the last change. Do you want
to save it now?"
)
if confirm("saving") == "Y":
save()
print("Read\n---")
filename = ask_filename()
read_file(filename)
update_last(filename)
def sort():
global changed
# You can sort the list as shown in the book
# using the bubble sort method
# Or combine Python's sort method with lambdas to
# define the list key
# address_book.sort(key=lambda e: return e[0])
end = len(address_book)
while end > 1:
i = 0
swapped = False
while i < (end - 1):
if address_book[i] > address_book[i + 1]:
# Option: address_book[i], address_book[i+1] = address_
book[i+1], address_book[i]
temp = address_book[i + 1]
address_book[i + 1] = address_book[i]
address_book[i] = temp
swapped = True
i += 1
if not swapped:
break
changed = True
def save():
global changed
if not changed:
print("You haven't changed the list. Do you want to save it
anyway?")
if confirm("saving") == "N":
return
print("Save\n\------")
filename = ask_filename()
file = open(filename, "w", encoding="utf-8")
for e in address_book:
[Link](f"{e[0]}#{e[1]}\n")
[Link]()
update_last(filename)
changed = False
def menu():
print(
"""
1 - New
2 - Modify
3 - Delete
4 - List
5 - Save
6 - Read
7 - Sort by name
0 - Exit
"""
)
print(f"\nNames in address book: {len(address_book)} Changed:
{changed}\n")
return validate_integer_range("Choose an option: ", 0, 7)
read_last_saved_address_book()
while True:
option = menu()
if option == 0:
break
elif option == 1:
new()
elif option == 2:
modify()
elif option == 3:
delete()
elif option == 4:
list_all()
elif option == 5:
save()
elif option == 6:
read()
elif option == 7:
sort()
Exercise 09-27
Modify the program to control each person’s birthday and email address.
address_book = []
def ask_name(default=""):
name = input("Name: ")
if name == "":
name = default
return name
def ask_phone(default=""):
phone = input("Phone: ")
if phone == "":
phone = default
return phone
def ask_email(default=""):
email = input("Email: ")
if email == "":
email = default
return email
def ask_birthday(default=""):
birthday = input("Birthday: ")
if birthday == "":
birthday = default
return birthday
def ask_filename():
return input("File name: ")
def search(name):
mname = [Link]()
for p, e in enumerate(address_book):
if e[0].lower() == mname:
return p
return None
def new():
global address_book, changed
name = ask_name()
if search(name) is not None:
print("Name already exists!")
return
phone = ask_phone()
email = ask_email()
birthday = ask_birthday()
address_book.append([name, phone, email, birthday])
changed = True
def confirm(operation):
while True:
option = input(f"Confirm {operation} (Y/N)? ").upper()
if option in "YN":
return option
else:
print("Invalid response. Choose Y or N.")
def delete():
global address_book, changed
name = ask_name()
p = search(name)
if p is not None:
if confirm("deletion") == "Y":
del address_book[p]
changed = True
else:
print("Name not found.")
def edit():
global changed
p = search(ask_name())
if p is not None:
name = address_book[p][0]
phone = address_book[p][1]
email = address_book[p][2]
birthday = address_book[p][3]
print("Found:")
show_data(name, phone, email, birthday)
name = ask_name(name) # If nothing is entered, keep the value
phone = ask_phone(phone)
email = ask_email(email)
birthday = ask_birthday(birthday)
if confirm("edit") == "Y":
address_book[p] = [name, phone, email, birthday]
changed = True
else:
print("Name not found.")
def list_all():
print("\nAddress Book\n\n\------")
# We use the enumerate function to get the position in the address
book
for position, e in enumerate(address_book):
# Print the position
print(f"\nPosition: {position}")
show_data(e[0], e[1], e[2], e[3])
print("\------\n")
def read_last_saved_address_book():
last = last_address_book()
if last is not None:
read_file(last)
def last_address_book():
try:
file = open("last address [Link]", "r", encoding="utf-8")
last = [Link]()[:-1]
[Link]()
except FileNotFoundError:
return None
return last
def update_last(name):
file = open("last address [Link]", "w", encoding="utf-8")
[Link](f"{name}\n")
[Link]()
def read_file(filename):
global address_book, changed
file = open(filename, "r", encoding="utf-8")
address_book = []
for l in [Link]():
name, phone, email, birthday = [Link]().split("#")
address_book.append([name, phone, email, birthday])
[Link]()
changed = False
def read():
global changed
if changed:
print(
"You haven't saved the list since the last change. Do you want
to save it now?"
)
if confirm("save") == "Y":
save()
print("Read\n---")
filename = ask_filename()
read_file(filename)
update_last(filename)
def sort():
global changed
# You can sort the list as shown in the book
# with the bubble sort method
# Or combine Python's sort method with lambdas to
# define the list key
# address_book.sort(key=lambda e: return e[0])
end = len(address_book)
while end > 1:
i = 0
swapped = False
while i < (end - 1):
if address_book[i] > address_book[i + 1]:
# Option: address_book[i], address_book[i+1] = address_
book[i+1], address_book[i]
temp = address_book[i + 1]
address_book[i + 1] = address_book[i]
address_book[i] = temp
swapped = True
i += 1
if not swapped:
break
changed = True
def save():
global changed
if not changed:
print("You haven't changed the list. Do you want to save it
anyway?")
if confirm("save") == "N":
return
print("Save\n\------")
filename = ask_filename()
file = open(filename, "w", encoding="utf-8")
for e in address_book:
[Link](f"{e[0]}#{e[1]}#{e[2]}#{e[3]}\n")
[Link]()
update_last(filename)
changed = False
def menu():
print(
"""
1 - New
2 - Edit
3 - Delete
4 - List
5 - Save
6 - Read
7 - Sort by name
0 - Exit
"""
)
print(f"\nNames in address book: {len(address_book)} Changed:
{changed}\n")
return validate_integer_range("Choose an option: ", 0, 7)
read_last_saved_address_book()
while True:
option = menu()
if option == 0:
break
elif option == 1:
new()
elif option == 2:
edit()
elif option == 3:
delete()
elif option == 4:
list_all()
elif option == 5:
save()
elif option == 6:
read()
elif option == 7:
sort()
Exercise 09-28
Modify the program to register multiple phones for the same person. This also allows
you to register the type of telephone number: cell phone, landline, home, or work.
address_book = []
def ask_name(default=""):
name = input("Name: ")
if name == "":
name = default
return name
def ask_phone(default=""):
phone = input("Phone: ")
if phone == "":
phone = default
return phone
def ask_phone_type(default=""):
while True:
type = input("Phone type [%s]: " % ",".join(phone_types)).lower()
if type == "":
type = default
for t in phone_types:
if [Link](type):
return t # Returns the full name
else:
print("Invalid phone type!")
def ask_email(default=""):
email = input("Email: ")
if email == "":
email = default
return email
def ask_birthday(default=""):
birthday = input("Birthday: ")
if birthday == "":
birthday = default
return birthday
def ask_filename():
return input("File name: ")
def search(name):
mname = [Link]()
for p, e in enumerate(address_book):
if e[0].lower() == mname:
return p
return None
def new():
global address_book, changed
name = ask_name()
if search(name) is not None:
print("Name already exists!")
return
phones = []
while True:
number = ask_phone()
type = ask_phone_type()
[Link]([number, type])
if confirm("that you want to register another phone") == "N":
break
email = ask_email()
birthday = ask_birthday()
address_book.append([name, phones, email, birthday])
changed = True
def confirm(operation):
while True:
option = input(f"Confirm {operation} (Y/N)? ").upper()
if option in "YN":
return option
else:
print("Invalid response. Choose Y or N.")
def delete():
global address_book, changed
name = ask_name()
p = search(name)
if p is not None:
if confirm("deletion") == "Y":
del address_book[p]
changed = True
else:
print("Name not found.")
def change():
global changed
p = search(ask_name())
if p is not None:
def list_all():
print("\nAddress Book\n\n\------")
# We use the enumerate function to get the position in the address
book
for position, e in enumerate(address_book):
# Print the position
print(f"\nPosition: {position}")
show_data(e[0], e[1], e[2], e[3])
print("\------\n")
def read_last_saved_address_book():
last = last_address_book()
if last is not None:
read_file(last)
def last_address_book():
try:
file = open("last address book [Link]", "r", encoding="utf-8")
last = [Link]()[:-1]
[Link]()
except FileNotFoundError:
return None
return last
def update_last(name):
file = open("last address book [Link]", "w", encoding="utf-8")
[Link](f"{name}\n")
[Link]()
def read_file(filename):
global address_book, changed
file = open(filename, "rb")
address_book = [Link](file)
[Link]()
changed = False
def read():
global changed
if changed:
print(
"You haven't saved the list since the last change. Do you want
to save it now?"
)
if confirm("saving") == "Y":
save()
print("Read\n---")
filename = ask_filename()
read_file(filename)
update_last(filename)
def sort():
global changed
# You can sort the list as shown in the book
# with the bubble sort method
# Or combine Python's sort method with lambdas to
# define the list key
# address_book.sort(key=lambda e: return e[0])
end = len(address_book)
while end > 1:
i = 0
swapped = False
def save():
global changed
if not changed:
print("You haven't changed the list. Do you want to save it
anyway?")
if confirm("saving") == "N":
return
print("Save\n\------")
filename = ask_filename()
def menu():
print(
"""
1 - New
2 - Change
3 - Delete
4 - List
5 - Save
6 - Read
7 - Sort by name
0 - Exit
"""
)
print(f"\nNames in address book: {len(address_book)} Changed:
{changed}\n")
return validate_integer_range("Choose an option: ", 0, 7)
read_last_saved_address_book()
while True:
option = menu()
if option == 0:
break
elif option == 1:
new()
elif option == 2:
change()
elif option == 3:
delete()
elif option == 4:
list_all()
elif option == 5:
save()
elif option == 6:
read()
elif option == 7:
sort()
Exercise 09-29
Modify Program 9.8 to use the p element for movie titles instead of h2.
movies = {
"drama": ["Citizen Kane", "The Godfather"],
"comedy": ["Modern Times", "American Pie", "Dr. Dolittle"],
"crime": ["Black Rain", "Death Wish", "Hard to Kill"],
"war": ["Rambo", "Platoon", "Tora!Tora!Tora!"],
}
Exercise 09-30
Modify Program 9.8 to generate an HTML list using the ul and li elements. Every
element in the list must be inside the ul element and inside an li element. Here’s an
example:
<ul\><li>Item1</li><li>Item2</li><li>Item3</li></ul>
movies = {
"drama": ["Citizen Kane", "The Godfather"],
"comedy": ["Modern Times", "American Pie", "Dr. Dolittle"],
"crime": ["Black Rain", "Death Wish", "Hard to Kill"],
"war": ["Rambo", "Platoon", "Tora!Tora!Tora!"],
}
Exercise 09-31
Create a program that corrects Program 9.9 to verify that z exists and is a directory.
import [Link]
if [Link]("z"):
print("The directory z exists.")
elif [Link]("z"):
print("z exists, but it is a file and not a directory.")
else:
print("The directory z does not exist.")
Exercise 09-32
Modify Program 9.9 to receive the file name or directory to be verified through the
command line. Print if it exists and if it’s a file or a directory.
import sys
import [Link]
if len([Link]) < 2:
print("Enter the name of the file or directory to check as a
parameter!")
[Link](1)
name = [Link][1]
if [Link](name):
print(f"The directory {name} exists.")
elif [Link](name):
print(f"The file {name} exists.")
else:
print(f"{name} does not exist.")
Exercise 09-33
Create a program that generates an HTML page with links to all the jpg and png files
found in a directory entered on the command line.
if len([Link]) < 2:
print("Enter the directory name to collect jpg and png files!")
[Link](1)
directory = [Link][1]
[Link](
"""
</body>
</html>
"""
)
[Link]()
Exercise 09-34
Revisit Program 7.2, the hangman game. Modify it to use time functions to record the
duration of the matches.
import time
Exercise 09-35
Using the [Link] function, create an HTML page with the name and size of each file
from a directory passed in the command line, including all its sub-directories.
import sys
import os
import [Link]
def generate_style(level):
return style_mask % (level * 20)
if len([Link]) < 2:
print("Enter the directory name to collect files!")
[Link](1)
directory = [Link][1]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Files</title>
</head>
<body>
"""
)
[Link](f"Files found starting from directory: {directory}")
generate_listing(page, directory)
[Link](
"""
</body>
</html>
"""
)
[Link]()
Exercise 09-36
Using the [Link] function, create a program that calculates the space occupied per
directory, generating an HTML page with the results.
import sys
import os
import [Link]
import math
def generate_style(level):
return style_mask % (level * 30)
nroot = [Link]([Link])
return level
if len([Link]) < 2:
print("Enter the directory name to collect files!")
[Link](1)
directory = [Link][1]
Exercise 09-37
Write a program that reads a student’s name and four grades. The program must
write the data into a file using the JSON format.
import json
Exercise 09-38
Modify the previous program to read the same file, allowing you to add more data. If
the same name is entered twice, update the data using the new entry.
Tip: You may have to use a list of dictionaries to hold multiple entries.
import json
import os
def read_grades():
grades = []
for i in range(4):
grade = float(input(f"Enter the {i+1}th grade: "))
[Link](grade)
return grades
def load_data():
# If the file doesn't exist, returns an empty list
if not [Link]("student_grades.json"):
return []
try:
with open("student_grades.json", "r") as file:
return [Link](file)
except [Link]:
return []
student_exists = False
for i, student in enumerate(students):
if student["name"] == name:
students[i] = new_student
student_exists = True
break
if not student_exists:
[Link](new_student)
Exercise 09-39
Modify Program 9.6, the phonebook. Have it read and write the phonebook in a file in
JSON format.
import json
contacts = []
def ask_name():
return input("Name: ")
def ask_phone():
return input("Phone: ")
def ask_filename():
return input("File name: ")
def search(name):
mname = [Link]()
for p, e in enumerate(contacts):
if e[0].lower() == mname:
return p
return None
def new():
name = ask_name()
phone = ask_phone()
[Link]([name, phone])
def delete():
name = ask_name()
p = search(name)
if p is not None:
del contacts[p]
else:
print("Name not found.")
def modify():
p = search(ask_name())
if p is not None:
name = contacts[p][0]
phone = contacts[p][1]
print("Found:")
show_data(name, phone)
name = ask_name()
phone = ask_phone()
contacts[p] = [name, phone]
else:
print("Name not found.")
def list_all():
print("\nContacts\n\n\------")
for e in contacts:
show_data(e[0], e[1])
print("\------\n")
def read():
global contacts
filename = ask_filename()
try:
with open(filename, "r", encoding="utf-8") as file:
contacts = [Link](file)
except FileNotFoundError:
print("File not found")
except [Link]:
print("Error reading JSON file")
def save():
filename = ask_filename()
def menu():
print(
"""
1 – New
2 – Modify
3 – Delete
4 – List
5 – Save
6 – Read
0 – Exit
"""
)
return validate_integer_range("Choose an option: ", 0, 6)
elif option == 5:
save()
elif option == 6:
read()
Exercise 09-40
Modify the [Link] program to print only the first 512 bytes of the file.
import sys
import itertools
if __name__ == "__main__":
with open([Link][1], "rb") as f:
image = [Link](512) # Doesn't read the entire file, only the first
512 bytes
print_bytes(image)
Exercise 09-41
Change the [Link] program to receive the maximum number of bytes to print
and how many bytes per line as command line parameters.
import sys
import itertools
if __name__ == "__main__":
if len([Link]) != 4:
print("Usage: python [Link] file max_bytes bytes_per_line")
[Link](1)
file = [Link][1]
max_bytes = int([Link][2])
bytes_per_line = int([Link][3])
print_bytes(image, bytes_per_line)
Exercise 09-42
Modify Program 9.20 so that it uses the name of the image to be generated from the
command line.
import sys
filename = [Link][1]
" r r r r b b b g r r g g g g r",
" rrr r b bbbbbb g g g g g r",
" r r b b b gr b rg g g g ",
" r r b b b g rrr g g gg r",
" r r b b b ggggg g g r",
]
# Point multiplier
# Each point will be copied multiplier times in the image
# If equal to 4, each point generates a 4x4 point block
multiplier = 32
# Add padding
width_bytes = width * 3
width_with_padding = padding(width_bytes)
if width_bytes != width_with_padding:
for p, d in enumerate(binary_data):
binary_data[p] = b"".join(
[binary_data[p], bytes(width_with_padding - width_bytes)]
)
bmp_header = [
b"BM", # Identifier
bytes_little_endian(54 + size), # Image size in bytes
bytes(4), # 4 bytes 0x00
bytes_little_endian(54), # Header size
]
dib_header = [
bytes_little_endian(40), # DIB header size
bytes_little_endian(width),
bytes_little_endian(
-height, signed=True
), # Negative height to build image from top to bottom
bytes_little_endian(1, 2), # Color planes
bytes_little_endian(24, 2), # Bits per pixel
bytes_little_endian(0), # No compression
bytes_little_endian(size),
bytes_little_endian(2835), # ceil(72 dpi x 39.3701 in/m) horizontal
bytes_little_endian(2835), # ceil(72 dpi x 39.3701 in/m) vertical
bytes_little_endian(0), # Number of colors in palette
bytes_little_endian(0), # Important colors
]
bmp_header_binary = b"".join(bmp_header)
dib_header_binary = b"".join(dib_header)
binary_data = b"".join(binary_data)
assert len(dib_header_binary) == 40
assert len(binary_data) == size
Exercise 09-43
Modify the program from the previous exercise to receive a second parameter with
the file name that contains the drawing. The goal is to read the drawing from that file.
import sys
output_file = [Link][1]
drawing_file = [Link][2]
# Point multiplier
# Each point will be copied multiplier times in the image
# If equal to 4, each point generates a 4x4 point block
multiplier = 32
# Add padding
width_bytes = width * 3
width_with_padding = padding(width_bytes)
if width_bytes != width_with_padding:
for p, d in enumerate(binary_data):
binary_data[p] = b"".join(
[binary_data[p], bytes(width_with_padding - width_bytes)]
)
bmp_header = [
b"BM", # Identifier
bytes_little_endian(54 + size), # Image size in bytes
bytes(4), # 4 bytes 0x00
bytes_little_endian(54), # Header size
]
dib_header = [
bytes_little_endian(40), # DIB header size
bytes_little_endian(width),
bytes_little_endian(
-height, signed=True
), # Negative height to build image top to bottom
bytes_little_endian(1, 2), # Color planes
bytes_little_endian(24, 2), # Bits per pixel
bytes_little_endian(0), # No compression
bytes_little_endian(size),
bytes_little_endian(2835), # ceil(72 dpi x 39.3701 in/m) horizontal
bytes_little_endian(2835), # ceil(72 dpi x 39.3701 in/m) vertical
bytes_little_endian(0), # Number of colors in palette
bytes_little_endian(0), # Important colors
]
bmp_header_binary = b"".join(bmp_header)
dib_header_binary = b"".join(dib_header)
binary_data = b"".join(binary_data)
Exercise 09-44
Modify the previous program to receive a third parameter with the color conversion
table in JSON format.
import sys
import json
output_file = [Link][1]
drawing_file = [Link][2]
color_file = [Link][3]
# Point multiplier
# Each point will be copied multiplier times in the image
# If equal to 4, each point generates a 4x4 point block
multiplier = 32
# Add padding
width_bytes = width * 3
width_with_padding = padding(width_bytes)
if width_bytes != width_with_padding:
for p, d in enumerate(binary_data):
binary_data[p] = b"".join(
[binary_data[p], bytes(width_with_padding - width_bytes)]
)
bmp_header = [
b"BM", # Identifier
bytes_little_endian(54 + size), # Image size in bytes
bytes(4), # 4 bytes 0x00
bytes_little_endian(54), # Header size
]
dib_header = [
bytes_little_endian(40), # DIB header size
bytes_little_endian(width),
bytes_little_endian(
-height, signed=True
bmp_header_binary = b"".join(bmp_header)
dib_header_binary = b"".join(dib_header)
binary_data = b"".join(binary_data)
Chapter 10
Exercise 10-01
Add size and brand attributes to the Television class. Create two Television objects
and assign them different sizes and brands. Then, print the value of those attributes
to confirm the independence of the values of each instance (object).
class Television:
def __init__(self):
[Link] = False
[Link] = 2
[Link] = 20
[Link] = "EastTiger"
tv = Television()
[Link] = 27
[Link] = "DingDang"
living_room_tv = Television()
living_room_tv.size = 52
living_room_tv.brand = "XangLa"
Exercise 10-02
Currently, the Television class initializes the channel with 2. Modify the Television
class to receive the initial channel in its constructor as an optional parameter.
class Television:
def __init__(self, initial_channel, min, max):
[Link] = False
[Link] = initial_channel
self.min_channel = min
self.max_channel = max
def channel_down(self):
if [Link] - 1 >= self.min_channel:
[Link] -= 1
def channel_up(self):
if [Link] + 1 <= self.max_channel:
[Link] += 1
tv = Television(5, 1, 99)
print([Link])
Exercise 10-03
Modify the Television class so that, if we ask to change the channel down beyond the
minimum, it goes to the maximum channel, and vice versa.
class Television:
def __init__(self, min, max):
[Link] = False
[Link] = min
self.min_channel = min
self.max_channel = max
def channel_down(self):
if [Link] - 1 >= self.min_channel:
[Link] -= 1
else:
[Link] = self.max_channel
def channel_up(self):
if [Link] + 1 <= self.max_channel:
[Link] += 1
else:
[Link] = self.min_channel
tv = Television(2, 10)
tv.channel_down()
print([Link])
tv.channel_up()
print([Link])
Exercise 10-04
Using what we learned with functions, modify the Television class constructor so that
channel_min and channel_max are optional parameters, where channel_min
defaults to 2 and channel_max defaults to 14.
class Television:
def __init__(self, min=2, max=14):
[Link] = False
[Link] = min
self.min_channel = min
self.max_channel = max
def channel_down(self):
if [Link] - 1 >= self.min_channel:
[Link] -= 1
else:
[Link] = self.max_channel
def channel_up(self):
if [Link] + 1 <= self.max_channel:
[Link] += 1
else:
[Link] = self.min_channel
tv = Television()
tv.channel_down()
print([Link])
tv.channel_up()
print([Link])
Exercise 10-05
Using the Television class modified in the previous exercise, create two instances
(objects), specifying the channel_min and channel_max value by name.
class Television:
def __init__(self, min=2, max=14):
[Link] = False
[Link] = min
self.min_channel = min
self.max_channel = max
def channel_down(self):
if [Link] - 1 >= self.min_channel:
[Link] -= 1
else:
[Link] = self.max_channel
def channel_up(self):
if [Link] + 1 <= self.max_channel:
[Link] += 1
else:
[Link] = self.min_channel
tv = Television(min=1, max=22)
tv.channel_down()
print([Link])
tv.channel_up()
print([Link])
Exercise 10-06
Modify the Television class so that the mute_channel_up and change_channel_down
methods return the channel after the change.
class Television:
def __init__(self, min=2, max=14):
[Link] = False
[Link] = min
self.min_channel = min
self.max_channel = max
def channel_down(self):
if [Link] - 1 >= self.min_channel:
[Link] -= 1
else:
[Link] = self.max_channel
return [Link]
def channel_up(self):
if [Link] + 1 <= self.max_channel:
[Link] += 1
else:
[Link] = self.min_channel
return [Link]
Exercise 10-07
Change the Television class to only accept the commands to change channels if
turned on.
class Television:
def __init__(self, min=2, max=14):
[Link] = False
[Link] = min
self.min_channel = min
self.max_channel = max
def channel_down(self):
if not [Link]:
return
if [Link] - 1 >= self.min_channel:
[Link] -= 1
else:
[Link] = self.max_channel
def channel_up(self):
if not [Link]:
return
if [Link] + 1 <= self.max_channel:
[Link] += 1
else:
[Link] = self.min_channel
Exercise 10-08
Change the program to produce a message stating the user has an insufficient account
balance if the user attempts to withdraw more than the available balance.
class Account:
def __init__(self, clients, number, balance=0):
[Link] = 0
[Link] = clients
[Link] = number
[Link] = []
[Link](balance)
def summary(self):
print(f"AC N°{[Link]} Balance: {[Link].2f}")
def statement(self):
print(f"Statement AC N° {[Link]}\n")
for o in [Link]:
print(f"{o[0]:10s} {o[1]:10.2f}")
print(f"\n Balance: {[Link].2f}\n")
class SpecialAccount(Account):
def __init__(self, clients, number, balance=0, limit=0):
Account.__init__(self, clients, number, balance)
[Link] = limit
Exercise 10-09
Modify the summary method of the Account class to display the name and phone
number of each client.
class Client:
def __init__(self, name, phone):
[Link] = name
[Link] = phone
class Account:
def __init__(self, clients, number, balance=0):
[Link] = 0
[Link] = clients
[Link] = number
[Link] = []
[Link](balance)
def summary(self):
print(f"AC N°{[Link]} Balance: {[Link].2f}\n")
for client in [Link]:
print(f"Name: {[Link]}\nPhone: {[Link]}\n")
def statement(self):
print(f"Statement AC N° {[Link]}\n")
for o in [Link]:
print(f"{o[0]:10s} {o[1]:10.2f}")
print(f"\n Balance: {[Link].2f}\n")
Exercise 10-10
Create a new account with Joao and Jose as clients and a balance of $500.
class Client:
def __init__(self, name, phone):
[Link] = name
[Link] = phone
class Account:
def __init__(self, clients, number, balance=0):
[Link] = 0
[Link] = clients
[Link] = number
[Link] = []
[Link](balance)
def summary(self):
print(f"AC N°{[Link]} Balance: {[Link].2f}\n")
for client in [Link]:
print(f"Name: {[Link]}\nPhone: {[Link]}\n")
def statement(self):
print(f"Statement AC N° {[Link]}\n")
for o in [Link]:
print(f"{o[0]:10s} {o[1]:10.2f}")
print(f"\n Balance: {[Link].2f}\n")
Exercise 10-11
Create classes representing states and cities. Each state has a name, an acronym, and
cities. Each city has a name and population. Write a test program to create three
states with a few cities. Display the population of each state as the sum of the
population of its cities.
class State:
def __init__(self, name, acronym):
[Link] = name
[Link] = acronym
[Link] = []
def population(self):
return sum([[Link] for c in [Link]])
class City:
def __init__(self, name, population):
[Link] = name
[Link] = population
[Link] = None
def __str__(self):
return (
f"City (name={[Link]}, population={[Link]},
state={[Link]})"
)
Exercise 10-12
Modify the Account and SpecialAccount classes so that the withdrawal operation
returns True if the withdrawal was made and False if the withdrawal failed.
class Client:
def __init__(self, name, phone):
[Link] = name
[Link] = phone
class Account:
def __init__(self, clients, number, balance=0):
[Link] = 0
[Link] = clients
[Link] = number
[Link] = []
[Link](balance)
def summary(self):
print(f"AC N°{[Link]} Balance: {[Link].2f}")
def statement(self):
print(f"Statement AC N° {[Link]}\n")
for o in [Link]:
print(f"{o[0]:10s} {o[1]:10.2f}")
print(f"\n Balance: {[Link].2f}\n")
class SpecialAccount(Account):
def __init__(self, clients, number, balance=0, limit=0):
Account.__init__(self, clients, number, balance)
[Link] = limit
Exercise 10-13
Change the SpecialAccount class so that your statement shows the limit and the total
available for withdrawal.
class Client:
def __init__(self, name, phone):
[Link] = name
[Link] = phone
class Account:
def __init__(self, clients, number, balance=0):
[Link] = 0
[Link] = clients
[Link] = number
[Link] = []
[Link](balance)
def summary(self):
print(f"AC N°{[Link]} Balance: {[Link].2f}")
def statement(self):
print(f"Statement AC N° {[Link]}\n")
for o in [Link]:
print(f"{o[0]:10s} {o[1]:10.2f}")
print(f"\n Balance: {[Link].2f}\n")
class SpecialAccount(Account):
def __init__(self, clients, number, balance=0, limit=0):
Account.__init__(self, clients, number, balance)
[Link] = limit
def statement(self):
[Link](self)
print(f"\n Limit: {[Link].2f}\n")
print(f"\n Available: {[Link] + [Link].2f}\n")
Exercise 10-14
Observe the withdrawal method from the Account and SpecialAccount classes.
Modify the withdrawal method of the Account class so that the possibility of
withdrawal is verified by a new method, replacing the current condition. This new
method should return True if the withdrawal can be performed or False if not. Modify
the SpecialAccount class to work with this new method. Check whether you need to
change the SpecialAccount withdrawal method or just the new method created to
verify the possibility of withdrawing.
class Client:
def __init__(self, name, phone):
[Link] = name
[Link] = phone
class Account:
def __init__(self, clients, number, balance=0):
[Link] = 0
[Link] = clients
[Link] = number
[Link] = []
[Link](balance)
def summary(self):
print(f"AC N°{[Link]} Balance: {[Link].2f}")
return True
else:
print("Insufficient balance!")
return False
def statement(self):
print(f"Statement AC N° {[Link]}\n")
for o in [Link]:
print(f"{o[0]:10s} {o[1]:10.2f}")
print(f"\n Balance: {[Link].2f}\n")
class SpecialAccount(Account):
def __init__(self, clients, number, balance=0, limit=0):
Account.__init__(self, clients, number, balance)
[Link] = limit
def statement(self):
[Link](self)
print(f"\n Limit: {[Link].2f}\n")
print(f"\n Available: {[Link] + [Link]:%10.2f}\n")
Exercise 10-15
Modify the UniqueList class to override the [Link] method. extend works
like append but takes a list as a parameter. Check the type of each element in the list
before adding it.
class UniqueList(UserList):
def __init__(self, elem_class, enumerable=None):
super().__init__(enumerable)
self.elem_class = elem_class
Chapter 11
Exercise 11-01
Make a program that creates the [Link] database with the price table to store a list
of sales prices for products. The table must contain the name of the product and its
respective price. The program must also insert some data for testing.
import sqlite3
from contextlib import closing
Exercise 11-02
Make a program to list all the prices in the [Link] database.
import sqlite3
from contextlib import closing
Exercise 11-03
Write a program that performs queries in the [Link] database created in Exercise
11.1. The program must ask for the product’s name and list its price.
import sqlite3
from contextlib import closing
Exercise 11-04
Modify the Exercise 11.3 program to ask for two values and list all the products with
prices between those two values.
import sqlite3
from contextlib import closing
[Link](
"""select * from prices
where price >= ? and price <= ?""",
(price1, price2),
)
found = 0
for result in [Link]():
print("Name: {0:30s} Price: {1:6.2f}".format(*result))
found += 1
if found == 0:
print("Not found.")
else:
print("{} product(s) found.".format(found))
Exercise 11-05
Write a program that increases the price of all products from the [Link] database
by 10%.
import sqlite3
from contextlib import closing
Exercise 11-06
Write a program that asks for the product’s name and a new price. Using the prices.
db database, update the product price with the same name in the database.
import sqlite3
from contextlib import closing
[Link](
"""select * from prices
where name = ?""",
(name,),
)
result = [Link]()
if result:
print("Name: {0:30s} Price: {1:6.2f}".format(*result))
new_price = input("Enter the new price: ")
[Link](
"""update prices
set price = ?
where name = ?""",
(new_price, name),
)
else:
print("Not found.")
Chapter 12
Exercise 12-01
Modify the previous program to recognize letter sequences. A letter is a character
between A and Z or between a and z (you must account for uppercase and lowercase
letters). Ignore accented characters. Print a list with the strings of letters found.
input_string = "ABC431DEF901c431203FXEW9"
output = []
number = []
Exercise 12-02
Using the pattern_check function, rewrite the function that parsed the numbers in
the entry ABC431DEF901C431203FXEW9 .
input_string = "ABC431DEF901c431203FXEW9"
def numbers(input_string):
found = 0
end = -1
for i, character in enumerate(input_string):
if "0" <= character <= "9":
found += 1
end = i
else:
break
return found, 0, end
position = 0
while position < len(input_string):
found, start, end = check_pattern(input_string[position:], [numbers])
if found > 0:
print(input_string[position : position + end + 1])
position += end + 1
else:
position += 1
Exercise 12-03
Using the pattern_check function, write a function that detects a date in the format
dd/mm/yy where dd is the day, mm the month, and yy the year. The function should
only detect the date pattern and it does not need to verify whether the date is valid.
Exercise 12-04
Using the pattern_check function, write a function that detects a value in dollars in
the format $999.99, where 9 represents any digit. The first number can have one or
more digits, but the second part (cents) must have a maximum of two digits.
if found > 0:
print(f"Dollars at positions: {position+start} to
{position+end} ", end="")
print("Dollars:", input_string[position + start : position +
end + 1])
found = True
position += end + 1
else:
position += 1
if not found:
print("No dollar values found in input")
print()
Exercise 12-05
Create a sequence function that receives qmax and qmin. It should work similarly to
number but call the sequence function. It should also work when qmin is 0 when the
sequence is optional.
inputs = [
"(((---)))", # Pattern found
"(((--)))", # Pattern not found
"(\----)", # Pattern not found
"\----", # Pattern not found
"((--))", # Pattern not found
"<(((--)))>", # Pattern found
"<<(((--)))>>", # Pattern found
"<<(((---)))>>", # Pattern found
"<<((--))>> <(((---)))> (((---))) ((((\----))))", # Pattern found
twice
]
# The pattern is a sequence of characters that can be optional
# < zero or up to two times
# ( three or up to four times
# - two or up to three times
# ) three or up to four times
# > zero or up to two times
# You can create other patterns to test the sequences function
pattern = [
partial(sequences, pattern="<", min_qty=0, max_qty=2),
partial(sequences, pattern="(", min_qty=3, max_qty=4),
partial(sequences, pattern="-", min_qty=2, max_qty=3),
partial(sequences, pattern=")", min_qty=3, max_qty=4),
partial(sequences, pattern=">", min_qty=0, max_qty=2),
]
print("Input:", input)
found = False
position = 0
while position < len(input):
found, start, end = check_pattern(input[position:], pattern)
if found > 0:
print(f"Pattern at positions: {position+start} to
{position+end} ", end="")
print("Pattern:", input[position + start : position + end +
1])
found = True
position += end + 1
else:
position += 1
if not found:
print("No pattern found in input")
print()
Exercise 12-06
Create a function using pattern_check that validates cell phone numbers. A cell
phone has 9 digits after the LDC (for example, (92)99812-1103).
return 1, 0, position - 1
def cell_number(input):
pattern = [
partial(sequence, pattern="("),
partial(number, min_qty=2, max_qty=3),
partial(sequence, pattern=")"),
partial(number, min_qty=5, max_qty=5),
partial(sequence, pattern="-"),
partial(number, min_qty=4, max_qty=4),
]
found, _, _ = check_pattern(input, pattern)
return found > 0
inputs = [
"(92)99999-9999", # Yes
"(11)99999-999", # No
"(2)99999-9999", # No
"(12)9999999999", # No
"(312)9999999999", # No
"(312)99999-9999", # Yes
]
Exercise 12-07
Write a program that validates user data entry. It will validate ISBNs (International
Standard Book Numbers), which are used to identify books worldwide. The program
must accept ISBNs in the following format: ISBN 999-9-99-999999-9, where each 9
represents a digit. The ISBN letters are optional and be case-insensitive. The space
between N and the first number is mandatory if the ISBN is present. Require the
dashes as in the example, verifying the correct number of digits.
import re
def validate_isbn(isbn):
# Regex pattern to validate the ISBN format
# ^ - start of string
# (?:ISBN\s+)? - optional "ISBN" followed by at least one space
# \d{3}-\d{1}-\d{2}-\d{6}-\d{1} - specific format with hyphens
# $ - end of string
# flags=[Link] - ignore case for "ISBN"
pattern = r"^(?:ISBN\s+)?\d{3}-\d{1}-\d{2}-\d{6}-\d{1}$"
while True:
isbn_input = input("Enter an ISBN: ").strip()
Exercise 12-08
Write a program that validates user data entry. It will validate ISSNs (International
Standard Serial Numbers), which are used to identify periodical publications, like
magazines. The program must accept ISSNs in the following format: ISSN 9999-
9999, where each 9 represents a digit. Require the dash at the end, verifying the
correct number of digits. The word ISSN is optional and must be accepted regardless
of the case. If the word ISSN is specified, the space between it and the number is
required.
import re
def validate_issn(issn):
# Pattern explanation:
# ^ - start of string
# (?:ISSN\s+)? - optional "ISSN" followed by one or more spaces
# \d{4}-\d{4} - exactly 4 digits, dash, exactly 4 digits
# $ - end of string
pattern = r"^(?:ISSN\s+)?\d{4}-\d{4}$"
print("ISSN Validator")
print("=" * 50)
print("Enter ISSN numbers to validate.")
print("Valid formats: 'ISSN 1234-5678' or '1234-5678'")
print("Press Enter without input to exit.")
print()
while True:
# Exit condition
if not issn_input:
print("Goodbye!")
break
print()
Exercise 12-09
Write a program that validates user data entry. Try to find a valid ISBN or ISSN, as
defined in previous exercises. Display a message stating whether the number is a
valid ISBN or ISSN (identify which one in your response).
CPF_RE = r"^\d{3}\.\d{3}\.\d{3}-\d{2}$"
CNPJ_RE = r"^\d{2}\.\d{3}\.\d{3}/\d{4}-\d{2}$"
def cpf(input):
return bool([Link](CPF_RE, input))
inputs = [
"12.345.678/9012-34", # Valid CNPJ
"12.345.678-9012-34", # Invalid
"99.999.999/9999-99", # Valid CNPJ
"123.456.789-01", # Valid CPF
"23.456.789-01", # Invalid
"999.456.789-01", # Valid CPF
]
Exercise 12-10
Write a function that accepts prices in dollars. The program must ignore white spaces
and accept values prefixed with $ or not. The user must enter correctly formatted
values with a comma separating the thousands and a period separating the cents. If
the user types cents, they must have two digits.
The function must return the entered value converted to float or generate a
ValueError exception if the value entered is invalid.
import re
DOLLARS_RE = r"^(([rR]\$)?(((\d{1,3}\.)?(\d{3}\.)*?\d{3})|(\d{1,3}))(,
\d{2})?)$"
# ^ - From the start of the string
# ( - Group of the right part of the expression, before the comma
# ([rR]\$)? - Optionally can have $
# ( - Group of the integer part
# ((\d{1,3}\.)?(\d{3}\.)*?\d{3}) - Group of the integer part with dots
separating thousands
# Required to ensure that parts between dots have 3 digits, except the
first one
# | - or
# (\d{1,3}) - Group of the integer part without dots
# ) - Group of the right part
# (,\d{2})? - Optionally can have the decimal part
# )$ - Until the end of the string
def clean_spaces(input):
return [Link](" ", "")
def dollars(input):
# It's easier to clean spaces before validation,
# since the regular expression is already quite complex
# Remember: you don't need to use regex for everything!
input = clean_spaces(input)
inputs = [
"$ 1.234,56", # Yes
"$ 1.234,56", # Yes
"$1.234,56", # Yes
"$12.123.234,56", # Yes
"1.234,56", # Yes
"$1.234,56", # Yes
"$234,56", # Yes
"$234,56", # Yes
"234,56", # Yes
"$234", # Yes
"$234", # Yes
"$2", # Yes
"$23", # Yes
"234", # Yes
"34", # Yes
"4", # Yes
"$234,4", # No - Cents must have 2 digits
"$234,4", # No - Cents must have 2 digits
"$1234,4", # No - Missing . to separate thousands
"$1234,4", # No - Missing . to separate thousands
"$1234.12,4", # No - Incorrect use of thousands separator (.)
"$1.24,56", # No - Irregular, only two numbers after the .
]
Chapter 13
Exercise 13-01
Modify Program 13.7 to save and load the drawing in JSON format. You can traverse
the objects on the canvas and save the chosen shape, coordinates, and colors.
import tkinter as tk
import [Link] as ttk
import json
from [Link] import askcolor
from [Link] import asksaveasfilename
from [Link] import askopenfilename
class App([Link]):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.background_color = ""
self.foreground_color = "black"
[Link] = [Link](self)
self.create_toolbar()
self.create_drawing_area()
[Link]("Drawing")
[Link]("1200x700")
[Link] = []
[Link]([Link].create_line((0, 0, 0, 0),
dash=[2, 4]))
[Link]([Link].create_line((0, 0, 0, 0),
dash=[2, 4]))
[Link] = 0
[Link] = None
[Link] = None
self.curr_id = 0
[Link](expand=True, fill=[Link])
[Link] = [Link].create_line
def create_drawing_area(self):
self.work_area = [Link]([Link], height=600)
self.work_area.grid(column=1, row=0, sticky=[Link])
[Link].grid_columnconfigure(1, weight=1)
[Link].grid_rowconfigure(0, weight=1)
def create_toolbar(self):
[Link] = [Link]([Link], width=100, height=600)
self.line_button = [Link](
[Link], text="Line", padding="10", command=self.line_
tool
)
self.line_button.pack()
self.oval_button = [Link](
[Link], text="Circle", padding="10", command=self.oval_
tool
)
self.oval_button.pack()
self.rectangle_button = [Link](
[Link],
text="Rectangle",
padding="10",
command=self.rectangle_tool,
)
self.rectangle_button.pack()
undo_button = [Link](
[Link], text="Undo", padding="10", command=[Link]
)
undo_button.pack()
clear_button = [Link](
[Link], text="Clear", padding="10", command=[Link]
)
clear_button.pack()
self.foreground_label = [Link]([Link], text="Foreground
Color")
self.foreground_label.pack()
self.foreground_button = [Link](
[Link],
text="Color",
command=self.foreground_color,
bg=self.foreground_color,
)
self.foreground_button.pack(fill="x")
self.background_label = [Link]([Link], text="Background
Color")
self.background_label.pack()
self.background_button = [Link](
[Link], text="Transparent", command=self.background_
color, bg=None
)
self.background_button.pack(fill="x")
self.save_button = [Link](
[Link], text="Save", padding="10", command=[Link]
)
self.save_button.pack(fill="x")
self.load_button = [Link](
[Link], text="Load", padding="10", command=[Link]
)
self.load_button.pack(fill="x")
[Link](column=0, row=0, sticky=[Link])
def undo(self):
if items := [Link].find_withtag("drawing"):
[Link](items[-1])
def clear(self):
[Link]("drawing")
def save(self):
filename = asksaveasfilename(
defaultextension=".json", filetypes=[("JSON", ".json")]
)
if not filename:
return # User cancelled
drawing = {} # Create a dictionary with the drawn objects
for item in [Link].find_withtag("drawing"):
drawing[item] = {
"type": (type := [Link](item)),
"coordinates": [Link](item),
"fill": [Link](item, "fill"),
}
if type in ["rectangle", "oval"]:
def load(self):
filename = askopenfilename(filetypes=[("JSON", ".json")])
if not filename:
return # User cancelled
with open(filename, "r") as f:
drawing = [Link](f)
[Link]()
for data in [Link]():
match data["type"]:
case "line":
[Link].create_line(
data["coordinates"], fill=data["fill"],
tags=["drawing"]
)
case "rectangle":
[Link].create_rectangle(
data["coordinates"],
fill=data["fill"],
outline=data["outline"],
tags=["drawing"],
)
case "oval":
[Link].create_oval(
data["coordinates"],
fill=data["fill"],
outline=data["outline"],
tags=["drawing"],
)
def background_color(self):
color = askcolor(title="Background color")
self.background_color = color[1] or ""
self.background_button.config(
text="Transparent" if self.background_color == "" else "",
background=self.background_color or "SystemButtonFace",
)
def foreground_color(self):
color = askcolor(title="Foreground color")
if color[1]:
self.foreground_color = color[1]
self.foreground_button.config(background=self.foreground_color)
def line_tool(self):
[Link] = [Link].create_line
def oval_tool(self):
[Link] = [Link].create_oval
def rectangle_tool(self):
[Link] = [Link].create_rectangle
{
"outline": self.foreground_color,
"fill": self.background_color,
},
)
[Link] = 1
App().mainloop()
Exercise 13-02
Modify the previous program and save the image in SVG format. The SVG format is a
text file that follows a well-defined format.
The coordinates are inside the style attribute. fill is the internal filling color. strike the
outline color. We have our oval in the ellipse element. cx, cy are the coordinates of the
center of the ellipse. rx and ry are the horizontal and vertical distance from the center
(radius).
The line is in the path element, property d. In d, we have a mini language in which m
means to move, followed by the coordinates x1, y1 (without spaces) of the first point
and the displacement (x2, y2) after that as the second coordinate. The line is then
drawn from (x1, y1) to (x1 + x2, y1 + y2). Try altering the file in a text editor and
changing the values before writing the program.
You can view an SVG file in any web browser, so don’t forget to reload the page
whenever you save your changes. Once you have a clear understanding of how to
manipulate these values, write the Python program. You can search for the
specification in SVG format on the internet, starting with the Wikipedia website:
import tkinter as tk
import [Link] as ttk
import json
from [Link] import askcolor
from [Link] import asksaveasfilename
from [Link] import askopenfilename
class App([Link]):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.background_color = ""
self.foreground_color = "black"
[Link] = [Link](self)
self.create_toolbar()
self.create_drawing_area()
[Link]("Drawing")
[Link]("1200x700")
[Link] = []
[Link]([Link].create_line((0, 0, 0, 0),
dash=[2, 4]))
[Link]([Link].create_line((0, 0, 0, 0),
dash=[2, 4]))
[Link] = 0
[Link] = None
[Link] = None
self.curr_id = 0
[Link](expand=True, fill=[Link])
[Link] = [Link].create_line
def create_drawing_area(self):
self.work_area = [Link]([Link], height=600)
self.work_area.grid(column=1, row=0, sticky=[Link])
[Link].grid_columnconfigure(1, weight=1)
[Link].grid_rowconfigure(0, weight=1)
[Link] = [Link](self.work_area, background="white")
[Link](fill=[Link], expand=True)
[Link]("<Motion>", self.mouse_move)
[Link]("<Button-1>", self.mouse_click)
[Link]("<ButtonRelease-1>", self.mouse_release)
[Link] = [Link](self.work_area, text="Move the mouse")
[Link](ipadx=10, ipady=10)
def create_toolbar(self):
[Link] = [Link]([Link], width=100, height=600)
self.line_button = [Link](
[Link], text="Line", padding="10", command=self.line_
tool
)
self.line_button.pack()
self.oval_button = [Link](
[Link], text="Circle", padding="10", command=self.oval_
tool
)
self.oval_button.pack()
self.rectangle_button = [Link](
[Link],
text="Rectangle",
padding="10",
command=self.rectangle_tool,
)
self.rectangle_button.pack()
undo_button = [Link](
[Link], text="Undo", padding="10", command=[Link]
)
undo_button.pack()
clear_button = [Link](
[Link], text="Clear", padding="10", command=[Link]
)
clear_button.pack()
self.foreground_label = [Link]([Link], text="Foreground
Color")
self.foreground_label.pack()
self.foreground_button = [Link](
[Link],
text="Color",
command=self.foreground_color,
bg=self.foreground_color,
)
self.foreground_button.pack(fill="x")
self.background_label = [Link]([Link], text="Background
Color")
self.background_label.pack()
self.background_button = [Link](
[Link], text="Transparent", command=self.background_
color, bg=None
)
self.background_button.pack(fill="x")
self.save_button = [Link](
[Link], text="Save", padding="10", command=[Link]
)
self.save_button.pack(fill="x")
self.load_button = [Link](
[Link], text="Load", padding="10", command=[Link]
)
self.load_button.pack(fill="x")
self.save_svg_button = [Link](
[Link], text="Save SVG", padding="10", command=self.
save_svg
)
self.save_svg_button.pack(fill="x")
[Link](column=0, row=0, sticky=[Link])
def undo(self):
if items := [Link].find_withtag("drawing"):
[Link](items[-1])
def clear(self):
[Link]("drawing")
def create_drawing_dict(self):
drawing = {} # Creates a dictionary with the drawn objects
for item in [Link].find_withtag("drawing"):
drawing[item] = {
"type": (type := [Link](item)),
"coordinates": [Link](item),
"fill": [Link](item, "fill"),
}
if type in ["rectangle", "oval"]:
outline = [Link](item, "outline")
drawing[item]["outline"] = outline
return drawing
def save(self):
name = asksaveasfilename(
defaultextension=".json", filetypes=[("JSON", ".json")]
)
if not name:
return # User cancelled
def save_svg(self):
name = asksaveasfilename(defaultextension=".svg", filetypes=[("SVG",
".svg")])
if not name:
return
size = [Link].winfo_width(), [Link].winfo_height()
base = f"""<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="{size[0]}mm" height="{size[1]}mm" viewBox="0 0 {size[0]}
{size[1]}" version="1.1" id="svg1" xmlns="[Link]
xmlns:svg="[Link]
<g id="layer1">"""
for i, item in enumerate(self.create_drawing_dict().values()):
x, y, xf, yf = item["coordinates"]
width, height = xf - x, yf - y
match item["type"]:
case "line":
base += f"""<path style="fill:none;stroke:
{item["fill"]};stroke-width:0.264583px;strokelinecap:butt;stroke-linejoin:
miter;stroke-opacity:1" d="m {x},{y} {width},{height}" id="line{i}" />"""
case "rectangle":
base += f"""<rect style="fill:{item["fill"] or "none"};
stroke:{item["outline"] or "none"};stroke-width:0.264583" id="rect{i}"
width="{width}" height="{height}" x="{item["coordinates"][0]}"
y="{item["coordinates"][1]}" />"""
case "oval":
base += f"""<ellipse style="fill:{item["fill"] or
"none"};stroke:{item["outline"] or "none"};stroke-width:0.264583"
id="oval{i}" cx="{x + width // 2}" cy="{y + height // 2}" rx="{width//2}"
ry="{height//2}" />"""
base += """</g></svg>"""
with open(name, "w") as f:
[Link](base)
def load(self):
name = askopenfilename(filetypes=[("JSON", ".json")])
if not name:
return # User cancelled
with open(name, "r") as f:
drawing = [Link](f)
[Link]()
for data in [Link]():
match data["type"]:
case "line":
[Link].create_line(
data["coordinates"], fill=data["fill"],
tags=["drawing"]
)
case "rectangle":
[Link].create_rectangle(
data["coordinates"],
fill=data["fill"],
outline=data["outline"],
tags=["drawing"],
)
case "oval":
[Link].create_oval(
data["coordinates"],
fill=data["fill"],
outline=data["outline"],
tags=["drawing"],
)
def background_color(self):
color = askcolor(title="Background Color")
self.background_color = color[1] or ""
self.background_button.config(
text="Transparent" if self.background_color == "" else "",
background=self.background_color or "SystemButtonFace",
)
def foreground_color(self):
color = askcolor(title="Foreground Color")
if color[1]:
self.foreground_color = color[1]
self.foreground_button.config(background=self.foreground_color)
def line_tool(self):
[Link] = [Link].create_line
def oval_tool(self):
[Link] = [Link].create_oval
def rectangle_tool(self):
[Link] = [Link].create_rectangle
App().mainloop()
Exercise 13-03
Modify the [Link] code (Program 13.12) to validate dates when editing or adding
new links. Display an error message if the date is invalid.
class Window([Link]):
MIN_X = 300
MIN_Y = 300
PADXY = 10
def create_controls(self):
self.f_url = [Link](self)
self.f_url.grid(row=0, column=0, columnspan=3, sticky=[Link],
**[Link])
self.l_url = [Link](self.f_url, text="URL")
self.l_url.pack(anchor=tk.W)
[Link] = [Link]()
self.e_url = [Link](self.f_url, textvariable=[Link])
self.e_url.pack(fill=tk.X, expand=True)
self.f_category = [Link](self)
self.f_category.grid(row=1, column=0, sticky=tk.W, **[Link])
self.l_category = [Link](self.f_category, text="Category")
self.l_category.pack(anchor=tk.W)
[Link] = [Link]()
self.e_category = [Link](self.f_category, textvariable=self.
category)
self.e_category.pack()
self.f_date = [Link](self)
self.f_date.grid(row=1, column=2, sticky=tk.E, **[Link])
self.l_date = [Link](self.f_date, text="Date")
self.l_date.pack(anchor=tk.W)
[Link] = Data(self.f_date)
[Link]()
self.f_notes = [Link](self)
self.f_notes.grid(row=2, column=0, columnspan=3, sticky=[Link],
**[Link])
self.l_notes = [Link](self.f_notes, text="Notes")
self.l_notes.pack(anchor=tk.W)
self.t_notes = [Link](self.f_notes, height=3)
self.t_notes.pack(expand=True, fill=[Link])
self.grid_columnconfigure(0, weight=2)
self.grid_columnconfigure(2, weight=1)
self.grid_rowconfigure(2, weight=1)
self.b_frame = [Link](self)
self.b_frame.grid(row=3, column=0, columnspan=3, **[Link])
self.b_ok = [Link](self.b_frame, text="Ok", command=[Link])
self.b_ok.pack(side=[Link])
self.b_cancel = [Link](self.b_frame, text="Cancel",
command=[Link])
self.b_cancel.pack(side=[Link])
def close(self):
[Link]()
def ok(self):
try:
self.validate_date()
except ValueError:
def validate_date(self):
day, month, year = [Link]().split("-")
valid_date = date(int(year), int(month), int(day))
return valid_date
Exercise 13-04
Modify the [Link] code (Program 13.12) not to accept blank URLs. Display an
error message if the URL is invalid (blank).
class Window([Link]):
MIN_X = 300
MIN_Y = 300
PADXY = 10
def create_controls(self):
self.f_url = [Link](self)
self.f_url.grid(row=0, column=0, columnspan=3, sticky=[Link],
**[Link])
self.l_url = [Link](self.f_url, text="URL")
self.l_url.pack(anchor=tk.W)
[Link] = [Link]()
self.e_url = [Link](self.f_url, textvariable=[Link])
self.e_url.pack(fill=tk.X, expand=True)
self.f_category = [Link](self)
self.f_category.grid(row=1, column=0, sticky=tk.W, **[Link])
self.l_category = [Link](self.f_category, text="Category")
self.l_category.pack(anchor=tk.W)
[Link] = [Link]()
self.e_category = [Link](self.f_category, textvariable=self.
category)
self.e_category.pack()
self.f_date = [Link](self)
self.f_date.grid(row=1, column=2, sticky=tk.E, **[Link])
self.l_date = [Link](self.f_date, text="Date")
self.l_date.pack(anchor=tk.W)
[Link] = Data(self.f_date)
[Link]()
self.f_notes = [Link](self)
self.f_notes.grid(row=2, column=0, columnspan=3, sticky=[Link],
**[Link])
self.l_notes = [Link](self.f_notes, text="Notes")
self.l_notes.pack(anchor=tk.W)
self.t_notes = [Link](self.f_notes, height=3)
self.t_notes.pack(expand=True, fill=[Link])
self.grid_columnconfigure(0, weight=2)
self.grid_columnconfigure(2, weight=1)
self.grid_rowconfigure(2, weight=1)
self.b_frame = [Link](self)
self.b_frame.grid(row=3, column=0, columnspan=3, **[Link])
self.b_ok = [Link](self.b_frame, text="Ok", command=[Link])
self.b_ok.pack(side=[Link])
self.b_cancel = [Link](self.b_frame, text="Cancel",
command=[Link])
self.b_cancel.pack(side=[Link])
def close(self):
[Link]()
def ok(self):
try:
self.validate_url()
except ValueError:
def validate_url(self):
if not [Link]():
raise ValueError("URL cannot be empty")
def validate_date(self):
day, month, year = [Link]().split("-")
date = date(int(year), int(month), int(day))
return date
Exercise 13-05
Modify the [Link] code (Program 13.12) to accept only URLs starting with
http:// or [Link]
class Window([Link]):
MIN_X = 300
MIN_Y = 300
PADXY = 10
def create_controls(self):
self.f_url = [Link](self)
self.f_url.grid(row=0, column=0, columnspan=3, sticky=[Link],
**[Link])
self.l_url = [Link](self.f_url, text="URL")
self.l_url.pack(anchor=tk.W)
[Link] = [Link]()
self.e_url = [Link](self.f_url, textvariable=[Link])
self.e_url.pack(fill=tk.X, expand=True)
self.f_category = [Link](self)
self.f_category.grid(row=1, column=0, sticky=tk.W, **[Link])
self.l_category = [Link](self.f_category, text="Category")
self.l_category.pack(anchor=tk.W)
[Link] = [Link]()
self.e_category = [Link](self.f_category, textvariable=self.
category)
self.e_category.pack()
self.f_date = [Link](self)
self.f_date.grid(row=1, column=2, sticky=tk.E, **[Link])
self.l_date = [Link](self.f_date, text="Date")
self.l_date.pack(anchor=tk.W)
[Link] = Data(self.f_date)
[Link]()
self.f_notes = [Link](self)
self.f_notes.grid(row=2, column=0, columnspan=3, sticky=[Link],
**[Link])
self.l_notes = [Link](self.f_notes, text="Notes")
self.l_notes.pack(anchor=tk.W)
self.t_notes = [Link](self.f_notes, height=3)
self.t_notes.pack(expand=True, fill=[Link])
self.grid_columnconfigure(0, weight=2)
self.grid_columnconfigure(2, weight=1)
self.grid_rowconfigure(2, weight=1)
self.b_frame = [Link](self)
self.b_frame.grid(row=3, column=0, columnspan=3, **[Link])
self.b_ok = [Link](self.b_frame, text="Ok", command=[Link])
self.b_ok.pack(side=[Link])
self.b_cancel = [Link](self.b_frame, text="Cancel",
command=[Link])
self.b_cancel.pack(side=[Link])
def close(self):
[Link]()
def ok(self):
try:
self.validate_url()
except ValueError as e:
def validate_url(self):
url = [Link]()
if not url:
raise ValueError("URL cannot be empty")
if not ([Link]("[Link] or [Link]("[Link]
raise ValueError("URL must start with http:// or [Link]
def validate_date(self):
day, month, year = [Link]().split("-")
valid_date = date(int(year), int(month), int(day))
return valid_date
Exercise 13-06
Modify Program 13.13 to load and save data from a database. Modify the SiteManager
class or create another one that allows you to switch the JSON storage for a database
like SQLite.
import [Link]
import sqlite3
import tkinter as tk
import [Link] as ttk
from [Link] import askquestion, showinfo, showerror
class Date([Link]):
def __init__(self, parent, min_year=00, max_year=40):
super().__init__(parent)
self.min_year = min_year
self.max_year = max_year
[Link] = [Link]()
[Link] = [Link]()
[Link] = [Link]()
self.create_controls()
def get(self):
return f"{[Link]()}-{[Link]()}-{[Link]()}"
def create_controls(self):
self.c_day = [Link](
self,
textvariable=[Link],
width=3,
values=[f"{d:02d}" for d in range(1, 32)],
state="readonly",
)
self.c_day.pack(side=[Link])
self.c_month = [Link](
self,
textvariable=[Link],
values=[f"{m:02d}" for m in range(1, 13)],
width=3,
state="readonly",
)
self.c_month.pack(side=[Link])
self.c_year = [Link](
self,
textvariable=[Link],
values=[f"{m:02d}" for m in range(self.min_year, self.max_year
+ 1)],
width=6,
state="readonly",
)
self.c_year.pack(side=[Link])
class Site:
def __init__(self, /, url=None, category=None, date=None, id=None,
notes=None):
if id is None:
id = str(uuid4())
[Link] = id
if date is None:
date = [Link]().strftime("%d-%m-%y")
[Link] = date
[Link] = url
[Link] = category
[Link] = notes
def __str__(self):
return f"Site {[Link]} {[Link]} {[Link]} {[Link]}"
class SitesDBManager:
def __init__(self):
[Link] = "[Link]"
exists = [Link]([Link])
[Link]()
if not exists:
self.create_table()
def create_table(self):
[Link](
"CREATE TABLE sites (id TEXT PRIMARY KEY, url TEXT, category
TEXT, date TEXT, notes TEXT)"
)
[Link]()
def connect(self):
[Link] = [Link]([Link])
def disconnect(self):
[Link]()
def load(self):
[Link] = {}
q = [Link]("SELECT * FROM sites")
for site in [Link]():
new_site = Site(
id=site[0],
url=site[1],
category=site[2],
date=site[3],
notes=site[4],
)
[Link][new_site.id] = new_site
return [Link]
class Window([Link]):
MIN_X = 300
MIN_Y = 300
PADXY = 10
def create_controls(self):
self.f_url = [Link](self)
self.f_url.grid(row=0, column=0, columnspan=3, sticky=[Link],
**[Link])
self.l_url = [Link](self.f_url, text="URL")
self.l_url.pack(anchor=tk.W)
[Link] = [Link]()
self.e_url = [Link](self.f_url, textvariable=[Link])
self.e_url.pack(fill=tk.X, expand=True)
self.f_category = [Link](self)
self.f_category.grid(row=1, column=0, sticky=tk.W, **[Link])
self.l_category = [Link](self.f_category, text="Category")
self.l_category.pack(anchor=tk.W)
[Link] = [Link]()
self.e_category = [Link](self.f_category, textvariable=self.
category)
self.e_category.pack()
self.f_date = [Link](self)
self.f_date.grid(row=1, column=2, sticky=tk.E, **[Link])
self.l_date = [Link](self.f_date, text="Date")
self.l_date.pack(anchor=tk.W)
[Link] = Date(self.f_date)
[Link]()
self.f_notes = [Link](self)
self.f_notes.grid(row=2, column=0, columnspan=3, sticky=[Link],
**[Link])
self.l_notes = [Link](self.f_notes, text="Notes")
self.l_notes.pack(anchor=tk.W)
self.t_notes = [Link](self.f_notes, height=3)
self.t_notes.pack(expand=True, fill=[Link])
self.grid_columnconfigure(0, weight=2)
self.grid_columnconfigure(2, weight=1)
self.grid_rowconfigure(2, weight=1)
self.b_frame = [Link](self)
self.b_frame.grid(row=3, column=0, columnspan=3, **[Link])
self.b_ok = [Link](self.b_frame, text="Ok", command=[Link])
self.b_ok.pack(side=[Link])
self.b_cancel = [Link](self.b_frame, text="Cancel",
command=[Link])
self.b_cancel.pack(side=[Link])
def close(self):
[Link]()
def ok(self):
try:
self.validate_url()
except ValueError as e:
showerror("Error", f"Invalid URL\n{e}")
return
try:
self.validate_date()
except ValueError:
showerror("Error", "Invalid date")
return
[Link] = [Link]()
[Link] = [Link]()
[Link] = [Link]()
[Link] = self.t_notes.get("1.0", [Link])
if self.on_change:
self.on_change([Link])
[Link]()
def validate_url(self):
url = [Link]()
if not url:
raise ValueError("URL cannot be empty")
if not ([Link]("[Link] or [Link]("[Link]
raise ValueError("URL must start with http:// or [Link]
def validate_date(self):
day, month, year = [Link]().split("-")
valid_date = date(int(year), int(month), int(day))
return valid_date
class App([Link]):
MIN_X = 800
MIN_Y = 200
def create_controls(self):
[Link] = [Link](self)
# Create table
[Link] = [Link](
[Link], columns=["url", "category", "date", "notes"],
show="headings"
)
[Link]("url", text="URL")
[Link]("category", text="Category")
[Link]("category", anchor=[Link])
[Link]("date", text="Date")
[Link]("date", anchor=[Link])
[Link]("notes", text="Notes")
[Link](row=0, column=0, sticky=[Link])
[Link](selectmode="browse")
# Add scrollbar
scrollbar = [Link](
[Link], orient=[Link], command=[Link]
)
[Link](yscroll=[Link])
[Link]("<Double-Button-1>", self.open_window)
[Link](row=0, column=1, sticky=[Link])
# Create menu
[Link] = [Link](self)
self.m_sites = [Link]([Link], tearoff=0)
self.m_sites.add_command(label="Add", command=[Link])
self.m_sites.add_command(label="Delete", command=[Link])
self.m_sites.add_separator()
self.m_sites.add_command(label="Delete All", command=self.delete_
all)
[Link].add_cascade(label="Sites", menu=self.m_sites)
[Link].add_command(label="About", command=[Link])
[Link](menu=[Link])
def add(self):
self.show_site(None)
def delete(self):
if selected_id := self.get_selected():
[Link](selected_id)
[Link](selected_id)
def delete_all(self):
if (
askquestion(
title="Delete All Sites",
message="Are you sure you want to delete all sites?",
)
== "yes"
):
[Link]()
def clear(self):
for id in [Link]():
[Link](id)
[Link]()
[Link](*[Link].get_children())
def about(self):
showinfo(
title="About",
message="Introduction to Programming with Python.\nhttps://
[Link]",
)
def show_data(self):
for site in [Link]():
self.add_site_to_table(site)
def get_selected(self):
if selected_item := [Link]():
return selected_item[0]
return None
App().mainloop()