Hands-On Python With 50 Exercises
Hands-On Python With 50 Exercises
PYTHON
with 50 Exercises, 2 Projects, 2 Assignments & Final
Exam
INTERMEDIATE
Musa Arda
Hands-On Python with 50 Exercises, 2 Projects, 2 Assignments
& Final Exam: Intermediate
By Musa Arda
Italic
Indicates new URLs, email addresses, filenames, and file
extensions.
Bold
Indicates new terms and important concepts to pay attention.
Constant width
Used for string literals and programming concepts within
paragraphs.
Here, you can download the current version for your operating
system. Once you download the executable file then it is very
straightforward to install Python.
Figure 2-2: The latest version on python.org
Make sure you select the Install launcher for all users and
Add Python 3.10 to PATH checkboxes. After selecting these
checkboxes you can click Install Now button. It will initialize the
setup process. It may take several minutes to finalize depending on
your computer.
Figure 2-4: Python setup progress
When your computer finishes installation you will see the final
screen saying Setup was successful.
Figure 2-5: Final setup screen
When you open PyCharm for the first time, you will see the
default Welcome Screen. Here you have 3 options:
You can create a New Project
You can Open an existing project
You can get an existing project from a Version Control
System (VCS) such as Git.
Figure 2-11: Welcome screen in PyCharm
Python Interpreter:
Python Interpreter is the application that runs your Python
code. More precisely, it is the python.exe file which we installed in
the previous sections. We can either create a new virtual
environment or we can use the base environment with the Base
Interpreter. As stated earlier, working with isolated virtual
environments is the recommended way and that’s what we
generally do in this book. So, we will select the New environment
using Virtualenv option.
Main Menu:
On the top you see the Main Menu, where you have all the
commands related to PyCharm IDE, configuration settings and file
editing options. Here are the items in the main menu and some of
the important functionalities they have:
File : Create a new project, open an existing one, save options
and IDE settings
Edit : Copy, paste, delete functions, find and replace, line
operations
View : Viewing options for tool windows and appearance
Navigate : Backward and forward navigation, navigate in files
Code : Code completion & inspection, folding and formatting
options
Refactor : Refactoring and extracting code fragments
Run : Run and debug with configurations
Tools : Tasks and team configurations
VCS : Version Control System settings like Github and Space
Window : Layout and Tabs configuration
Help : Keymap Reference, Demos and Screencasts, Learn IDE
Features
Project Explorer:
Project Explorer is on the left side and it’s where you manage
project files, folders and virtual environments. For the current
project you can switch between different views as Project, Project
Files and Open Files.
Output Window:
On the bottom of the screen we have the Output Window. It
displays the output generated by our application and messages from
the Python Interpreter.
Here are the tabs in the Output Window:
Run: The output when you run the code. You can rerun the
code or edit run configuration
Problems: Displays the errors and problems in the code and the
entire project
Python Packages: This tab displays all the packages you have
in the current project. You can view package documentation and
add new packages in this tab.
2- The Run button in quick access menu on the top right of the
editor.
Figure 2-27: Run button in the quick access menu
When you press any of these Run buttons or use the keyboard
shortcuts, Python Interpreter compiles and executes the code. Keep
in mind that, the interpreter does compilation and execution line by
line at run time.
In our current project, My_Project , we only have one Python
file which is main.py . In general, Python projects have a starting
point which is called the main file. Here, our main file is the
main.py file which is created by PyCharm when we initialized the
project (Figure 2-13).
You can download the Project in the Github Repository of this
book. Here is the content of the main.py file:
Let’s fix this error and configure the interpreter. There are
multiple ways to access the interpreter configuration screen. The
first way is to click on the Configure Python Interpreter button
which is at the right of the error text. The second way is to click on
the file name (which is main here) at the quick access menu and
then select Edit Configurations. See the image below:
Let’s move on with File > Settings way. When you click on
the Settings button under the File menu it will open the settings
window for PyCharm. This is the main screen where we do almost
any customization in PyCharm.
System Interpreter:
If you do not want to create a new virtual environment and a
new interpreter, you can use the System Interpreter. It’s the base
Interpreter you have when you install Python. To see it, click on
the System Interpreter tab on the left.
When you click on the System Interpreter tab, you will see all
the Python Interpreters on your machine (Figure 2-38). If you click
OK, your current project will be using the Base Interpreter instead
of creating a new virtual environment and a new interpreter.
As we stated earlier we will create a new virtual environment
for this project, so click on the Virtual Environment tab again
and then click the OK button (Figure 2-37). PyCharm creates a
new virtual environment for us in the venv folder. Now you can
see that the Interpreter Error has been resolved and the new venv
folder created in the project. See the final figure below:
Figure 2-39: New virtual environment (venv folder) in the project
Debugging in PyCharm
In computer programming, Debugging is the process of
detecting and removing of existing and potential errors. These
errors are also called as ‘bugs’. The bugs in a program can cause it
to behave unexpectedly or crash. In this section we will see how to
do debugging in PyCharm.
Debugging is done via breakpoints. Breakpoints are the special
places in the code where you intentionally stop (pause) the
execution of the program. This is a temporary stop and we do this
to see what’s going on in our program.
The breakpoints are only operational in debug mode. They do
not work in normal run mode. In other words, to be able to debug
your code at breakpoints you need to run the application in debug
mode. We will see it in a minute.
To place a breakpoint in PyCharm, you simply click on the
gutter once. The gutter is the column where you see the line
numbers. In the image below we put a breakpoint on line 9:
When you run the application in debug mode, you will see a
new window in the bottom, which is the Debug screen (Figure 2-
41). There are two main tabs on the Debug window, Debugger and
Console. Under the Debugger tab, you can see two parts which are
Frames and Variables. Frames tab displays the execution order of
your program and the Variables part is where you manage your
variables. For example, we only have one variable in the print_hi
function, which is name . The value for this variable is
‘PyCharm’ .
Resume Program:
You can continue the execution with the Resume Program
button (Figure 2-42). PyCharm will continue to execute and it will
finish execution if it does not encounter another breakpoint. If it
encounters another breakpoint it will pause again and wait.
Stop:
If you want to stop debugging and exit the Debugger you can
use the Stop button (Figure 2-42). It will terminate the current
debug session and set the program to its initial state.
Now let’s see debugging in more detail. We will first stop the
current debug session. Click on the stop button in the debugger
window. Then create a new Python file (.py) in our project and
name it as debug_example.py . To create a new file, in the Project
Explorer pane, right click on the project name and select New >
File as in Figure 2-43. And name the file as debug_example.py .
Figure 2-43: Create a new Python file in project
We will define a function in this new file. Then we will call this
function with some arguments. Here is the complete code for the
debug_example.py file:
We will set two breakpoints in this file, on lines 3 and 11. See
the image below:
Figure 2-44: Breakpoints in debug_example.py file
Now the code execution waits on line 11. In Figure 2-46, you
can see that we have to variables a and b and their values. If you
click on the Resume Program button, which is the play like green
As you see in Figure 2-50, after we click the Step Over button,
PyCharm executes line 3 and it creates a new variable which is
sum_of_nums . You can see it in the Variables tab in Debugger.
The current line is line 4 now. If we click on Step Over button one
more time, PyCharm will finish the execution of the summation
function and it will return to the function call in line 11. And if you
click it one more time, PyCharm will end the running because there
is no more lines to execute.
As a final example, you can remove the breakpoint on line 3
and try the Step Into button to be able to pause inside the
summation function. Here is the image for it:
To start with let’s create a Python file in the project. The file
name will be _1_exception_vs_syntax_error.py . Make sure you
use an underscore before the number 1 as _1 . In general it is a
good practice not to start with a number for file names.
Now that we create the file, let’s see examples of Syntax Errors.
In the first example we will add an extra parenthesis at the end of
print() function.
[1]: 1 # Ex:
2 # SyntaxError
3 print('Python Parser error'))
If you run the code in cell 1, you will get a SyntaxError that
tells you the parenthesis at the end is not matched. Here is the
image for the error in the output window in PyCharm:
[2]: 1 # Ex:
2 # SyntaxError: EOL while scanning string literal
3 a = "12'
4 print(a)
[3]: 1 # Ex:
2 # IndentationError: expected an indented block
3 def myFunc():
4 # function scope
5 print('A')
Exception:
Let's assume, your code is syntactically correct. So it will start
execution. If it encounters an unexpected situation (error) during
the execution (run-time), Python Interpreter will raise an error. This
is error is called Exception.
In the first example, let’s see a very common error when you
have to deal with mathematical operations in your code. It is the
error that occurs when you try to divide a number by zero.
[4]: 1 # Ex:
2 # ZeroDivisionError: division by zero
3 a = 7 / 0
[5]: 1 # Ex:
2 # NameError: name 't' is not defined
3 print(t)
[6]: 1 # Ex:
2 a = 12
3 b = 'B'
# TypeError: unsupported operand type(s) for +: 'int'
4
and 'str'
5 print(a + b)
raise :
Appropriate Exceptions will help you to debug your code more
easily. Moreover it will let other applications, who call your code,
to understand what happened. To raise an exception in Python we
use the raise keyword.
Let’s see how we use the raise keyword with an example. We
will ask for an integer from the user. If the user does not enter an
integer, we will raise an Exception.
Before writing the complete code for this example, let’s see
what happens if we do not care for exceptions in our code:
assert :
The if statement in the raise_defined_exception function (in
cell 10) was actually an ‘assertion’ operation. That means, if the
code has not been asserted, it will not move on. In other words, the
assertion in the if statement stops execution if the user input is not
an integer.
In Python, for checking assertions we have a special keyword as
assert . In the raise_defined_exception function, we can use
assert statement instead of the if statement.
The syntax is: assert <condition_to_check>
If 'condition_to_check' is not True , then Python will raise an
exception. We mainly use assertion statement for debugging
purposes.
Let’s see how we use the assert keyword with an example. We
will do the same integer check with the help of the assert
statement.
[14]: 5.0
Now, let’s call the function with zero as the second parameter
and see what happens:
try:
....
............
......error......
........ will not be executed
..... will not be executed
....... will not be executed
except:
........
........
[16]: 1 # Ex:
2 # with no exception handling
3 def get_squares():
4 user_input = input('Enter a number: ')
5 num = int(user_input)
6 print(num**2)
As you see in cell 17, we pass some arbitrary text as ‘asdf ’ and
our program crashes. We get an exception of type ValueError and
Python Interpreter terminates the program execution. The reason is
the line 5 in the function. It is where we try to convert the user
input to an integer.
Now let’s redefine this function with a try-except block. We
will do our operations in the try block. The try block is like a
safe place to run your code. If any exception occurs we know that
the execution will jump to the except block.
In cell 18, we write almost all the code in the try block. That’s
very common in programming. The try block gives you the
opportunity to control the execution flow in case of any exceptions.
And in line 7, we have the except block. When an exception
occurs in the try block, the except block will be executed and it
will print a text of 'Not a number...'.
Let’s call this function and pass the same text of ‘asdf ’ :
As you see in cell 19, we call the function and pass a text which
is not an integer and it printed the text of 'Not a number...'. Our
program did not crash and we don’t see any exceptions in the
output. We handled a possible exception in our function.
Let’s add some functionality to the except block in the
get_squares_try function. After printing 'Not a number...' text,
let’s ask for a new input. We will do it with the help of Recursion.
Recursion is the case when the function calls itself. Our function
will call itself to ask for a new input if the user enters a text which
is not numeric.
Let’s do another example now. This time, let’s try to open a file
with Python. We will define a function which will take the path of
the file as the parameter and open it. Then it will read the content
of the file line by line and print the lines. We will define this
function with no exception handling first, then we will redefine it
properly.
We need a file in our project to be able to open it. Let’s create a
text file. Right click on the project name and then New > File. The
file name is series.txt . It will be a plain text file and contain some
TV series. Here is the content of this file:
Queen's Gambit
Fargo
Dark
True Detective
Dogs of Berlin
The Crown
Now that we a file to open and read, let’s define our function:
[22]: 1 # Ex:
2 # Open a file.
3 # no exception handling
4 def open_file(path):
5 # open()
6 file = open(path)
7
8 # loop over file line by line
9 for line in file:
10 print(line.split())
In cell 24, we write all the code in the try block. If anything
unexpected occurs here we know that the code in the except block
will be executed. And it will print a text stating no such file in this
path. Let’s call it and see:
As you see in cell 25, we handled the exception that will occur
when there is no file in the given path.
try:
.....
.....
error
.....
except Exception_Type_1:
...actions for type 1...
.....
except Exception_Type_2:
...actions for type 2...
.....
try:
......
......
......
except Ex1:
......
......
except Ex2:
.......
else:
.......
no-errors
In cell 29, you see the function definition. It opens the file in the
try block (line 4). Don’t worry you will learn file operations in
detail in a Chapter 6.
We already know that if the file doesn’t exist the open()
function will raise a FileNotFoundError exception. That’s why
we put the exception type after the except clause as: except
FileNotFoundError . And inside the except block we print a text
telling that there is no file with this path.
If the open() function can open the file successfully, then it
will assign the resulting object to the file variable. Since the try
block runs without any errors, the code execution will skip the
except block and it will come to the else block. Here in the else
block, we read the file content as file.read() and we print the
result as: print(file.read()) .
Before calling the open_file() function, let’s create a new text
file to read. Its name will be example.txt and here is the content
for this new file:
try:
<---- code ---->
except:
<---- error ---->
else:
<---- no errors ---->
try:
<---- code ---->
except:
<---- error ---->
else:
<---- no errors ---->
In cell 31, we call our function with the correct path. Since
example.txt file exists in the current directory, the open()
function in the try block runs successfully. Since there are no
errors in the try block, the else block is executed and it prints the
file content.
In cell 33, we call the function with a wrong file path and it
prints the exception description as [Errno 2] No such file or
directory: 'exampleeee.txt'.
pass :
The pass statement is a null operation. When it is executed,
nothing happens. It is useful as a placeholder when a statement is
required syntactically, but no code needs to be executed. In loops,
function definitions, class definitions, or in if statements empty
code is not allowed. Which means, in such places, you must have at
least one line of code which is not a comment line. So we use the
pass statement in these cases.
The difference between a comment and a pass statement is
that, pass is not ignored by the Python Interpreter while the
comments are.
In the previous example, let’s say we don’t want to take any
actions if the file does not exist. We simply want to pass the code
further. We can use the pass statement here:
In cell 34, line 7, we have the pass clause in the except block.
Which means, we will do nothing if the file does not exist with the
given path. We simply pass the execution to the next line which is
line 11. It is the first line after the try-except-else block.
Let’s call the function with a path that does not include a file in
it:
In cell 35, we call the function with a wrong path, which means
the except block is executed. Since we have the pass keyword in
the except block in line 7, the code execution moves to the line 11
and it prints as 'Code line after try-except-else' .
finally
The try statement has another optional clause which is called
finally . In Python, the finally keyword is intended to define
clean-up actions that must be executed under all circumstances.
An example clean-up action might be to close an open file or to
release a resource before finalizing the try block.
If a finally clause is present in the try block, it will execute as
the last task before the try statement completes. The finally
clause runs whether or not the try statement produces an
exception.
Here is the complete syntax for try-except-else-finally
structure:
try:
......
......
......
except Ex1:
error
......
......
except Ex2:
error
.......
else:
.......
no-errors
finally:
whether an error or not
.......
We will create a new Python file for this section. The file name
is _5_finally.py .
Now that we know the complete structure of a try block in
Python, let’s do some examples. In the first example we will revisit
the division function we defined earlier. It takes two parameters,
x and y , and it prints the result of the division operation.
[36]: 1 # Ex:
2 def division(x, y):
3 try:
4 result = x / y
5 except ZeroDivisionError as e:
6 print(e)
7 else:
8 print('Result:', result)
9 finally:
10 print(‘Try block is finished...')
As you see in cell 37, we call the function with zero as the
divisor. It causes a ZeroDivisionError in the try block and the
except block executes. The except block prints the text of
“ division by zero” which is the standard description for
ZeroDivisionError in Python. But we see one more line in the
output which is “ Try block is finished...”. This text is printed in
the finally block in line 10.
Now, let’s call our function with valid numbers and see what
happens:
[39]: 1 # Ex:
2 # close the file in finally
3 def open_file(path):
4 try:
5 file = open(path, encoding='utf-8')
6 except Exception as ext:
7 print(ext)
8 else:
9 print(file.read())
10 finally:
11 try:
12 file.close()
13 print('Closing the file.')
14 except:
15 pass
try:
<---- code ---->
except:
<---- error ---->
else:
<---- no errors ---->
finally:
<---- in any case ---->
Let’s call the function and see the output. First let’s call with a
valid path:
try:
<---- code ---->
except:
<---- error ---->
else:
<---- no errors ---->
finally:
<---- in any case ---->
Closing the file.
As you see in cell 40, our function opens the file and prints its
content successfully. And then it closes the file. Let’s call it with a
wrong file path now:
As you see in cell 41, our function simply printed the error text
and finalized execution. This is the text we print in the except
statement in line 7.
Now that you learned Exception Handling in Python, it’s time
to have quiz on it. You will have a quiz of 10 questions, and you
are expected to solve the question on your own. The solutions are
provided right after the questions.
QUIZ - Exception Handling
Now it’s time to solve the quiz for this chapter. You can
download the quiz file, QUIZ_Exception_Handling.zip, from the
Github Repository of this book. It is in the folder named
3_Exception_Handling. You should put the quiz file in the
Exception_Handling project we build in this chapter.
Q1:
Hints:
assert
pass
do not use try-except
Expected Output:
# call with a Tuple
is_list(('a', 'b', 'c'))
Output: AssertionError: The parameter type is not List.
[1]: 1 # Q 1:
2
3 # ---- your solution here ---
4
5 # call the function you defined
6 # call with a Tuple
7 is_list(('a', 'b', 'c'))
8
9 # call with a List
10 # is_list(['a', 'b', 'c'])
Q2:
Hints:
try-except
except .... as ....
Expected Output:
print(sum_of_list([1, 'b', 3]))
Output: TypeError: unsupported operand type(s) for +=: 'int' and
'str'
print(sum_of_list([1, 2, 3]))
Output: 6
[2]: 1 # Q 2:
2
3 # ---- your solution here ---
4
5 # call the function you defined
6 print(sum_of_list([1, 'b', 3]))
7 # print(sum_of_list([1, 2, 3]))
Q3:
Hints:
try-except-else
except .... as ....
Expected Output:
print(sum_of_list_else([1, 'b', 3]))
Output: TypeError: unsupported operand type(s) for +=: 'int' and
'str'
print(sum_of_list_else([1, 2, 3]))
Output: 6
[3]: 1 # Q 3:
2
3 # ---- your solution here ---
4
5 # call the function you defined
6 print(sum_of_list_else([1, 'b', 3]))
7 # print(sum_of_list_else([1, 2, 3]))
Q4:
Hints:
try-except-else
assert
pass
try-except-else inside for loop
Expected Output:
sum_of_numbers_in_list([1, 'a', 'b', 3])
Output:
item a is not a number
item b is not a number
4
print(sum_of_numbers_in_list([1, 2, 3]))
Output: 6
[4]: 1 # Q 4:
2
3 # ---- your solution here ---
4
5 # call the function you defined
6 print(sum_of_numbers_in_list([1, 'a', 'b', 3]))
7 # print(sum_of_numbers_in_list([1, 2, 3]))
Q5:
Below, you see a function named total_likes.
This function creates a dictionary of reviews.
And it returns total number of likes in this dictionary.
But there is a problem.
If you run the function as it is currently, you will see it raises
an exception.
Fix this bug in the function by using try-except-else structure.
Hints:
examine each review carefully :)
def total_likes():
reviews = [{ 'Image': 4, 'Like': 20, 'Comment': 12},
{'Like': 15, 'Comment': 8, 'Share': 10},
{'Image': 7, 'Comment': 16, 'Share': 37},
{'Image': 6, 'Like': 10, 'Comment': 9}]
total = 0
return total
Expected Output:
total_number_of_likes = total_likes()
print(total_number_of_likes)
Output: 45
[5]: 1 # Q 5:
2
3 # ---- your solution here ---
4
5 # call the function you defined
6 total_number_of_likes = total_likes()
7 print(total_number_of_likes)
[5]: 45
Q6:
Expected Output:
result = calculator()
print(result)
Output:
Please enter an operation: 4 / 0
AssertionError: Divisor cannot be zero!
[6]: 1 # Q 6:
2
3 # ---- your solution here ---
4
5 # call the function you defined
6 result = calculator()
7 print(result)
Q7:
Expected Output:
give_me_a_key()
Output:
Please give me a key: b
B
[7]: 1 # Q 7:
2
3 # ---- your solution here ---
4
5 # call the function you defined
6 give_me_a_key()
Q8:
Hints:
try-except-else
raise only (no exception type declaration)
Expected Output:
my_list = ['x', 'y', 'z', 't']
ind = 7
result = item_at_index(my_list, ind)
print(result)
Output: IndexError: list index out of range
[8]: 1 # Q 8:
2
3 # ---- your solution here ---
4
5 # call the function you defined
6 my_list = [ 'x', 'y', 'z', 't']
7 ind = 7
8 result = item_at_index(my_list, ind)
9 print(result)
[8]: IndexError: list index out of range
Q9:
Hints:
try-except-else-finally
you need to check for the file in finally block
Expected Output:
file_reader('serieeees.txt')
Output: FileNotFoundError: [Errno 2] No such file or directory:
'serieeees.txt'
[9]: 1 # Q 9:
2
3 # ---- your solution here ---
4
5 # call the function you defined
6 # file which doesn't exist
7 path = "serieeees.txt"
8 file_reader(path)
9
10 # file which exists
11 # path = "series.txt"
12 # file_reader(path)
FileNotFoundError: [Errno 2] No such file or
[9]:
directory: 'serieeees.txt'
Q10:
series = {
'Game of Thrones': 8,
'Fargo': 4,
'Dark': 3,
'True Detective': 3,
'Dogs of Berlin': 1,
'The Crown': 6
}
Hints:
while (infinite loop)
try-except-else-finally
Expected Output:
series_and_seasons()
Output:
Please enter a series name: Queens Gambit
No such series in the dictionary. Please try again:
Please enter a series name: Squid Game
No such series in the dictionary. Please try again:
Please enter a series name: Dark
number of seasons in Dark is: 3
Congratulations. You win :)
[10]: 1 # S 10:
2
3 # ---- your solution here ---
4
5 # call the function you defined
6 series_and_seasons()
S1:
[1]: 1 # S 1:
2 def is_list(a_list):
3 # assertion
assert type(a_list) == list, AssertionError("The
4
parameter type is not List.")
5
6 # since we passed assertion
7 print(a_list)
8 pass
9
10 # call the function you defined
11 # call with a Tuple
12 is_list(('a', 'b', 'c'))
13
14 # call with a List
15 # is_list(['a', 'b', 'c'])
S2:
[2]: 1 # S 2:
2 def sum_of_list(a_list):
3 try:
4 total = 0
5 for i in a_list:
6 total += i
7 return total
8 except TypeError as type_err:
9 raise type_err
10
11 # call the function you defined
12 print(sum_of_list([1, 'b', 3]))
13 # print(sum_of_list([1, 2, 3]))
S3:
[3]: 1 # S 3:
2 def sum_of_list_else(a_list):
3 try:
4 total = 0
5 for i in a_list:
6 total += i
7 except TypeError as type_err:
8 raise type_err
9 else:
10 return total
11
12 # call the function you defined
13 print(sum_of_list_else([1, 'b', 3]))
14 # print(sum_of_list_else([1, 2, 3]))
S4:
[4]: 1 # S 4:
2 def sum_of_numbers_in_list(a_list):
3 total = 0
4
5 for i in a_list:
6 try:
7 # is this element int -> assert
8 assert int(i)
9 except:
10 print("item {0} is not a number.".format(i))
11 pass
12 else:
13 total += i
14
15 return total
16
17 # Important Note:
# this time, since we will check every element one
18 by one, we will place try-except-else inside the for
loop.
19
20 # call the function you defined
21 print(sum_of_numbers_in_list([1, 'a', 'b', 3]))
22 # print(sum_of_numbers_in_list([1, 2, 3]))
[5]: 1 # S 5:
2 def total_likes():
reviews = [{ 'Image': 4, 'Like': 20, 'Comment':
3
12},
4 {'Like': 15, 'Comment': 8, 'Share': 10},
5 {'Image': 7, 'Comment': 16, 'Share': 37},
6 {'Image': 6, 'Like': 10, 'Comment': 9}]
7
8 total = 0
9
10 for review in reviews:
11 try:
12 like = review[ 'Like']
13 except:
14 # moves to the next element in the loop
15 continue
16 else:
17 total += like
18
19 return total
20
21 # call the function you defined
22 total_number_of_likes = total_likes()
23 print(total_number_of_likes)
[5]: 45
S6:
[6]: 1 # S 6:
2 def calculator():
3
4 user_input = input('Please enter an operation: ')
5
6 # first let's split user_input
7 elements = user_input .split()
8
9 # check length of elements
10 if len(elements) != 3:
raise Exception('Please enter two operands and
11
an operation type, separated by space.')
12
13 # check for operation type
14 operations = ( '+', '-', '*', '/')
15 operation_symbol = elements[ 1]
16 if not operation_symbol in operations:
raise Exception('{0} type, is not valid. It must
17
be in {1}'.format(operation_symbol, operations))
18
19 # operands check
20 try:
21 num_1 = float(elements[0])
22 num_2 = float(elements[2])
23 except:
24 raise Exception('Operands must be numeric!')
25 else:
26 # so far all is OK -> operands are numeric
27 if operation_symbol == '+':
28 result = num_1 + num_2
29 if operation_symbol == '-':
30 result = num_1 - num_2
31 if operation_symbol == '*':
32 result = num_1 * num_2
33 if operation_symbol == '/':
34 assert num_2 != 0, 'Divisor cannot be zero!'
35 result = num_1 / num_2
36
return "{0} {1} {2} = {3}".format(num_1,
37
operation_symbol, num_2, result)
38
39 # call the function you defined
40 result = calculator()
41 print(result)
S7:
[7]: 1 # S 7:
2 def give_me_a_key():
3
4 char = input('Please give me a key: ')
5 result = ''
6
7 # check if int
8 try:
9 int(char)
10 except:
11 # it is not int
12 # check for letter -> isalpha()
13 try:
14 assert char .isalpha()
15 result = char .upper()
16 except:
17 # it is not a letter
18 result = char
19 else:
20 # it is int
21 result = int(char)**2
22 finally:
23 print(result)
24
25 # call the function you defined
26 give_me_a_key()
S8:
[8]: 1 # S 8:
2 def item_at_index(a_list, index):
3 try:
4 item = a_list[index]
5 except IndexError as ie:
6 raise
7 else:
8 return item
9
10 # call the function you defined
11 my_list = [ 'x', 'y', 'z', 't']
12 ind = 7
13 result = item_at_index(my_list, ind)
14 print(result)
S9:
[9]: 1 # S 9:
2 def file_reader(path):
3 try:
4 file = open(path)
5 except FileNotFoundError:
6 raise
7 else:
8 print(file.read())
9 finally:
10 try:
11 file.close()
12 except:
13 pass
14
15 # call the function you defined
16 # file which doesn't exist
17 path = "serieeees.txt"
18 file_reader(path)
19
20 # file which exists
21 # path = "series.txt"
22 # file_reader(path)
S10:
[10]: 1 # S 10:
2 def series_and_seasons():
3 series = {
4 'Game of Thrones': 8,
5 'Fargo': 4,
6 'Dark': 3,
7 'True Detective': 3,
8 'Dogs of Berlin': 1,
9 'The Crown': 6
10 }
11
12 while True:
13 try:
14 name = input('Please enter a series name: ')
print('number of seasons in {0} is:
15
{1}'.format(name, series[name]))
16 except:
print('No such series in the dictionary.
17
Please try again: ')
18 else:
19 break
20
21 print('Congratulations. You win :)')
22
23 # call the function you defined
24 series_and_seasons()
math :
[1]: 3.141592653589793
random :
The random module implements pseudo-random number
generators for various distributions. This module helps us to get a
random probability between 0 and 1, a random integer in an
interval, or a sample from a list.
[2]: 0.8495443771116593
[3]: 12
[5]: [5, 1, 9]
platform :
The platform module is used to access to the underlying
platform’s identifying data, such as architecture, machine,
processor, system, version, etc. Let’s import this module and print
it to see the basic data about it.
[6]: 1 import platform
2 print(platform)
os :
This module provides a portable way of using operating system
dependent functionality.
[11]: 1 import os
2
3 # current folder of our project
4 print(os.getcwd())
[11]: ...\4_Modules_and_Packages\Modules_and_Packages
[12]: <your_user_name>
sys :
This module provides access to some variables used or
maintained by the interpreter and to functions that interact strongly
with the interpreter. It is always available.
One of the most important variables in the sys module is the
sys.path variable. This variable is a list of strings that specifies the
search path for modules. In other words, Python Interpreter uses
the sys.path variable to find the modules. Let’s print all the paths
in sys.path list with a for loop:
You see the list of paths in the sys.path variable in the output
of cell 13.
[14]: 0.47178217055015603
[16]: 8
[18]: 4.0
In cell 18, we import the sqrt() function from the math
module and we name it as sq . Now, we can use the sq function to
take the square root of numbers. In line 3, we print the square root
of 16 as sq(16) and the result is 4.0 .
Wildcard Imports:
We can import all of the module content with the wildcard
character which is star (*). The syntax is: from module import *.
When you use the wildcard character (*) for import, Python
imports all the content of that module. But this type of import may
cause name clashes which are conflicts between names defined
locally and the ones imported. So you should try to avoid wildcard
imports.
Let’s see an example of how wildcard imports can be
dangerous. Let’s say, we import the random module with
wildcard. Then we define a custom function named randint .
[20]: 1 """
2 this module prints:
3 'Hello Python Module' statement.
4 """
5
6 def greeting():
7 print('Hello Python Module')
In cell 20, you see the code in the first_module.py file. At the
top, we have some basic information about the module, in form of
a multi-line comment. And in line 6, we have a function definition.
The function name is greeting and it prints the text of 'Hello
Python Module'. Now, we will call this function from another file.
We will create a new Python file for this section. The file name
is _2_define_custom_modules.py . In this new file we will import
our first_module . Then we will call the greeting function in that
module. Let’s do it:
[23]: 1 """
2 module: input_operations
3 This module is for input operations.
4 """
5
6 def get_input(type='text'):
return input('Please enter a/an {0}:
7
'.format(type))
8
9 def get_integer():
10 while True:
11 try:
12 user_input = get_input( type='integer')
13 num = int(user_input)
14 except:
15 continue
16 else:
17 return num
18
19 def get_float():
20 while True:
21 try:
22 user_input = get_input( type='float')
23 flt = float(user_input)
24 except:
25 continue
26 else:
27 return flt
[28]: ...\4_Modules_and_Packages\Modules_and_Packages
...\AppData\Local\Programs\Python\Python39\python39.zip
...\AppData\Local\Programs\Python\Python39\DLLs
...\AppData\Local\Programs\Python\Python39\lib
…\AppData\Local\Programs\Python\Python39
...\4_Modules_and_Packages\Modules_and_Packages\venv
...\4_Modules_and_Packages\Modules_and_Packages\venv\lib\sit
packages
In cell 28, we import the sys module and get the search paths in
line 5 as: python_search_path = sys.path. Then in line 8, we
print the paths with the help of a for loop. And you see the list of
all directories where Python search for modules and packages.
Directory Access:
To be able to import a module in a directory in the same
project, we use the syntax of: <dir_name>.<module_name> .
Let’s create a folder in the current project. The folder name will
be modules . To create a directory (folder) in PyCharm, right click
on the project name and then select New > Directory. Type the
name as modules and press Enter.
Figure 4-4: Create a directory (folder) in PyCharm
[30]: 1 """
2 Project level access:
3 Modules_and_Packages\venv\lib\site-packages
4 """
5
6 import local_module_input_operations
7
user_input =
8
local_module_input_operations.get_input()
9 print(user_input)
[31]: 1 """
2 System level access (global access):
3 ...\Python\Python39\lib
4 """
5
6 import global_input_operations
7
8 print(global_input_operations.get_integer())
[32]: 1 """
2 module: mod_1
3 """
4
5 def print_mod_1():
6 print('Module 1')
[33]: 1 """
2 module: mod_2
3 """
4
5 def print_mod_2():
6 print('Module 2')
Figure 4-9: The pack package and the modules in it
Now that we have a custom package, let’s create the Python file
for this section. The file name is _4_packages.py and it will use
the modules in the pack package.
To access a module under a package, we first type the package
name then the module name. Here is the syntax:
<package_name>.<module_name> .
[34]: Module 1
Module 2
In cell 34, we import the mod_1 and mod_2 modules in the
pack package as: import pack.mod_1, pack.mod_2. Then we
call the functions in these modules. Be careful that we import only
the individual modules here, not the pack package as a whole.
__init__.py :
The __init__.py files are required to make Python treat
directories containing this file as packages. This prevents
directories with a common name, such as string, unintentionally
hiding valid modules that occur later on the module search path. In
the simplest case, __init__.py can just be an empty file, but it can
also execute initialization code for the package.
In general __init__.py files contain:
sub-packages and imports for modules
global variables
documentation
Now let’s modify the __init__.py file in the pack package. It
will import the modules which are in the current package. Here is
the file content:
[35]: 1 """
2 __init__.py
3 The pack package has two modules:
4 * mod_1
5 * mod_2
6 """
7
8 # absolute import
9 # wrong way
10 # import mod_1
11 # import mod_2
12
13 # relative import
14 # correct way
15 from . import mod_1
16 from . import mod_2
[36]: Module 1
Module 2
Installing Packages
In this section we will learn how to install third party packages
into our Python environment. There are many different ways to
install packages. The most common way is to use pip. Pip is a
package itself and serves as the package manager for Python
packages and modules. It is installed by default when you install
Python (starting with Python 3.4) or when you start a new virtual
environment. Here is the documentation about how to use pip.[6]
PyPI: The Python Package Index (PyPI) is the central repository
of software for the Python programming language. PyPI helps you
find and install software developed and shared by the Python
community.
However we will not use pip in this book. Instead, we will use
PyCharm to install packages for us. Since we develop our projects
in PyCharm, it is safer to let PyCharm to handle package
installation including all the dependencies etc.
Let say we want to install a package for PDF operations. The
package name is PyPDF2 and it’s very common for PDF related
tasks in Python. Actually we will need this package later on in this
book. Let’s install this package to our project with PyCharm.
In PyCharm, under the main menu select File and then
Settings. It will open up the Settings window. Here, under Project,
click on Python Interpreter. See the image below:
Figure 4-10: Packages in the Settings window
In the Settings window, you see all the external packages which
are installed in the current virtual environment. Remember that,
every project may have a separate virtual environment with
different packages and package versions. You see the pip package
as the first one, which is installed by default.
Now we want to install a new package. To do this, click on the
plus ( + ) icon on the package list. It will open the Package
Installer window. Here you have to type the package name, which
is PyPDF2 in our case. PyCharm will search and find the package
with its official documentation.
Figure 4-11: Package Installer window
Q1:
Hints:
randint()
[1]: 1 # Q 1:
2
3 #---- your solution here ----
4
5 # call the function you defined
6 random_printer()
[1]: 136
Q2:
Hints:
randint()
sample()
[2]: 1 # Q 2:
2
3 #---- your solution here ----
4
5 # call the function you defined
6 random_list()
[2]: [160, 119, 172, 110, 167, 132, 152, 127, 135, 200]
[160, 127, 172, 200]
Q3:
Hints:
getcwd()
[3]: 1 # Q 3:
2
3 #---- your solution here ----
Q4:
Hints:
* platform
[4]: 1 # Q 4:
2
3 #---- your solution here ----
Q5:
[5]: 1 # Q 5:
2
3 #---- your solution here ----
Q6:
Hints:
your module should implement its own exception handling
Expected Output:
'Pyton Programming Language.... /@*-'
Consonants: {'r', 'P', 'L', 'y', 'm', 'n', 't', 'g'}
[6]: 1 # Q 6:
2 #-- create the consonants module first --
3
4 # import the module you create
5 import consonants
6
7 text = 'Pyton Programming Language A.... /@*-'
8 cons = consonants .get_consonants(text)
9 print(cons)
[6]: {'P', 'L', 't', 'r', 'g', 'm', 'n', 'y'}
Q7:
Create a module named vowel and make it available for all files
in this project.
In this module define a function named get_vowels.
This function takes a text as parameter and it will return a set of
vowels in this text.
Call this function with a text and print the vowels in this text.
Hints:
your module should implement its own exception handling
inspect sys.path
venv/lib/site-packages
Expected Output:
'Pyton Programming Language.... /@*-'
Vowels: {'a', 'e', 'i', 'o', 'u'}
[7]: 1 # Q 7:
2
3 #-- create the vowel module first --
4
5 import vowel
6
7 text = 'Pyton Programming Language .... /@*-'
8 vows = vowel .get_vowels(text)
9 print(vows)
Q8:
Create a Python Package named quiz_packages in the current
project folder.
Copy the modules you created in Q6 (consonants) and Q7
(vowel) in this package.
Import these modules from quiz_packages package and use their
functions (get_consonants, get_vowels)
Hints:
Python Package (__init__.py)
global variables in the __init__.py file.
Expected Output:
text = 'Pyton Programming Language.... /@*-'
get_consonants(text) -> {'n', 'm', 'P', 'L', 'r', 'g', 'y', 't'}
get_vowels(text) -> {'i', 'o', 'u', 'a', 'e'}
[8]: 1 # Q 8:
2
3 #-- create the quiz_packages first --
4
5 import quiz_packages
6
7 text = 'Pyton Programming Language.... /@*-'
cons =
8
quiz_packages.consonants.get_consonants(text)
9 vows = quiz_packages .vowels.get_vowels(text)
10 print(cons)
11 print(vows)
Q9:
Copy the quiz_packages Python Package you created in Q8.
Now make it available for all Python projects on your machine.
In other words, make it a global package.
And rename it as quiz_packages_global.
Hints:
create a global Python Package
sys.path
to see where Python is installed on your machine:
command prompt (cmd) -> where python
Expected Output:
text = 'Pyton Programming Language.... /@*-'
get_consonants(text) -> {'r', 'P', 'y', 'm', 'n', 'L', 't', 'g'}
get_vowels(text) -> {'i', 'a', 'o', 'e', 'u'}
[9]: 1 # Q 9:
2
3 #-- create the quiz_packages_global first --
4
5 import quiz_packages_global
cons =
6
quiz_packages_global.consonants.get_consonants(text)
7 vows = quiz_packages_global .vowels.get_vowels(text)
8 print(cons)
9 print(vows)
Q10:
S1:
[1]: 1 # S 1:
2 def random_printer():
3 import random
4 random_integer = random .randint(100, 200)
5 print(random_integer)
6
7 # call the function you defined
8 random_printer()
[1]: 180
S2:
[2]: 1 # S 2:
2 def random_list():
3 import random as rnd
4
5 the_list = []
6
7 for i in range(10):
8 random_integer = rnd .randint(100, 200)
9 the_list.append(random_integer)
10
11 print(the_list)
12
13 selected = rnd .sample(the_list, k=4)
14 print(selected)
15
16 # call the function you defined
17 random_list()
[2]: [160, 119, 172, 110, 167, 132, 152, 127, 135, 200]
[160, 127, 172, 200]
S3:
[3]: 1 # S 3:
2
3 import os
4
5 path = os .getcwd()
6 print(path)
S4:
[4]: 1 # S 4:
2 import platform
3
4 # OS
5 print('OS:', platform.system())
6
7 # Processor
8 print('Processor:', platform.processor())
S5:
[5]: 1 # S 5:
2 import sys
3
4 search_path = sys .path
5
6 for p in search_path:
7 print(p)
S6:
1 """
2 consonants module.
3 """
4
5 def get_consonants(text):
6
7 consonants = set()
8
9 vowels = 'aeiou'
10
11 try:
12 for letter in text:
13 if letter .lower() not in vowels and letter .isalpha():
14 consonants.add(letter)
15 except:
16 pass
17 finally:
18 return consonants
[6]: 1 # S 6:
2 import consonants
3
4 text = 'Pyton Programming Language A.... /@*-'
5 cons = consonants .get_consonants(text)
6 print(cons)
S7:
1 """
2 vowels module.
3 """
4
5 def get_vowels(text):
6
7 vowels_set = set()
8
9 vowels = 'aeiou'
10
11 try:
12 for letter in text:
13 if letter .lower() in vowels and letter .isalpha():
14 vowels_set.add(letter)
15 except:
16 pass
17 finally:
18 return vowels_set
[7]: 1 # S 7:
2
3 import vowel
4
5 text = 'Pyton Programming Language .... /@*-'
6 vows = vowel .get_vowels(text)
7 print(vows)
S8:
[8]: 1 # S 8:
2 import quiz_packages
3
4 text = 'Pyton Programming Language.... /@*-'
5 cons = quiz_packages .consonants.get_consonants(text)
6 vows = quiz_packages .vowels.get_vowels(text)
7 print(cons)
8 print(vows)
S9:
[9]: 1 # S 9:
2
3 # import sys
4 #
5 # for p in sys.path:
6 # print(p)
7 #
8 # ...\Python\Python39\Lib
9
10 import quiz_packages_global
cons =
11
quiz_packages_global.consonants.get_consonants(text)
12 vows = quiz_packages_global .vowels.get_vowels(text)
13 print(cons)
14 print(vows)
S10:
[3]: 1 # %f
2 import math
3 pi = math .pi
4 text = 'pi number in math: %f' % pi
5 print(text)
In cell 3, we first get the number pi from the math module as:
pi = math.pi. Then, we use the %f placeholder as: 'pi number
in math: %f' % pi. And the result is: pi number in math:
3.141593 .
As you see in the output of cell 3, the value of pi is 3.141593 .
Let’s say we want to display only two decimal places. This is
where we need to use %.nf placeholder.
[4]: 1 # %.nf
2 import math
3 pi = math .pi
4 text = 'pi number in math: %.2f' % pi
5 print(text)
[7]: P: ./com/pty.py
V: 1.8
A: Musa Arda
[13]: 2 * 3 = 6
[16]: 1 # Ex 2
2 num_1 = 5
3 num_2 = 8
4 result = num_1 + num_2
5
6 temp = Template( '$a + $b = $c')
7 print(temp.substitute(a=num_1, b=num_2, c=result))
[16]: 5 + 8 = 13
Hints:
use % operator
[1]: 1 # Q 1:
2
3 # define the function
4 # --- your solution here ---
5
6 # call the function you defined
7 # --- your solution here ---
8
9 # print with % operator
10 # --- your solution here ---
Q2:
[2]: 1 # Q 2:
2
3 # call the day_names function
4 # --- your solution here ---
5
6 # print with str.format()
7 # --- your solution here ---
Q3:
Hints:
use f-string
[3]: 1 # Q 3:
2
3 # call the day_names function
4 # --- your solution here ---
5
6 # print with f-string
7 # --- your solution here ---
Hints:
use Template Strings
[4]: 1 # Q 4:
2
3 # import the necessary class
4 # --- your solution here ---
5
6 # call the day_names function
7 # --- your solution here ---
8
9 # instantiate a template object with placeholders
10 # --- your solution here ---
11
12 # print with Template String
13 # --- your solution here ---
capitals = {
'USA': 'Washington',
'China': 'Beijing',
'Germany': 'Berlin',
'UK': 'London'
}
Q5:
[5]: 1 # Q 5:
2
3 # --- your solution here ---
Q6:
[6]: 1 # Q 6:
2
3 # --- your solution here ---
Q7:
[7]: 1 # Q 7:
2
3 # --- your solution here ---
Q8:
[8]: 1 # Q 8:
2
3 # --- your solution here ---
Q9:
Print the country names and capitals by using a for loop and f-
strings as follows:
'<capital> is the capital of <country>'
Expected Output:
Washington is the capital of USA
Beijing is the capital of China
Berlin is the capital of Germany
London is the capital of UK
[9]: 1 # Q 9:
2
3 # --- your solution here ---
Q10:
Expected Output:
Washington is the capital of USA
Beijing is the capital of China
Berlin is the capital of Germany
London is the capital of UK
[10]: 1 # Q 10:
2
3 # --- your solution here ---
S1:
[1]: 1 # S 1:
2
3 def day_names():
4 name = input('Please enter a day name: ')
5 return (name, len(name))
6
7 # call the function you defined
8 day, num_of_letters = day_names()
9
10 # print with % operator
11 print("%s has %d letters." % (day, num_of_letters))
S2:
[2]: 1 # S 2:
2
3 # call the day_names function
4 day, num_of_letters = day_names()
5
6 # print with str.format()
7 print('day: {0}, lenght: {1}'.format(day,
num_of_letters))
S3:
[3]: 1 # S 3:
2
3 # call the day_names function
4 day, num_of_letters = day_names()
5
6 # print with f-string
7 print(f"day: { day }, length: {num_of_letters}")
S4:
[4]: 1 # S 4:
2
3 # import the necessary class
4 from string import Template
5
6 # call the day_names function
7 day, num_of_letters = day_names()
8
9 # print with Template String
10 template = Template( 'day: $x, length: $y')
11 print(template.substitute(x=day, y=num_of_letters))
[4]: Please enter a day name: Monday
day: Monday, length: 6
capitals = {
'USA': 'Washington',
'China': 'Beijing',
'Germany': 'Berlin',
'UK': 'London'
}
S5:
[5]: 1 # S 5:
2
3 s = "The capital city of USA is %(USA)s" % capitals
4 print(s)
S6:
[6]: 1 # S 6:
2
3 country = 'Germany'
s = "The capital city of {0} is {1}".format(country,
4
capitals[country])
5 print(s)
S7:
[7]: 1 # S 7:
2
3 country = 'UK'
s = f "The capital city of {country} is
4
{capitals[country]}"
5 print(s)
S8:
[8]: 1 # S 8:
2
3 from string import Template
4
5 country = 'USA'
6 template = Template( 'The capital city of $co is $ca')
print(template.substitute(co=country,
7
ca=capitals[country]))
S9:
[9]: 1 # S 9:
2
3 for country, city in capitals .items():
4 print(f'{city} is the capital of {country}')
[10]: 1 # S 10:
2
3 [
4 print(f'{city} is the capital of {country}')
5 for country, city in capitals .items()
6 ]
[1]: Lorem
ipsum
dolor
sit
amet
. . .
. . .
Antryl
€
[2]: Lorem
ipsum
dolor
sit
amet
. . .
. . .
Antryl
€
Important Note:
You should always close the file you open. To close the file we
use built-in file.close() method on the file object.
[3]: 1 # Important
2 # You should always close the file you open.
3 file.close()
What happens if the file which you try to open does not exist?
As you may guess, you will get an exception. The exception type
will be FileNotFoundError . Let’s see an example:
[6]: Lorem
ipsum
dolor
sit
amet
. . .
. . .
Antryl
€
with:
In Python, we have context managers that facilitate the proper
handling of resources like opening and closing files. The with
keyword is a context manager which handles file opening and
closing operations automatically. When we use the with context
manager, we don’t need to worry about try-except-else-finally
blocks to ensure that the file is closed after usage even if there is an
exception.
Here is the sample syntax for the with context manager:
[7]: Lorem
ipsum
dolor
sit
amet
. . .
. . .
Antryl
€
[9]: 1 # Ex
2 # r -> read
3 # mode='r' read
4 # mode='rt' read as text
with open('words.txt', encoding='utf-8', mode='rt') as
5
file:
6 content_up_to = file.read()
7 print(content_up_to)
[9]: Lorem
ipsum
dolor
sit
amet
. . .
. . .
Antryl
€
In cell 9, we open the words.txt file in read and text mode as:
mode='rt' .
Let’s do another example. In this one, we will open a binary
file, an image file, in read mode. To do this we need to have the
image in the current folder. We already have such a file in the
project folder, which I prepared for you. The image is
python_logo.png . It is the official Python logo in png format.
[10]: 1 # Ex
2 # read a binary file
3 # mode='rb'
4 with open('python_logo.png', mode='rb') as file:
5 content = file.read()
6 print(content)
b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x02\x02\x
[10]:
…
Reading:
We will cover three most common file reading methods in this
section. Here they are:
read(size) :
Reads some quantity of data and returns it as a string (in text
mode) or bytes object (in binary mode). size is an optional
numeric argument. When size is omitted or negative, the entire
contents of the file will be read and returned.
readline() :
Reads a single line from the file. A ‘line’ means, a set of
characters which ends with a newline character ( \n ). If it is the last
line in the file, \n can be omitted.
readlines() :
Reads all the remaining lines in the file.
In cell 11, we read all of the file content at once with read()
method as: lorem.read() . Here, lorem is the file object which we
get from the open() function in the with context manager. File
mode is ‘rt’ which is ‘read – text’ and the encoding is 'utf-8' .
Now let’s read only the first 50 characters in the file. This
means, we have to specify a reading size while calling the read()
method.
In cell 12, we read only the first 50 characters in the file as:
lorem.read(50) . The value of 50 , here is the size parameter
which we pass to the read() method.
Let’s say we don’t want to read all the file content at once. We
need to read a single line instead. To do this, we need to use the
readline() method.
In cell 13, we read a single line from the lorem file, which is
the first line. And then we print it. You can think of readline() as
a cursor that reads the current line and move to the next one and
then wait. It only reads one line and wait at the beginning of the
next one.
If you call the readline() one more time in the same with
context, it will read the second line, because it already read the
first. Let’s see it:
[14]: 1 # read the file line by line -> first two lines
with open('lorem_ipsum.txt', mode='rt',
2
encoding='utf-8') as lorem:
3 # read the first line
4 line_1 = lorem .readline()
5 print(line_1)
6 # read the second line
7 line_2 = lorem .readline()
8 print(line_2)
<_io.TextIOWrapper name='lorem_ipsum.txt'
[15]:
mode='rt' encoding='utf-8'>
As you see in the output of cell 15, the class name of our lorem
object is TextIOWrapper and it is in the io module of Python.
The filename is lorem_ipsum.txt , mode is ‘rt’ and encoding is
'utf-8' . The good news here is, you can loop over the content of
TextIOWrapper object. Let’s do it:
[16]: 1 # read all the lines one by one -> for loop
with open('lorem_ipsum.txt', mode='rt',
2
encoding='utf-8') as lorem:
3 for line in lorem:
4 print(line)
In cell 16, we loop over the content of the lorem object. In the
for loop, we get each line one by one and then print it. You see
blank lines in the output. These lines are due to the new line
character which is \n . If you examine the content of the
lorem_ipsum.txt file you will see that each sentence is a separate
line. That means, there is a new line character at the end of each
line, even though we don’t see them.
Let’s remove the new line characters in the output now:
[17]: 1 # read all the lines one by one -> for loop
2 # get the sentences only
with open('lorem_ipsum.txt', mode='rt',
3
encoding='utf-8') as lorem:
4 for line in lorem:
5 # split(): splits the text from space chars
6 # space chars: ' ', \n, \t
7 line_content = line .split('\n')
8 # split() returns a list
9 sentence = line_content[ 0]
10 print(sentence)
Writing:
Like read methods we have write methods in Python. Most
commonly used write methods are as follows:
write(string) : Writes the contents of string to the file,
returning the number of characters written.
writelines() : It is used to insert multiple strings at a single
time. For a list of string elements, each string is inserted in the
text file.
To be able to write to a file in Python, we need to open it one of
the editing modes. The editing modes are:
w : Write - Opens the file in write-only mode. The pointer is
placed at the beginning of the file and this will overwrite any
existing file with the same name. So it clears all the content
and open as a blank file (DANGEROUS). It creates a new file
if one with the same name does not exist.
a : Append - Opens the file for appending new content to it
(SAFE). The pointer is placed at the end of the file. It creates a
new file if one with the same name does not exist.
As soon as you run the code in cell 18, all the content in the
lorem_ipsum.txt file will be removed and you will see a single
line as: 'New Line from Code'. Which is the text we write in line
3, inside the with context manager. That’s why, opening files in
‘w’ mode is extremely dangerous. We write to the lorem file as:
lorem.write('New Line from Code').
Now, let’s restore the lorem_ipsum.txt file content to its
original form and try to open it in append, ‘a’ , mode:
As you see, the append mode, mode='a' , is safe when you deal
with file editing. Now let’s assume that, we need to add multiple
lines at once. We will use the writelines() method for this.
Rename:
Let’s start with renaming. To rename files in Python, we use
os.rename() method. Its syntax is: os.rename(src, dst). The first
parameter, src , is the actual name of the file or directory to
rename. And the second parameter, dst , is the new name of the file
or directory.
[21]: 1 import os
2
3 # RENAME
os.rename('file_to_rename.txt',
4
'file_with_new_name.txt')
Delete:
We will cover two ways to delete a file in Python. And we will
use the os module for both methods. These methods are:
os.remove(path)
os.unlink(path)
For both methods, if Python cannot find at the specified path, it
will raise an exception of type FileNotFoundError . So keep this
in mind, when you try to delete a file. To avoid getting errors, you
might want to check if the file exists before you try to delete it.
These methods, do not return any value if they execute
successfully.
Let’s start with remove() method. Let’s delete the empty file
which we renamed as file_with_new_name.txt :
And that’s it. We delete the unlink.txt file in cell 24. As you
see, it is very easy to delete files in Python.
Create:
To be able to create a file in Python we use open() function in
create mode, which is ‘x’ . Here is the syntax: open(filename,
mode='x') . Note that, the create mode ( x ) returns an
error( FileExistsError ) if the file already exists. It does not return
any value if it creates the file successfully.
When we run the code in cell 25, Python creates a new file with
name file_to_create.txt . And then in line 5, we write the text of
'This is a new file with mode x....' in this file.
Let’s try to create a file which already exists. Since we know
we will get FileExistsError , we will do create operation in a try-
except block.
[26]: 1 # FileExistsError -> if you try to create an existing
file
2 try:
with open('file_to_create.txt', mode='x') as
3
new_file:
new_file.write('This is a new file with mode
4
x....')
5 except:
6 print('File already exist....')
In cell 26, in the try block, we try to create a file which already
exists in the current project. So we get FileExistsError error and
the except block is executed and it prints as 'File already
exist....' .
Getting Folder and File List
In this section we will see how can get the list of the contents in
a directory. Content types might be files and folders. We will list
them by using built-in Python modules.
We will create a new Python file for this section. The file name
is _4_getting_folder_and_file_list.py .
Way 1: os.listdir()
os.listdir(path) returns a list containing the names of the
entries in the directory given by path. The list is in arbitrary order.
If a file is removed from or added to the directory during the call of
this function, whether a name for that file be included is
unspecified.
Let’s get all the content in the current project:
In cell 27, we get the current project path in line 5 as: path =
os.getcwd() . Then in line 8, we call the listdir() method with this
path as: content = os.listdir(path). This method returns a list of
content and we print this content in line 9. And finally in line 10,
we set a for loop to loop over the content and print each item one
by one.
Way 2: os.scandir()
os.scandir(path) returns an iterator of o s .D i r E n t r y objects
corresponding to the entries in the directory given by path. The
entries are yielded in arbitrary order.
Let’s get the content of the project folder with scandir()
method now:
[28]: …
<DirEntry 'venv'>
<DirEntry 'words.txt'>
<DirEntry '_1_open_a_file.py'>
<DirEntry '_2_reading_and_writing.py'>
…
In cell 28, we call the scandir() method with the current
project path as: folder = os.scandir(path). Then we print the
items in the folder iterator with the help of a for loop. As you see
in the output each element is of type DirEntry .
Way 3: pathlib.Path.iterdir()
In the third way, we will use the pathlib module. There is a
class called Path in this module and we will be using a method of
that class. The method name is iterdir() .
[29]: …
...\File_Operations\venv
...\File_Operations\words.txt
...\File_Operations\_1_open_a_file.py
...\File_Operations\_2_reading_and_writing.py
…
In cell 29, we import the Path class from the pathlib module
as: from pathlib import Path. Then in line 4 we call the Path
constructor method to create an instant of the Path class. We pass
the current project path as the argument and the result is our local
path_content variable. Then in the for loop we call the iterdir()
method of this path_content variable as:
path_content.iterdir() . We name each item as inner_content
and we print it.
The good think about the iterdir() method is it return the files
and folders with their absolute paths, which might be very useful in
certain situations.
Way 1: os.listdir()
We will get all the content with listdir() method as we did
before. But this time, we filter only the files. We will use a built-in
method in the os.path module for filtering. The method name is
isfile() . The syntax is: os.path.isfile(content) . It returns True if
the content is a file, False otherwise. Let’s see:
[30]: …
python_logo.png
words.txt
_1_open_a_file.py
_2_reading_and_writing.py
…
In cell 30, we get all the content with the listdir() method as:
content = os.listdir(path). But this content includes both files
and folders in it. We have to filter only the files. Inside the for
loop, we check if the current item is a file or not as: if
os.path.isfile(c) . And we print it if it’s a file. You see a list of files
only in the output.
Way 2: os.scandir()
We will use os.scandir() again to get the content as a whole.
Then we will filter the files with is_file() method, which returns
True if the object is a file. Let’s see:
[31]: …
<DirEntry 'python_logo.png'>
<DirEntry 'words.txt'>
<DirEntry '_1_open_a_file.py'>
<DirEntry '_2_reading_and_writing.py'>
…
In cell 31, we get all the file content in line 4 as: folder =
os.scandir(path) . And in the for loop we check if the current item
is a file or not as: if f.is_file(). And we print it if it’s a file.
Way 3: pathlib.Path.iterdir()
We will use pathlib.Path.iterdir() again to get the content as
a whole. Then we will filter the files with is_file() method. It is a
method of the Path class and returns True if the object is a file.
Let’s see:
[32]: …
...\File_Operations\python_logo.png
...\File_Operations\words.txt
...\File_Operations\_1_open_a_file.py
...\File_Operations\_2_reading_and_writing.py
…
In cell 32, we import the Path class as: from pathlib import
Path . Then, we get the whole content of our project folder as:
path_content = Path(path). And we loop over the result of
path_content.iterdir() . Inside the for loop, we check if the
current item is a file or not as: if inner_content.is_file(). And we
print it if it’s a file.
Get All Folders (Folders only):
Now we are ready to get only the list of folders (directories) in a
directory. We will use the same three ways as we did in this
section.
Way 1: os.listdir()
We will use isdir() method to filter folders. The syntax is:
os.path.isdir(content) . It returns True if the content is a folder,
False otherwise. Let’s see:
[33]: …
venv
dest
example_dir_1
path_folder_1
…
In cell 33, inside the for loop, we filter the folders as:
os.path.isdir(c) . And in the output, you see the list of directories
in the project folder.
Way 2: os.scandir()
We will use is_dir() method to filter the directories. It is a
method of the Path class and returns True if the object is a
directory (folder). Let’s see:
[34]: …
<DirEntry 'venv'>
<DirEntry 'dest'>
<DirEntry 'example_dir_1'>
<DirEntry 'path_folder_1'>
…
In cell 34, inside the for loop, we filter the folders as: if
f.is_dir() . The output is the list of DirEntry objects which are of
type folder in our project.
Way 3: pathlib.Path.iterdir()
To filter the directories, we will use is_dir() method from the
Path class. It returns True if the object is a directory (folder).
Let’s see:
[35]: …
...\File_Operations\venv
...\File_Operations\dest
...\File_Operations\example_dir_1
...\File_Operations\path_folder_1
…
Single Directory:
Let’s start by creating a single directory with os and pathlib
modules. We will start by getting the main directory path which is
the path of our current project. In the first example we will use the
os module.
[38]: 1 # FileExistsError
2 try:
3 p = Path( 'path_folder_1')
4 p.mkdir()
5 except FileExistsError as file_error:
6 # print('This folder exists.')
7 print(file_error)
In cell 38, we wrap the code which creates the directory named
path_folder_1 in try block. And in the except block we catch
the exceptions of type FileExistsError . We get the standard error
description as file_error and print it, which you see in the output.
The reason for the error is that, there is already a directory with this
name in our project.
Multiple Directories:
By multiple directories, we mean nested directories. Now we
will see how we can create nested directories with a single line of
code in Python. You can also call nested directories as a folder
tree.
exist_ok=True :
We saw that, we get FileExistsError when we try to create an
existing folder. And we set a try-except block to handle this case.
But there is a much more elegant way for this operation. There is a
parameter of makedirs() method which solves this problem. The
parameter name is exist_ok , and if you pass True to it
( exist_ok=True ), Python will not raise an exception when the file
exists.
String Methods:
We will start with using string methods. For example, let’s say
we want to search for all the Python files ( .py ) in the current
project. To do this, we will use os and pathlib modules and
endswith() string method.
[43]: 1 import os
2 from pathlib import Path
3
4 # Ex: String Methods
5
6 # search path for the current project
7 search_path = '.'
8
9 for k in os .listdir(search_path):
10 print(k)
[43]: …
venv
python_logo.png
words.txt
_1_open_a_file.py
_2_reading_and_writing.py
…
[44]: _1_open_a_file.py
_2_reading_and_writing.py
_3_deleting_and_renaming.py
_4_getting_folder_and_file_list.py
…
In cell 44, we use the with context manager to get the folder at
search_path which is our project directory. Here is the code: with
Path(search_path) as path. Remember that Path(search_path)
gives us the folder or file at the search_path . We name this object
as path .
In the for loop in line 4, we get the directory content with
path.iterdir() . This gives us all the content. In line 6, we filter the
content with an if statement as: if content.is_file() and
content.name.endswith('.py') . The if statement checks two
things: First, it should be a file: content.is_file() . Second, its
name should end with .py: content.name.endswith('.py') . If
these two are both True , then this means the current content is a
file that ends with .py extension.
fnmatch Module:
There is a more elegant way in Python, if you want to filter a
directory content. It uses built-in fnmatch module which supports
wildcards search. The special characters used in shell-style
wildcards are:
Pattern Meaning
* matches everything
? matches any single character
[seq] matches any character in seq
[!seq] matches any character not in seq
Since we want all the files that end with .py extension, we need
a pattern for this. This pattern should include a * character, which
means “everything that matches”.
fnmatch.fnmatch(filename, pattern): This method is used to
test whether the filename string matches the pattern string,
returning True or False .
[45]: _1_open_a_file.py
_2_reading_and_writing.py
_3_deleting_and_renaming.py
_4_getting_folder_and_file_list.py
…
Delete Folders:
We will cover three different ways to delete folders, which are:
1. os.rmdir() : if the folder is empty
2. pathlib.Path.rmdir() : if the folder is empty
3. shutil.rmtree() : removes in any case (empty or not)
Let’s start by creating two folders to delete:
[47]: 1 # os
2 os.rmdir('folder_to_delete_1')
[49]: 1 # shutil
2 shutil.rmtree('folder_to_delete_2')
Copy Files:
We will use the shutil module to copy files either in the same
folder or from one directory to another. Here are the methods:
1. shutil.copy(src, dst)
2. shutil.copy2(src, dst)
The both methods copy the file src (source) to the file or
directory dst (destination). copy2() is identical to copy() except
that copy2() also attempts to preserve file metadata.
To do the copy operation, we will be needing two folders:
source and destination. Which we abbreviate as ‘src’ and ‘dest’
in the project. Before moving on, please make sure that you created
these folders in the project. Inside the src folder create a file as
source_file.txt , and put some arbitrary text in it as: ‘This is the
source file in src folder ’. Now let’s copy this file.
In cell 50, we copy the file from src folder to the dest folder
as: shutil.copy2(src, dest). The file name in the source folder
was source_file.txt but we rename it in the destination folder as
final_file.txt . Their contents are exactly the same because we just
copy the source file.
Copy Folders:
We will use the shutil module to copy folders. Here is the
method for this: shutil.copytree(src, dst). It copies an entire
directory tree rooted at src to a directory named dst and returns
the destination directory.
Let’s copy the src folder and rename the copy as
src_copy_with_shutil :
In cell 51, we copy the entire folder tree of the src folder with a
new name as src_copy_with_shutil . When you run this code, you
will see both folders have exactly the same content in the project
directory.
When you run the code in cell 52, Python will move the
src_copy_with_shutil folder under the example_dir_1 folder.
Reading and Creating Archive (Zip) Files
In this section we will see how we read and create archive (zip)
files with Python. We will be using the built-in zipfile module of
Python.
We will create a new Python file for this section. The file name
is _8_reading_and_creating_archive_files.py .
Let’s print all the content in our project to start with:
[53]: 1 import os
2
3 # print the project content
4 for d in os .listdir():
5 print(d)
[53]: …
neural_style_transfer.zip
…
When you run the code in cell 53, you will see that there is a zip
file in the project folder. The name of this file is
neural_style_transfer.zip . You can download it from the Github
Repository of the book. We will use this file in this section but let’s
talk a little bit about neural style transfer.
<zipfile.ZipFile filename='neural_style_transfer.zip'
[54]:
mode='r'>
In cell 54, we open the zip file in read mode. Be careful that,
we didn’t extract it yet, we just open the file for reading its content.
The code is: archive =
zipfile.ZipFile('neural_style_transfer.zip', 'r'). And you see
the resulting file object in the output.
Now that we open our zip file, let’s see what’s in it:
[55]: …
<ZipInfo
filename='neural_style_transfer/Maidens_Tower.png'
…
<ZipInfo filename='neural_style_transfer/nst.png' …
…
In cell 55, we print the archive file content. The ZipFile class
has a constant named filelist . It stores a list of all files within the
ZIP file. The data about each file is stored as a ZipInfo object.
The filelist constant holds a list of all these objects. That is what
we print in the output of cell 55.
[56]: 1 # extract it
2 archive.extractall()
Important Note:
You should always close the file you open with Python. If you
do not use the with context manager, then you have to close it
manually. That’s what we do next:
Q1:
Hints:
os
read mode ('r')
[1]: 1 # Q 1:
2 path = 'quiz_files/flower_names.txt'
3
4 # --- your solution here ---
Hints:
os
append mode ('a')
[2]: 1 # Q 2:
2
3 path = 'quiz_files/flower_names.txt'
4 new_flower = 'Z: Zinnia elegans'
5
6 # --- your solution here ---
Q3:
Hints:
os
to create file -> mode='x'
encoding
[3]: 1 # Q 3:
2
3 import os
4
5 path = 'quiz_files/file_to_delete.txt'
6
7 # --- your solution here ---
<_io.TextIOWrapper
[3]: name='quiz_files/file_to_delete.txt' mode='x'
encoding='utf-8'>
Q4:
Hints:
os
try-except
to delete -> os.remove()
[4]: 1 # Q 4:
2
3 # --- your solution here ---
Q5:
Get the list of all content (files and folders) in this project
directory.
Use this methods:
1- os.listdir()
2- os.scandir()
3- pathlib.Path.iterdir()
[5]: 1 # Q 5:
2
3 import os
4 from pathlib import Path
5
6 # 1st Way:
7 # os.listdir()
8 # --- your solution here ---
9
10 # 2nd Way
11 # os.scandir()
12 # --- your solution here ---
13
14 # 3rd Way
15 # Path
16 # . -> current folder location
17 # --- your solution here ---
[5]: .idea
dest
example_dir_1
example_dir_2
…
Q6:
[6]: 1 # Q 6:
2
3 # --- your solution here ---
Q7:
Hints:
os
os.makedirs()
pathlib.Path.mkdir()
exist_ok=True
parents
[7]: 1 # Q 7:
2
3 import os
4 from pathlib import Path
5
6 # (os.makedirs)
7 # --- your solution here ---
8
9 # (pathlib.Path.mkdir)
10 # --- your solution here ---
Q8:
Find all the files which has '*a*d*.py' in the file name.
Use Comprehensions for both getting the list and printing it.
Hints:
os
os.scandir()
os.getcwd()
fnmatch
[8]: 1 # Q 8:
2
3 import os
4 import fnmatch
5
6 # get the list
7 # --- your solution here ---
8
9 # print the list
10 # --- your solution here ---
[8]: _2_reading_and_writing.py
_3_deleting_and_renaming.py
_4_getting_folder_and_file_list.py
…
Q9:
[9]: 1 # Q 9:
2
3 import os
4 import shutil
5 import fnmatch
6
7 # create the search path
8 # --- your solution here ---
9
10 # define the pattern
11 # --- your solution here ---
12
13 # find the files with pattern and print
14 # --- your solution here ---
15
16 # remove files and folders with pattern
17 # --- your solution here ---
[9]: ...\File_Operations\quiz_files
folder 1
folder 10
Q10:
[10]: 1 # Q 10:
2
3 # --- your solution here ---
SOLUTIONS - File Operations
Here are the solutions for the quiz for Chapter 6 - File
Operations.
S1:
[1]: 1 # S 1:
2 path = 'quiz_files/flower_names.txt'
3
4 # 1st Way:
5 with open(path) as file:
6 flowers = file.read()
7 print(flowers)
8
9 # 2nd Way
10 with open(path, mode='r') as file:
11 for flower in file:
12 # \n char -> split()
13 # print(flower.split())
14 print(flower, end='')
S2:
[2]: 1 # S 2:
2
3 path = 'quiz_files/flower_names.txt'
4 new_flower = 'Z: Zinnia elegans'
5
6 with open(path, mode='a') as file:
7 # create an empty line -> \n
8 file.write('\n')
9
10 # add the new flower
11 file.write(new_flower)
S3:
[3]: 1 # S 3:
2
3 import os
4
5 path = 'quiz_files/file_to_delete.txt'
6
7 with open(path, mode='x', encoding='utf-8') as file:
8 file.write('This file will be deleted in the Quiz.')
9 print(file)
<_io.TextIOWrapper
[3]: name='quiz_files/file_to_delete.txt' mode='x'
encoding='utf-8'>
S4:
[4]: 1 # S 4:
2
3 import os
4
5 path = 'quiz_files/file_to_delete.txt'
6
7 try:
8 os.remove(path)
9 except:
10 print('File not found.')
S5:
[5]: 1 # S 5:
2
3 import os
4 from pathlib import Path
5
6 # 1st Way:
7 # os.listdir()
8 for content in os .listdir():
9 print(content)
10
11 # 2nd Way
12 # os.scandir()
13 for content in os .scandir():
14 print(content.name)
15
16 # 3rd Way
17 # Path
18 # . -> current folder location
19 for content in Path( '.').iterdir():
20 print(content)
[5]: .idea
dest
example_dir_1
example_dir_2
…
S6:
[6]: 1 # S 6:
2
3 import os
4
5 os.mkdir('quiz_files/folder 1')
6 os.mkdir('quiz_files/folder 1/sub folder 1')
7 os.mkdir('quiz_files/folder 1/sub folder 2')
8
9 os.mkdir('quiz_files/folder 2')
10
11 os.mkdir('quiz_files/folder 3')
12 os.mkdir('quiz_files/folder 3/sub folder 3')
S7:
[7]: 1 # S 7:
2
3 import os
4 from pathlib import Path
5
6 # (os.makedirs)
os.makedirs('quiz_files/folder 10/sub folder 10',
7
exist_ok=True)
os.makedirs('quiz_files/folder 10/sub folder 20',
8
exist_ok=True)
9
10 # (pathlib.Path.mkdir)
11 Path('quiz_files/folder 20').mkdir(exist_ok=True)
Path('quiz_files/folder 30/sub folder
12
30').mkdir(parents=True, exist_ok=True)
S8:
[8]: 1 # S 8:
2
3 import os
4 import fnmatch
5
6 pattern = '*a*d*.py'
7
8 # get the list
9 files = [ file.name
10 for file in os .scandir()
if file.is_file() and fnmatch .fnmatch(file.name,
11
pattern)]
12
13 # print the list
14 [print(f) for f in files]
[8]: _2_reading_and_writing.py
_3_deleting_and_renaming.py
_4_getting_folder_and_file_list.py
…
S9:
[9]: 1 # S 9:
2
3 import os
4 import shutil
5 import fnmatch
6
7 # create the search path
8 project_path = os .getcwd()
9 search_path = os .path.join(project_path, 'quiz_files')
10 print(search_path)
11
12 # define the pattern
13 pattern = '*1*'
14
15 # find the files with pattern and print
16 [print(content.name)
17 for content in os .scandir(search_path)
18 if fnmatch .fnmatch(content.name, pattern)]
19
20 # remove files and folders with pattern
21 [shutil.rmtree(content)
22 for content in os .scandir(search_path)
23 if fnmatch .fnmatch(content.name, pattern)]
[9]: ...\File_Operations\quiz_files
folder 1
folder 10
S10:
[10]: 1 # S 10:
2
3 import shutil
4
shutil.make_archive('quiz_folder_archive', 'zip',
5
'quiz_files')
Project 1 - Working with PDF & CSV Files
In this project we will learn how to work with PDF and CSV
files in Python. We will read, write, edit and copy CSV files. We
will read PDF file properties and pages, extract pages from PDF,
merge and rotate PDF files. We will install and use external Python
packages and you will have the chance to practice what you learned
so far in this book.
Reading CSV:
We will read movies.csv and movies_semicolon.csv files
with two different delimiters. You have to make sure that the
delimiter which you read the file with is the same character that is
saved in the file. Otherwise, you will not be able to read the csv file
successfully.
csv.reader() : This is the method which we will use to read csv
files. Its syntax is: csv.reader(csv_file, delimiter, dialect,
quoting) . We will see the parameters in detail later on in this
section.
Let’s define our first function to read the movies.csv file with
the comma (,) as the delimiter:
[2]: 1 # delimiter = ,
2 def csv_read():
3 with open(movie_path, 'r') as file:
4 # first get a csv reader
5 movies = csv .reader(file, delimiter=',')
6
7 # csv.reader() returns -> iterator
8 for movie in movies:
9 # each line is a list
10 print(movie)
[4]: 1 # delimiter = ;
2 def csv_read_semicolon():
3 with open(movie_path_semicolon, 'r') as file:
4 # first get a csv reader
5 movies = csv .reader(file, delimiter=';')
6
7 # csv.reader() returns -> iterator
8 for movie in movies:
9 # each line is a list
10 print(movie)
In cell 9, we comment out the first three lines and call the
csv_operations.csv_sniffer() function. In the output, we see that
our movies_semicolon.csv file has a valid header and the
delimiter is semi-colon (;).
Writing to CSV:
So far, we covered how we read data in a csv file. Now it’s time
to see how we write into csv files. Before start editing our csv files
it is a good idea to take backup of them. In our movies.csv file
there are 249 movies. Now we will add a new row to it, the 250th
row.
csv.writer() : This is the first method which we will use to write
into csv files. Its syntax is: csv.writer(csv_file, delimiter,
dialect, quoting). It returns a writer object responsible for
converting the user’s data into delimited strings on the given file
object.
writer.writerow() : This is the second method which we will
use to write into csv files. Its syntax is: writer.writerow(row) . It
writes the row parameter to the writer ’s file object, formatted
according to the current dialect. There is also a
writer.writerows(rows) method to write multiple rows at once.
<PyPDF2.pdf.PdfFileReader object at
[16]:
0x0000026F0BB91570>
Number of pages: 149
/Creator: LaTeX with hyperref package
/Title: Python Tutorial
/Author: Guido van Rossum, and the Python
development team
/Producer: XeTeX 0.99998
/CreationDate: D:20210112133708-00'00'
[18]: CONTENTS
1 Whetting Your Appetite 3
2 Using the Python Interpreter 5
2.1 Invoking the Interpreter . . . . . . . . . . . . 5
2.1.1 Argument Passing . . . . . . . . . . . . 6
2.1.2 Interactive Mode. . . . . . . . . . . .6
2.2 The Interpreter and Its Environment . . . . . . . .
. . . . 6
2.2.1 Source Code Encoding . . . . . . . . . . . . 6
3 An Informal Introduction to Python 9
…
Rotate PDF:
In the next function we will see how we can rotate PDF files
either clockwise or counter clockwise. We will be using the
PdfFileMerger from PyPDF2 package. Let’s rotate.
And this is the end of our project on CSV and PDF files.
In the next chapter, you will have an assignment related to this
project. You will try to re-build the same project by following the
guidelines I prepared for you.
Assignment 1 - Working with PDF & CSV
Files
We finished our first project in this book which is Project 1 -
Working with PDF & CSV Files. Now it’s your turn to recreate the
same project. This chapter is the assignment on the Project 1.
In this assignment you will read, write, edit and copy CSV files.
You will read PDF file properties and pages, extract pages from
PDF, merge and rotate PDF files. You will install and use external
Python packages and will have the chance to practice what you
learned so far in this book.
To complete the assignment, follow the instructions where you
see a #TODO statement. You can find the solutions in the previous
chapter.
In both files, the first row is the header row. Header row
contains headers for each column, if you think this data as a
spreadsheet. Here are some of the column headers: Id, Title, Year,
Rated, Released, Runtime, Genre, Director, Writer, Actors, Plot,
Language, Country, Awards, Poster, imdbRating, imdbVotes,
imdbID, Type etc.
Reading CSV:
We will read movies.csv and movies_semicolon.csv files
with two different delimiters. You have to make sure that the
delimiter which you read the file with is the same character that is
saved in the file. Otherwise, you will not be able to read the csv file
successfully.
csv.reader() : This is the method which we will use to read csv
files. Its syntax is: csv.reader(csv_file, delimiter, dialect,
quoting) . We will see the parameters in detail later on in this
section.
Let’s define our first function to read the movies.csv file with
the comma (,) as the delimiter:
[2]: 1 # delimiter = ,
2 def csv_read():
3 with open(movie_path, 'r') as file:
4 # first get a csv reader
5 movies = csv .reader(file, delimiter=',')
6
7 # csv.reader() returns -> iterator
8 #TODO loop over movies:
9 # each line is a list
10 print(movie)
[4]: 1 # delimiter = ;
2 def csv_read_semicolon():
3 with open(movie_path_semicolon, 'r') as file:
4 # first get a csv reader
5 movies = #TODO call csv.reader() with delimiter
as ';'
6
7 # csv.reader() returns -> iterator
8 #TODO loop over movies and print
In cell 9, we comment out the first three lines and call the
csv_operations.csv_sniffer() function. In the output, we see that
our movies_semicolon.csv file has a valid header and the
delimiter is semi-colon (;).
Writing to CSV:
So far, we covered how we read data in a csv file. Now it’s time
to see how we write into csv files. Before start editing our csv files
it is a good idea to take backup of them. In our movies.csv file
there are 249 movies. Now we will add a new row to it, the 250th
row.
csv.writer() : This is the first method which we will use to write
into csv files. Its syntax is: csv.writer(csv_file, delimiter,
dialect, quoting). It returns a writer object responsible for
converting the user’s data into delimited strings on the given file
object.
writer.writerow() : This is the second method which we will
use to write into csv files. Its syntax is: writer.writerow(row) . It
writes the row parameter to the writer ’s file object, formatted
according to the current dialect. There is also a
writer.writerows(rows) method to write multiple rows at once.
Important Note: We will open the files in append ( 'a' ) mode,
NOT IN write ( 'w' ). Remember that, we will lose all the existing
content in the file, if we open it in ‘w’ mode.
<PyPDF2.pdf.PdfFileReader object at
[16]:
0x0000026F0BB91570>
Number of pages: 149
/Creator: LaTeX with hyperref package
/Title: Python Tutorial
/Author: Guido van Rossum, and the Python
development team
/Producer: XeTeX 0.99998
/CreationDate: D:20210112133708-00'00'
Rotate PDF:
In the next function we will see how we can rotate PDF files
either clockwise or counter clockwise. We will be using the
PdfFileMerger from PyPDF2 package. Let’s rotate.
And this is the end of our assignment on CSV and PDF files.
For the solutions you can check Chapter 7 Project 1 - Working with
PDF & CSV Files.
9. OOP
Object-Oriented Programming (OOP) is a programming
paradigm based on the concept of "objects", which can contain
data and code: data in the form of fields (often known as attributes
or properties), and code, in the form of procedures (often known as
methods).
OOP uses objects and classes in programming, which aims to
implement real-world entities like inheritance, polymorphism,
encapsulation, etc. The main idea of OOP is to bind the data and
the functions that work together as a single unit. It is a widely used
programming paradigm to write powerful applications. It models
complex things as reproducible, simple structures that enables code
reusability, scalability and efficiency.
In this chapter we will learn all of the fundamental concepts of
OOP in Python. We will start with OOP basics, then we will learn
inheritance, encapsulation and polymorphism. We will move on
with multiple inheritance, method resolution order and finally
operator overloading.
We will create a new PyCharm project for this chapter. The
project name is OOP. You are recommended to create a new
project with a new virtual environment. You can also download the
project from the Github Repository of the book if you want. Here is
the project that we will develop in this chapter:
Figure 9-1: OOP project for this chapter
# main.py file
if __name__ == '__main__':
import _1_oop_basics
OOP Basics
Python is an object oriented programming language. Almost
everything in Python is an object.
In Python objects have:
Attributes (properties)
behaviors (methods)
Class:
A class is a blueprint for creating objects. It is the template for
creating objects. It tells how the objects are going to look like.
For example: A house in real word is an Object. And the
architectural drawing of that house is the Class.
To create a class in Python, use the class keyword. Then we
give a name to the class and we open the class scope with a colon
( : ). Here is an example syntax: class MyFirstClass:.
Let’s define a class for the Penguin we talked about:
Object:
An Object is an instance of the Class. Instantiate is a technical
term and it means “creating Objects from Classes”. We create
Object by calling Classes. Actually by calling class constructors,
which we will see later in this chapter.
We have a Penguin class that we defined. Now, let’s create an
object out of this class:
Class Attribute:
The attributes that all the Penguins have. Their scientific family,
for example. All the Penguins are from Spheniscidae family.
Another example for the class attribute for the Penguins might be
number of legs. All the Penguins have 2 legs regardless of their
species. Class attributes are defined at class level which means they
are not nested in any method.
Instance Attribute:
The attributes which are special to each individual Penguin:
age, color, height, weight, etc. Instance attributes are owned by the
specific instances (objects) of a class. Instance attributes are
defined in methods. So they are not class level, they are instance
level.
Method:
Methods are Functions which are defined in a Class. Methods
are the behaviors of the object. Methods can also be Instance
Methods and Class Methods.
Now that we know the basic terms about OOP, let’s do some
examples to fully understand them. We will start by defining our
Penguin class.
__init__(self, ....):
When we create an object from a class, the first method which
is called is the __init__() method. It is known as the 'constructor'
method. In other words, __init__() creates an instance of the class.
In Python, every class has an __init__() method. Either you define
it explicitly or it is defined implicitly by Python, it exists in every
class. The first parameter in the __init__() method will always be
self (just like every other method in a class). After that you can
declare any arguments you want your class to accept.
Now that we know more about the building blocks of classes
and objects let’s redefine our Penguin class and give it more
functionality.
Parent Class: the class being inherited from, also called base
class or super class.
Child Class: the class that inherits from another class, also
called derived class or sub class.
In cell 13, we define the Owl class which inherits from the
Bird class. In class definition, the parent class is written inside the
parentheses as: class Owl(Bird). Now the Bird is the parent class
and the Owl is the child class. Owl inherits all the properties and
methods from its parent. It can use them as they are or it can
override (redefine) them. It can also add new attributes or methods
which are not present in its parent.
In our example, we override __init__() , whoAmI() and
swim() methods. We do not redefine the fly() method, which
means we use it as it is. We have a new method in the Owl class
which doesn’t exist in its parent. This is the night_vision()
method. Night vision is special to the Owls, it is not common for
all the Birds. So we add this functionality to the Owl class only.
In the __init__() method of the Owl class we have a special
method call which is super() . Let’s first see its definition.
super() :
In Python, the super() method lets you to access methods in the
parent class. It returns a temporary object of the super class that
then allows you to call that super class’s methods.
In the __init__() method of the Owl class we call the
__init__() method of its parent as: super().__init__() . Here
super() simply means the Bird class.
This is a very common pattern in OOP for the __init__()
methods. You first call the __init__() method of the parent, then
you add custom functionality for your child class.
Now let’s create an Owl object and see inheritance in action:
.
Encapsulation
In some situations, we may not want anyone to access the
attributes in our class. We may want to control the access. This is
done via Encapsulation.
In OOP terminology, encapsulation is the packing of attributes
and methods within a single object. By doing so, we can hide the
internal state of the object from the outside. Encapsulation provides
information hiding.
Private Attributes: Attributes that are only accessible inside
the class. No access from the outside. To make an attribute private
we use a prefix of double underscores as: __attribute . Let’s see
an example:
In cell 18, we try to access the price of our phone object as:
phone.__price . But we get an AttributeError sayin:
'Telephone' object has no attribute '__price'. Did you mean:
'get_price'? . Why do we get this error? Because __price is
private. You cannot access private attributes from outside of the
class. The correct way is to call the get_price() method to learn
the price, which we will do it in a minute.
Let’s call the sell() method:
As you see in cell 19, we get the selling price of $1000 which
is the current value of the __price attribute.
Now you will see something interesting. Let’s set the price to
$5000. To do this, we just assign this value to the phone.__price .
Let’s do it and see what happens:
[22]: 5000
As you see in the output of cell 22, the value of phone.__price
is 5000 . But it has no effect on the __price attribute inside the
Telephone class. With this example, I wanted to show you, how it
might be tricky when you deal with private elements of a class.
You should be aware of what you are doing, and you should always
use get-set logic to manipulate private elements.
In Python, you can set attributes to the objects, independent of
their classes. For example, we can add a new attribute to our phone
object as color. Let’s do it:
[23]: Black
As you see the selling price, which means the current value of
the __price attribute is 8000 . So our get-set logic worked
perfectly.
Now that, you learned the basic logic behind encapsulation, you
should answer the question of: Why do we need Encapsulation?
We need it, to give all the control to the class. The class can do
necessary checks in the set methods and can implement or reject
any modification it wants, regardless of the outside world.
As the last example, let’s say someone wants to set a negative
value for the price of a Telephone :
[27]: 1 # Ex:
2 # If you want to set a negative price
3 # Price -> -2000
4 phone.set_price(-2000)
5 price = phone .get_price()
6 print('Current Price is:', price)
As you see in cell 27, we try to set -2000 as the price and the
set_price() method returns as ‘Price must be POSITIVE.’. And
we see the price hasn’t changed we call the get_price() method. It
is still 8000 .
Polymorphism
Polymorphism is another important concept in OOP. The word
poly-morphism means having many forms. It refers to the same
object (or function) exhibiting different forms and behaviors in
different scenarios. In simple words, it means the same function
name behaving differently for different classes.
To be able to understand polymorphism, we will use three
classes: Bird , Owl and Penguin . Bird is the parent class, Owl
and Penguin are child classes. We have already defined the Bird
class in cell 11, so we will use it as it is. We will redefine the two
child classes, Owl and Penguin . Both will inherit from the Bird
class:
In cell 28, you see the redefinition of the Owl class. In the
__init__() method it doesn’t call the super class. A class is free to
choose if it wants to call __init__() method of the super class or
not as: super().__init__() . We don’t need it in the Owl class
anymore, so we didn’t call the super() here. The __init__()
method in Owl class only prints as 'Owl is created.'.
In cell 12, we have the definition for the Car class. It starts
with the docstring. And in the docstring we state what this class is.
You can add more information if you want in the docstrings.
The Car class, has no explicit __init__() method. Which
means Python will create an implicit one for us.
There is a class attribute named brand and value of ‘BMW’ .
This means that every Car object we create will have their brand
as ‘BMW’.
There is a method in the class named as work . The method has
no parameters, excluding the self because it is the default one.
Let’s get the brand attribute on the class itself. Be careful that,
we will not create any objects. We will use the class name to get
the attribute:
[35]: BMW
[37]: Car
[40]: BMW
This car works.
[42]: 1 # Ex
2 import math
3
4 class Circle:
5
6 def __init__(self, radius):
7 self.__radius = radius
8
9 def get_radius(self):
10 return self.__radius
11
12 def set_radius(self, new_radius):
13 if new_radius > 0:
14 self.__radius = new_radius
15 else:
16 print('Radius must be positive.')
17
18 def perimeter(self):
19 return math .pi * self.__radius**2
[43]: 10
314.1592653589793
[44]: 1256.6370614359173
In cell 47, we delete the age attribute of the std object as: del
std.age . We will get an error if we want to print this attribute now.
Let’s see:
To delete the entire object we use the same syntax: del object.
Let’s delete the std object we created in cell 46.
And that’s it. The std object will be deleted after we run the
code: del std. And again, you will get an error if you try to access
it after deletion.
[53]: 1 # Ex
2 import math
3
4 # super class -> Shape
5 class Shape(object):
6 """Super class for shapes."""
7 def __init__(self, color='red'):
8 self.color = color
In cell 53, you see the definition of the Shape class. It inherits
from the object class as: class Shape(object). Actually this
explicit inheritance is not necessary, because we already know that
every class inherits from the object class. So you do not need to
write it in the class parentheses. I did it intentionally, to show you
that, object is the parent class of all classes in Python.
There is an __init__() method in the Shape class, which
defines an instance attribute named color as: self.color = color.
The default value for the color parameter is ‘red’ .
Now let’s define the child (sub) classes:
In cell 56, we have the last shape, Square , which also inherits
from the Shape class. In its __init__() method it first calls the
__init__() method of its super class then it sets an attribute as
self.side . The default values for the parameters are: side=1.0 ,
color='white' .
Now that we have our classes defined, let’s create objects from
them:
[58]: 1 # Circle
2 ci = Circle(radius =5)
3 print('Radius of Circle ci:', ci.radius)
[60]: 1 # Rectangle
2 re = Rectangle(width =2, length=8, color='yellow')
3 print('Color of Rectangle re:', re.color)
4 print(re.area())
Proof:
Let’s prove that the object class is the super class for all the
classes in Python. We created an object named sh in cell 57. It was
an instance of the Shape class. Now let’s use this object to check
if it is an instance of the object class:
issubclass():
There is a built-in function in Python, which checks if a class is
a sub class of another class. Let’s check if the Square class is a
sub class of the Shape class:
[62]: 1 # issubclass()
2 # is Square class is subclass of Shape class
print('Is Square class is subclass of Shape class?: ',
3
issubclass(Square, Shape))
As you see in cell 64, line 12, the SubClass inherits from two
parents as: SubClass(Main_1, Main_2). This is how we define
multiple inheritance. We just put the parent class names into the
parentheses. The names are separated by commas.
Now let’s do a real world example. First, we will define a
Clock class to keep track of the time. Then we will define a
Calendar class to simulate a physical calendar. And finally we will
define a CalendarClock which is the combination of both classes.
[65]: 1 # Ex
# Clock and Calendar -> CalendarClock will
2
inherit from both
3
4 # Clock Class
5 class Clock:
6 """Simulates the clock."""
7
8 def __init__(self, hours, minutes, seconds):
9 self.__hours = hours
10 self.__minutes = minutes
11 self.__seconds = seconds
12
13 def set_clock(self, hours, minutes, seconds):
14 self.__hours = hours
15 self.__minutes = minutes
16 self.__seconds = seconds
17
18 def time(self):
return '{0}:{1}:{2}'.format(self.__hours,
19
self.__minutes, self.__seconds)
In cell 65, you see the definition of the Clock class. It has three
attributes defined in the __init__() method which are:
self.__hours , self.__minutes , self.__seconds . All three of them
are private attributes, because they start with double underscores
( __ ). So, no one can access or modify them from outside the class.
That’s why we have a set_clock() method to be able to set new
values to them. And a time() method to display them in time
format.
Let’s create an object of type Clock , then set the time and
finally display it:
[66]: 0:0:0
10:4:28
[68]: 6:11:2019
16:1:2020
<_4_multiple_inheritance.CalendarClock object at
[70]:
0x0000019E31C2C040>
16:45:7
8:12:2020
Question:
If we have the same method in different classes in the
inheritance chain which one will be executed?
Answer:
Python will execute the nearest one, which means the one in the
nearest ancestor.
In cell 79, you see the Method Resolution Order (MRO) for the
CalendarClock class. We get it as: CalendarClock.__mro__ .
It’s a tuple so we iterate over it to print the class names. And in the
output you see the list of classes in order.
This list means that, for the CalendarClock class, Python will
look for a method:
1. First in the class itself: CalendarClock
2. Then in the first ancestor which is the parent: Clock
3. Then in the grandparent: SandWatch
2. In the first ancestor which is the parent: Calendar (number
2. here is not a mistake, it is deliberately written. Because Clock
and Calendar are at the same level.)
4. Finally in the root class: object
To make it crystal clear, please examine the diagram below
carefully, in which MRO for the CalendarClock class is depicted:
As you see in the output of the cell 86, we get a very nice
output when we print the Point class object. In line 4, you see
another way of calling the __str__() method which is with the
class name as: Point.__str__(p1) .
This finalizes our topic on Object Oriented Programming. We
have covered almost all of the fundamental concepts of OOP in
great detail with lots of examples. I hope you learned it by hearth,
because it is one of most important tools that software developers
should have in their toolkits.
QUIZ - OOP
Now it’s time to solve the quiz for this chapter. You can
download the quiz file, QUIZ_OOP.zip, from the Github
Repository of this book. It is in the folder named 9_OOP. You
should put the quiz file in the OOP project we build in this chapter.
Here are the questions for this chapter:
QUIZ - OOP:
Q1:
Hints:
* __init__
Expected Output:
James Bond
Clark Kent
[1]: 1 # Q 1:
2
3 # --- your solution here ---
4
5 # Create Student Objects
6 std_1 = Student( 'James Bond', '777')
7 std_2 = Student( 'Clark Kent', '333')
8
9 print(std_1.name)
10 print(std_2.name)
Q2:
Student data:
name: John Doe, number: 1111
courses:
* Python Hands-On
* Machine Learning
Expected Output:
student.get_courses() -> ['Python Hands-On', 'Machine
Learning']
[2]: 1 # Q 2:
2
3 # --- your solution here ---
4
5 # print(student.get_courses())
Q3:
Hints:
* to calculate distance (d):
* get the difference between x values (x_diff)
* get the difference between y values (y_diff)
* d = math.sqrt(x_diff**2 + y_diff**2)
Expected Output:
p_1 -> (1, 7)
p_2 -> (4, 3)
dist = p_1.distance(p_2)
print(dist) -> 5.0
[3]: 1 # Q 3:
2
3 # --- your solution here ---
4
5 # create two objects
6 # --- your solution here ---
7
8 # print docstring
9 # --- your solution here ---
10
11 dist = p_1 .distance(p_2)
12 print(dist)
Q4:
Expected Output:
p_1 = Point(5, 8)
p_2 = Point(9, 8)
p_3 = Point(5, 2)
p_4 = Point(9, 2)
width = rect.width
length = rect.length
area = rect.area()
width: 4.0
length: 6.0
area: 24.0
[4]: 1 # Q 4:
2
3 # --- your solution here ---
4
5 # create Point objects
6 # --- your solution here ---
7
8 # create Rectangle object
9 # --- your solution here ---
10
11 # Print width, length and area
12 # --- your solution here ---
[4]: width: 4.0
length: 6.0
area: 24.0
Q5:
Expected Output:
account = BankAccount()
account.display_balance()
account.deposit(500)
account.display_balance()
account.withdraw(200)
account.display_balance()
Balance: 0
Balance: 500
Balance: 300
[5]: 1 # Q 5:
2
3 # --- your solution here ---
4
5 # create object and call methods
6 account = BankAccount()
7 account.display_balance()
8
9 account.deposit(500)
10 account.display_balance()
11
12 account.withdraw(200)
13 account.display_balance()
[5]: Balance: 0
Balance: 500
Balance: 300
Q6:
Hints:
* super()
* __str__ -> operator overloading
* do not implement withdraw in this class.
Super class (BankAccount) is already doing this.
You should just check it here.
Expected Output:
min_account = MinimumBankAccount(100)
print(min_account)
min_account.deposit(500)
min_account.display_balance()
min_account.withdraw(200)
min_account.display_balance()
min_account.withdraw(300)
min_account.display_balance()
[6]: 1 # Q 6:
2
3 # --- your solution here ---
4
5 # create an object
6 min_account = MinimumBankAccount( 100)
7
8 # print the class
9 print(min_account)
10
11 min_account.deposit(500)
12 min_account.display_balance()
13
14 min_account.withdraw(200)
15 min_account.display_balance()
16
17 # withdraw the remaining amount
18 min_account.withdraw(300)
19 min_account.display_balance()
Q7:
class K:
def a(self):
return self.b()
def b(self):
return 'K'
class L(K):
def b(self):
return 'L'
k = K()
l = L()
print(k.b(), l.b())
print(k.a(), l.a())
Q8:
Expected Output:
album = Album('Yesterday and Today', 'The Beatles', 1966)
album.add_song('Yesterday')
album.add_song('Act Naturally')
album.add_song('What Goes On')
Yesterday
Act Naturally
What Goes On
[8]: 1 # Q 8:
2
3 # --- your solution here ---
4
5 # create an Album
6 # --- your solution here ---
7
8 # add songs
9 # --- your solution here ---
10
11 # print the songs with a for loop
12 # --- your solution here ---
[8]: Yesterday
Act Naturally
What Goes On
Q9:
Expected Output:
# Artist
beatles = Artist('The Beatles')
# Album
album_1 = Album('Yesterday and Today', 'The Beatles', 1966)
album_1.add_song('Yesterday')
album_1.add_song('Act Naturally')
beatles.add_album(album_1)
[9]: 1 # Q 9:
2
3 # --- your solution here ---
4
5 # Artist
6 beatles = Artist( 'The Beatles')
7
8 # Album
album_1 = Album( 'Yesterday and Today', 'The
9
Beatles', 1966)
10 album_1.add_song('Yesterday')
11 album_1.add_song('Act Naturally')
12 beatles.add_album(album_1)
13
14 album_2 = Album( 'Let It Be', 'The Beatles', 1970)
15 album_2.add_song('Let It Be')
16 album_2.add_song('For You Blue')
17 beatles.add_album(album_2)
18
19 print('----- albums -----')
20 # --- your solution here ---
21
22
23 print('---- songs ------')
24 # --- your solution here ---
25
26
27 print('----- songs - comprehension -----')
28 # --- your solution here ---
Q10:
Hints:
* __add__() -> this is + operator method
Expected Output:
album_1 = Album('Yesterday and Today', 'The Beatles', 1966)
album_1.add_song('Yesterday')
album_1.add_song('Act Naturally')
Yesterday
Act Naturally
Let It Be
For You Blue
[10]: 1 # Q 10:
2
3 # --- your solution here ---
4
5 # create albums and add songs
6 # --- your solution here ---
7
8 # add two albums
9 all_songs = album_1 + album_2
10
11 # print all songs in both albums
12 # --- your solution here ---
[10]: Yesterday
Act Naturally
Let It Be
For You Blue
SOLUTIONS - OOP
Here are the solutions for the quiz for Chapter 9 - OOP.
SOLUTIONS - OOP:
S1:
[1]: 1 # S 1:
2 class Student:
3
4 def __init__(self, name, number):
5 self.name = name
6 self.number = number
7
8 # Create Student Objects
9 std_1 = Student( 'James Bond', '777')
10 std_2 = Student( 'Clark Kent', '333')
11
12 print(std_1.name)
13 print(std_2.name)
S2:
[2]: 1 # S 2:
2 class Student:
3
4 def __init__(self, name, number):
5 self.name = name
6 self.number = number
7 # list of courses
8 self.courses = []
9
10 def enroll(self, course):
11 # check if already enrolled
12 if not course in self.courses:
13 self.courses.append(course)
14
15 def get_courses(self):
16 return self.courses
17
18
19 # create object
20 student = Student( 'John Doe', '1111')
21
22 student.enroll('Python Hands-On')
23 student.enroll('Machine Learning')
24
25 print(student.get_courses())
S3:
[3]: 1 # S 3:
2 import math
3
4 class Point:
5 """it represents a point in (x,y) coordinates."""
6
7 def __init__(self, x, y):
8 self.x = x
9 self.y = y
10
11 def distance(self, other):
12 x_diff = self.x - other .x
13 y_diff = self.y - other .y
14 d = math .sqrt(x_diff**2 + y_diff **2)
15 return d
16
17 # create two objects
18 p_1 = Point( 1, 7)
19 p_2 = Point( 4, 3)
20
21 # print docstring
22 print(Point.__doc__)
23
24 dist = p_1 .distance(p_2)
25 print(dist)
S4:
[4]: 1 # S 4:
2 class Rectangle:
"This class represents a rectangle on (x,y)
3
coordinate axis."
4 def __init__(self, p_1, p_2, p_3, p_4):
5 self.corner_1 = p_1
6 self.corner_2 = p_2
7 self.corner_3 = p_3
8 self.corner_4 = p_4
9 self.calculate_width()
10 self.calculate_length()
11
12 def calculate_width(self):
self.width =
13
self.corner_1.distance(self.corner_2)
14
15 def calculate_length(self):
self.length =
16
self.corner_1.distance(self.corner_3)
17
18 def area(self):
19 return self.width * self.length
20
21 # create Point objects
22 p_1 = Point( 5, 8)
23 p_2 = Point( 9, 8)
24 p_3 = Point( 5, 2)
25 p_4 = Point( 9, 2)
26
27 # create Rectangle object
28 rectangle = Rectangle(p_1, p_2, p_3, p_4)
29
30 # Print width, length and area
31 print('width:', rectangle.width)
32 print('length:', rectangle.length)
33 print('area:', rectangle.area())
S5:
[5]: 1 # S 5:
2 class BankAccount:
3
4 def __init__(self):
5 self.balance = 0
6
7 def withdraw(self, amount):
8 self.balance -= amount
9 return self.balance
10
11 def deposit(self, amount):
12 self.balance += amount
13 return self.balance
14
15 def display_balance(self):
16 print('Balance:', self.balance)
17
18 # create object and call methods
19 account = BankAccount()
20 account.display_balance()
21
22 account.deposit(500)
23 account.display_balance()
24
25 account.withdraw(200)
26 account.display_balance()
[5]: Balance: 0
Balance: 500
Balance: 300
S6:
[6]: 1 # S 6:
2 class MinimumBankAccount(BankAccount):
3 def __init__(self, min_balance):
4 super().__init__()
5 self.min_balance = min_balance
6
7 # override withdraw
8 def withdraw(self, amount):
9 # check when withdraw
10 if self.balance - amount < self.min_balance:
print('Sorry, you can not withdraw this
11
amount.')
12 else:
13 BankAccount.withdraw(self, amount)
14
15 # __str__() -> overload -> print()
16 def __str__(self):
17 return 'This is MinimumBankAccount class.'
18
19 # create an object
20 min_account = MinimumBankAccount( 100)
21
22 # print the class
23 print(min_account)
24
25 min_account.deposit(500)
26 min_account.display_balance()
27
28 min_account.withdraw(200)
29 min_account.display_balance()
30
31 # withdraw the remaining amount
32 min_account.withdraw(300)
33 min_account.display_balance()
S7:
[7]: 1 # S 7:
2 class K:
3 def a(self):
4 return self.b()
5
6 def b(self):
7 return 'K'
8
9 class L(K):
10 def b(self):
11 return 'L'
12
13
14 k = K()
15 l = L()
16
17 print(k.b(), l.b())
18 # K, L
19
20 print(k.a(), l.a())
21 # K, L
[7]: K L
K L
S8:
[8]: 1 # S 8:
2 class Song:
def __init__(self, name, artist, album,
3
song_number):
4 self.name = name
5 self.artist = artist
6 self.album = album
7 self.song_number = song_number
8
9 def __str__(self):
10 return self.name
11
12 class Album:
13 def __init__(self, name, artist, year):
14 self.name = name
15 self.artist = artist
16 self.year = year
17 self.songs = []
18
19 def add_song(self, song_name):
20 # length of songs
21 song_number = len(self.songs)
22
23 # create Song object
song = Song(song_name, self.artist, self,
24
song_number)
25
26 # append this song into songs
27 self.songs.append(song)
28
29 def __str__(self):
30 return self.name
31
32 # create an Album
album = Album( 'Yesterday and Today', 'The Beatles',
33
1966)
34
35 # add songs
36 album.add_song('Yesterday')
37 album.add_song('Act Naturally')
38 album.add_song('What Goes On')
39
40 # print the songs with a for loop
41 for song in album .songs:
42 print(song)
[8]: Yesterday
Act Naturally
What Goes On
S9:
[9]: 1 # S 9:
2 class Artist:
3 def __init__(self, name):
4 self.name = name
5 self.albums = []
6
7 def add_album(self, album):
8 if not album in self.albums:
9 self.albums.append(album)
10
11 # Artist
12 beatles = Artist( 'The Beatles')
13
14 # Album
album_1 = Album( 'Yesterday and Today', 'The
15
Beatles', 1966)
16 album_1.add_song('Yesterday')
17 album_1.add_song('Act Naturally')
18 beatles.add_album(album_1)
19
20 album_2 = Album( 'Let It Be', 'The Beatles', 1970)
21 album_2.add_song('Let It Be')
22 album_2.add_song('For You Blue')
23 beatles.add_album(album_2)
24
25 print('----- albums -----')
26 for album in beatles .albums:
27 print(album)
28
29 print('---- songs ------')
30 for album in beatles .albums:
31 for song in album .songs:
32 print(song)
33
34 print('----- songs - comprehension -----')
35 [print(song)
36 for album in beatles .albums
37 for song in album .songs]
S10:
[10]: 1 # S 10:
2 class Album(object):
3 def __init__(self, name, artist, year):
4 self.name = name
5 self.artist = artist
6 self.year = year
7 self.songs = []
8
9 def add_song(self, song_name):
10 # length of songs
11 song_number = len(self.songs)
12
13 # create Song object
song = Song(song_name, self.artist, self,
14
song_number)
15
16 # append this song into songs
17 self.songs.append(song)
18
19 def __str__(self):
20 return self.name
21
22 # overload -> + operator -> __add__() method
23 def __add__(self, other):
24 all_songs = self.songs + other .songs
25 return all_songs
26
27 # create albums and add songs
album_1 = Album( 'Yesterday and Today', 'The
28
Beatles', 1966)
29 album_1.add_song('Yesterday')
30 album_1.add_song('Act Naturally')
31
32 album_2 = Album( 'Let It Be', 'The Beatles', 1970)
33 album_2.add_song('Let It Be')
34 album_2.add_song('For You Blue')
35
36 # add two albums
37 all_songs = album_1 + album_2
38
39 # print all songs in both albums
40 for song in all_songs:
41 print(song)
[10]: Yesterday
Act Naturally
Let It Be
For You Blue
10. Project 2 - Movie Library with Tkinter
In this project we will create a GUI (Graphical User Interface)
application with Python. This is our last project and we will create
a Movie Library which replicates the IMDB Top 250 Movies list.
We will use almost all the concepts we covered so far and this will
give you the chance to practice what you learned in this book. Our
main focus will be on Object Oriented Programming and classes as
you may expect.
The Mockups
This will be a big project with lots of modules in it, and at the
end you will have a fully working app, which you can continue to
develop. Here are the mockups of our project:
Figure 10-1: Home Page
In Figure 10-1 you see the Home Page. This is the initial page
for our Movie Library. On the left you see the menu and on the
right there is the content area. In the menu, we have four pages as
Home, Movie List, Movie Detail and About. We will develop the
first three pages in this project. You can continue with more pages
if you want.
Figure 10-2: Movie List
In Figure 10-2 you see the Movie List page. This page contains
the list of IMDB Top 250 movies. It is paginated and we have 10
movies on each page. We have the small image on the first column
of each row. This images are provided in the project directory. The
column headers are Row Number (#), Title, Year, Rating and # of
Ratings. You can click on any row to see the details of that movie.
It will navigate you to the Movie Detail page. The color of the
selected button on the left menu becomes orange when we
navigate.
Figure 10-3: Movie Detail
On the Movie Detail Page, we have all the data about the
selected movie: Id, Title, Year, Runtime, Genre, Director, Actors,
Language, Country, Awards, imdbRating, imdbVotes and imdbID.
On the top of the page we have the Title and the big image of the
movie. Big images are provided in the project directory.
Here are the folders and packages which we will create in this
project:
1. data : it will contain data files like colors.py, geometry.py,
imdb_top_250.csv and menus.py
2. images : it will contain folders like home, posters_large and
posters_small for movie images
3. mockup : it will contain the mockup images of the project
4. page : it is a package that contains the page folders as home,
movie_detail and movie_list
5. tkinter_basics : it contains basic widgets to learn Tkinter
6. widget : it is a package that contains the widget folders as
button, left_frame, right_frame and window
If you see an empty Tkinter window like the one in Figure 10-
5, then you are all set. Your Tkinter application is up and running.
We will fill it with widgets soon.
Now we are ready to create some widgets and see them on our
Tkinter window. Let’s start with a Button .
Button
Inside the tkinter_basics folder create a new Python file as
button.py . We will create a Tkinter Button in this file. Here is the
complete code of the file:
If you see the image in Figure 10-6, then you’re button code
works correct. If you click on the button it will close the window,
because we pass window.destroy for the command parameter.
Checkbutton
Inside the tkinter_basics folder create a new Python file as
checkButton.py . We will create a Tkinter Checkbutton in this
file. Here is the complete code of the file:
In cell 4, you see the code in the label_entry.py file. This time
we import the classes with named imports as: from tkinter
import Label, Entry. Now we can directly use the class names to
create widgets.
In lines 16 and 17 we create two Label widgets. We gave the
names of lblName and lblLastName to them. And here is the
code for the first one: lblName = Label(master=window,
text='Name') .
In lines 20 and 21 we instantiate two Entry widgets as
entName and entLastName . The only parameter we use is the
master , which we pass the window as: entName =
Entry(window) .
In lines 24 and 25 we place the first Label and the first Entry .
Since we want them to be on the same horizontal line, we set their
row numbers equal ( row=0 ). Here is the code for the Label:
lblName.grid(row=0, column=0). And the code the Entry widget:
entName.grid(row=0, column=1). Be careful that, the column
number for the entName is 1 .
In lines 27 and 28 we place the second Label and the second
Entry on the same horizontal line ( row=1 ). Here is the code for
the Label widget: lblLastName.grid(row=1) . We skip the
column parameter for lblLastName because the default value is
0 . And the code the Entry widget: entLastName.grid(row=1,
column=1) . Be careful that, the column number for the
entLastName is 1 .
And the last line is the mainloop as: window.mainloop() .
Now we can run this file to see the labels and entries. In
PyCharm, right click anywhere on the code editor and select Run
‘label_entry’ . You should a see a window like the one below:
Frame
Frame is a widget that is used for grouping and organizing
other widgets. It works like a container, which is responsible for
arranging and positioning its content. It is a rectangular region on
the screen that organizes the layout and provides padding to other
widgets.
Inside the tkinter_basics folder create a new Python file as
frame.py . We will create a Tkinter Frame widget in this file.
Here is the complete code of the file:
In cell 5, you see the code in the frame.py file. We import the
classes with named imports as: from tkinter import Frame,
Button .
In line 15, we create a Frame object as: frame =
Frame(window) . As you see we only pass the first parameter
which is the master .
In line 18, we create a button as btnLeft . Here is the code:
btnLeft = Button(master=frame, text='Frame Button Left',
bg='black', fg='white'). Please be careful that, this time the
master of the button is not window. The master is our frame ,
because we want the button to be in the frame: master=frame .
And in line 19, we pack this button as:
btnLeft.pack(side=tk.LEFT) . The side parameter decides where
to put the button in its parent container. And we pass
side=tk.LEFT . Which means it will be placed on the left.
In lines 21 and 22, we instantiate another button as btnRight
in the same way we created the previous one. The only difference
here is the placement. We pack this one to the right as:
btnRight.pack(side=tk.RIGHT) .
In line 25, we pack the frame as : frame.pack(). Since its
master is the window , Python will put it inside the window
object.
And the last line is the mainloop as: window.mainloop() .
Now we can run this file to see the two buttons in a frame. In
PyCharm, right click anywhere on the code editor and select Run
‘frame’ . You should a see a window like the one below:
Text
The Text widget is used where we need to insert multi-line text
fields. It allows us to display and edit multi-line text area and
format it, such as changing its color and font.
Inside the tkinter_basics folder create a new Python file as
text.py . We will create a Tkinter Text widget in this file. Here is
the complete code of the file:
In cell 6, you see the code in the text.py file. We import the
Text class with named imports as: from tkinter import Text.
In line 15, we create a Text widget as: txt = Text(window,
height=5, width=40). For the first parameter, which is master ,
we pass our main window object. Then we pass the height and
width for the text area.
In line 18, we pack our txt object as: txt.pack() .
And the last line is the mainloop as: window.mainloop() .
Now we can run this file to see a multi-line text on the screen.
In PyCharm, right click anywhere on the code editor and select
Run ‘text’. You should a see a window like the one below:
menus.py :
This file contains a dictionary that stores the menu names and
texts. Here is the content of it:
colors.py :
The last Python file in the data folder is colors.py . In which
we define a simple class called COLORS . This class will store the
constants for the colors we will use throughout the project. Here is
its content:
main.py
In most of the Python projects, the entry point is main.py file.
This is where you start the project, create necessary objects and call
related functions to run the project. We will stick to this convention
here, so main.py file is the entry point of our Movie Library.
In our main.py file we will set the structure what you see in
Figure 10-11. We will create the Main Window first, then the
Left Frame and finally the Right Frame. Here is the code:
In cell 10, you see the initial form of our main.py file. It has
no functionality yet. It only has some placeholder comments for the
time being. But soon we will fill it with our classes.
It starts by importing the classes for the main window and
frames. As you see in the structure in Figure 10-11, there are three
parts in the main.py file which are:
1. Root Window which is the main window and the class for it is:
Window
2. Left Frame is left side of the screen and the class is:
LeftFrame
3. Right Frame is right hand side and the class is: RightFrame
widget Package:
We will create a Python package for our widgets. We want all
of our widgets to be located in the same directory, which will make
things easier when anyone needs to use them. Each widget will be
in a separate folder. And in each folder, there will be a class for
that widget. Here are the folders for widgets: button , left_frame ,
right_frame and window .
To create the widget package, in PyCharm right click on the
project name and select New > Python Package. Name the
package as widget and hit Enter. Now you have a package named
widget that has the __init__.py file in it. Remember that, this
__init__.py file is the entry point for packages. We will use it in a
minute.
page Package:
This will be the package for the pages in our project. Each page
will be in a separate folder, in which we will have a class for that
page. The folders for pages are: home , movie_detail , and
movie_list .
To create the page package, in PyCharm right click on the
project name and select New > Python Package. Name the
package as page and hit Enter. Now you have a package named
page that has the __init__.py file in it.
Please see the image below for the packages and folders in
them:
Now that we have our packages created, let’s define the related
classes. We will start by the definition of the Window class.
Window
The Window class will work as a wrapper for the main
window and related functionalities. It will be located in a folder
named window in the widget package (see Figure 10-12). So you
have to create the window folder first, then the class in it. The file
name for the class Window.py which is the same as the class
name it contains. So the full path for the class is:
widget/window/Window.py . Here is the code for the Window
class:
In cell 12, you see the code in thee __init__.py file of widget
package. This file is the entry point of this package, so it is
responsible for doing all the imports for the classes and objects that
belong to the package.
In line 9, we import the Window class as: from
.window.Window import Window. Please be careful that the file
path starts with a dot (.) and this is not a mistake. The dot (.)
means the current package. So here is how we read the statement
below:
.window.Window : in the current package we have a folder
called window and in that folder we have a file called Window .
The general syntax for importing classes in the __init__.py
files is: from .[file_path] import Class. We will use this syntax
very frequently in our project. Now we are ready to use the
Window class in any file in the project.
Before defining new classes, let’s see if our app runs at this
state. Since we already have our Window class that has the main
Tkinter window in it, let’s call it in the main.py file:
If you don’t see the screen in Figure 10-13, then most probably
there are some errors somewhere in your code. You should debug
each file one by one to find and fix the bugs. If you see the screen
the same as here, then you are good to go. We can move on.
In cell 14, you see the initial state of the LeftFrame class. In
its __init__() method, it takes two parameters as window and
name .
In line 12, we create a Tkinter Frame object and name it as
self.frame . Here is the code for it:
self.frame = tk.Frame(master=window, name=name,
bg=COLORS.GRAY)
This self.frame object is going to be the Frame in this class.
Its master is the window parameter, which is going to be the main
Tkinter window. Its name comes from the name parameter and its
background color is COLORS.GRAY . We will change this color
later on.
We also need a master attribute in the current class, that’s why
in line 17, we have self.master = window. This is needed to be
able to store the main window object throughout the class. Its
name is self.master from now on in this class.
In line 18, we call the self.add_frame() method.
In line 19, we call the self.add_button() method.
You see the definition of the add_frame() method in line 21.
This method is responsible for packing the current frame
( self.frame ) in its master. Remember that, to be able to display an
object on Tkinter window we have to pack it. Here is the code for
packing: self.frame.pack(side=tk.LEFT, fill=tk.Y, pady=(62,
0)) . The side parameter is tk.LEFT , which means this frame will
be displayed on the left of the main window. We pass the value of
tk.Y (means Y axis) to the fill parameter, which means we want it
to fill the vertical axis. The last parameter is pady , which is simply
the padding in the Y axis. The value is a tuple of two numbers as
(62, 0). The first number, 62 , is the padding amount (in text units)
from the top and the second one, 0 , is the padding from the
bottom.
In line 24, we have the add_button() method. It creates a
Tkinter Button as: btn = tk.Button(master=self.frame,
text='Left Menu Button'). The master for this btn object is
self.frame , which means this button will be placed in the current
frame (left frame). And then we pack the button as: btn.pack() .
We finished the first form of the LeftFrame , which we will
revisit a lot in the next sections. But for now, let’s see how it looks
on the main window. To display it, we have to call it from the
main.py file. But to be able to call it from main.py file, we have
to import it in the __init__.py file of the widget package. Let’s
do it first:
In cell 18, you see the definition of our Button class. In the
__i n i t __() method we have parameters as: master , name , text ,
fg , bg , width , height , handle_click , padx=0 , pady=0 ,
side=tk.TOP . Most of them are self-explanatory so no need to
repeat them. But handle_click is new. We will use it as the event
handler when the user clicks on our Button . Since we want this
Button class to be a generic class, we cannot define a static click
handler. That’s why it is parametric here.
In line 13, in the __i n i t __() method, we create a button
attribute as self.button . This is the instance of the Tkinter Button
and we will be using it in our class as the button object. We pass
the parameters we get from the caller. What’s new here is
activebackground parameter. It represents the background color
when a button is active, which means when it is clicked.
In line 25, we call the self.add_button() method to configure
and pack the button object, which is self.button .
We finished the definition of our Button class. Let’s add it to
the __init__.py file of the widget package:
[19]: 1 # __init__.py file of the widget package
2
3 """
4 modules and packages -> main entry point
5 """
6
7 # import modules and packages -> . notation
8 # from .[file_path] import Class
9 from .window.Window import Window
10 from .left_frame.LeftFrame import LeftFrame
11 from .button.Button import Button
Wrong Import:
Please examine carefully, the way we import the Button class.
We didn’t import it as: from widget import Button. See the line
8, which is commented out. This will raise a circular import
error. Because this line will import the widget package as a
whole, which contains this LeftFrame class too. That means, we
are trying to import ourselves. So it will not work.
Correct Import:
The correct way to import a class in the same package is to use
the exact import, which means typing the complete file location.
Here is the code: from widget.button.Button import Button.
We write the file location where we want to import the Button
class. And it works.
58 else:
child.configure(bg=COLORS.BLACK,
59
fg=COLORS.ORANGE)
If you can see the screen in Figure 10-17, when you run the
project, then you are ready for the next step which is the Right
Frame configuration. If you don’t see this screen or getting any
errors then it’s time for debugging for you.
Right Frame
Now it’s time to create the right frame. We will define a
RightFrame class for this purpose. It’s going to keep the pages on
the right hand side of the screen. Actually these pages will be the
content of our application. Remember that we had the MENU
dictionary (see cell 7) for the pages. Here are our pages: Home,
Movie List, Movie Detail, and About. We will create the first three
in this project and I will leave the About page to you. And the
RightFrame class will be the container for these pages.
To create the RightFrame class we will create a folder and
Python file first. In the widget package, create a directory named
right_frame . And in the right_frame folder create a Python file
named RightFrame.py . This is where we define our RightFrame
class. Here is the initial code for our right frame:
20 self.add_frame()
21
22 def add_frame(self):
23 # frame content
24 self.frame_content()
self.frame.pack(side=self.side, fill=tk.BOTH,
25
expand=True)
26
27 def frame_content(self, page_name='home'):
28 if page_name == 'home':
29 # add home page
30 pass
31 elif page_name == 'movieList':
32 # add movie list page
33 pass
34 elif page_name == 'movieDetail':
35 # add movie detail page
36 pass
Home Page
Let’s create the home page of our application. We will create
the all of the pages under the page package. For the home page,
the class name will be in the same name as Home . Let’s start by
creating a new folder named home in the page package. And in
this home directory create a new Python file as Home.py .
Remember by convention, in our project the file names and the
classes are usually the same. Here is the initial content of the
Home class:
[26]: 1 # page/home/Home.py file
2
3 import tkinter as tk
4
5 from data.colors import COLORS
6
7 class Home:
8 """It creates the Home Page."""
9
10 def __init__(self, window, bg_color,
11 relief=tk.SUNKEN, side=tk.LEFT):
12 self.frame = tk .Frame(
13 master=window,
14 name='home',
15 relief=relief,
16 bg=bg_color)
17 self.side = side
18 self.bg_color = bg_color
19 self.frame_content()
20 self.add_frame()
21
22 def add_frame(self):
self.frame.pack(side=self.side, fill=tk.BOTH,
23
expand=True)
24
25 def frame_content(self):
26 course = tk .Label(self.frame,
27 text='Python Hands-On',
28 font=('Helvetica', 18, 'bold'),
29 fg=COLORS.BLACK,
30 bg=self.bg_color)
31 course.place(x=315, y=50)
In cell 26, we have our Home class. In its __init__() method,
it creates a frame as self.frame by calling tk.Frame. The important
parameters to the init method are window which will be the master
and bg_color for background color. We have to more parameters
as relief and side which have default values. In the init method
we call two methods which are self.frame_content() and
self.add_frame() .
In line 22, we have the add_frame() method which simply
packs the self.frame . Here is the code:
self.frame.pack(side=self.side, fill=tk.BOTH, expand=True).
In line 25, you see the frame_content() method. It creates a
Tkinter Label and names it as course . It sets the text , font , fg
and bg parameters of the label. This will hold the course name on
our main page. Then in line 31, it puts this label on an exact
location on the screen by providing the x and y coordinates. Here
it is: course.place(x=315, y=50).
Now we should import the Home class in the __init__.py file
of the page package. Below is the code for that:
Pillow Package:
For rendering the Python logo, which is an image, we need to
install a third-party package that is the Pillow package. Pillow is a
not built-in package in Python. So we have to install it in the virtual
environment of our current project. We will install it in PyCharm.
If you need help on how to install packages in PyCharm please
revisit the Installing Packages section in Chapter 4 – Modules and
Packages. Where you will see the necessary steps in detail. Below
you can see an image for the package you should install. Just click
on Install Package after you find it.
33 instructor = tk .Label(self.frame,
34 text='(Musa Arda)',
35 font=('Helvetica', 18, 'bold'),
36 fg=COLORS.BLACK,
37 bg=self.bg_color)
38 instructor.place(x=344, y=105)
39
40 self.render_image()
41
42 project = tk .Label(self.frame,
text='Capstone Project - Movie
43
Library with Tkinter',
44 font=('Helvetica', 18, 'bold'),
45 fg=COLORS.WHITE,
46 bg=COLORS.BLACK)
47 project.place(x=140, y=765)
48
49 def render_image(self):
load =
50
Image.open('images/home/python_logo.png')
51 render = ImageTk .PhotoImage(load)
img_lb = tk .Label(self.frame, image=render,
52
bg=self.bg_color)
53 img_lb.image = render
54 img_lb.place(x=136, y=180)
In cell 29, you see the final form of our Home class. We import
two classes from the PIL package. PIL is a sub package inside
the Pillow package which we installed. Here is the import
statement: from PIL import Image, ImageTk.
We add new widgets in the frame_content() method.
In line 33, we add an instructor field, which stores the
instructor name and place it on the screen.
In line 40, we call the render_image() method to render the
python_logo.png image.
In line 42, we add the last field as project by passing the
project name to the tk.Label() widget.
In line 49, you see the definition of the render_image()
method. To render an image in a Tkinter application, first we need
to open the image. We use the Image class from PIL to do this in
line 50. Here is the code: load =
Image.open('images/home/python_logo.png') . We call the
opened image as load .
In line 51, we call ImageTk.PhotoImage class and pass the
load object as: render = ImageTk.PhotoImage(load). And we
name the object we instantiate as render . This will be our image
to pass to the tk.Label .
In line 52, we call tk.Label class to render a label on the
screen. Here is the code : img_lb = tk.Label(self.frame,
image=render, bg=self.bg_color).
This img_lb label will display the image. To make it work we
have to complete two steps:
First, we need to pass our render object to the image
parameter of the tk.Label class as: image=render
Second, we need to explicitly set the image attribute of our
img_lb as in line 53: img_lb.image = render
And finally we place the img_lb on the screen with setting the
x and y coordinates.
Now let’s run the project and see our Home page as displayed
in the image below:
Figure 10-21: Home page finalized
If you see the same output as in Figure 10-21, then it’s great.
Your project works as expected. If you do not see the Python logo
as a transparent image, which might occur in some cases, don’t
worry. Most probably, the reason is zipping and unzipping of the
project. So, just open the python_logo.png file in an image
processor (like Photoshop) and save it as a transparent png image.
Or you can find a transparent Python logo on the internet and use
it. Just make sure you handle the x and y values for placement if
you change any of the content in home page. Because we used
absolute units while placing our widgets.
44
45 # add new page -> page_name
RightFrame.frame_content(rightFrame,
46
page_name)
47
48 def add_menus(self):
49 # add menus in loop
50 for menu_key, menu_text in MENU .items():
51 if menu_key == 'about':
button = Button( self.frame, menu_key,
52
menu_text,
COLORS.ORANGE,
53
COLORS.BLACK,
54 18, 2,
55 handle_click=self.handle_click,
56 side=tk.BOTTOM)
57 else:
button = Button( self.frame, menu_key,
58
menu_text,
COLORS.ORANGE,
59
COLORS.BLACK,
60 18, 2,
61 handle_click=self.handle_click,
62 side=tk.TOP)
63
64 def manage_button_colors(self, event):
65 # clicked button -> event.widget
# all the menu buttons ->
66
event.widget.master.children
for child in
67
event.widget.master.winfo_children():
68 if child == event .widget:
child.configure(bg=COLORS.ORANGE,
69 fg=COLORS.WHITE)
70 else:
child.configure(bg=COLORS.BLACK,
71
fg=COLORS.ORANGE)
In cell 34, you see the modified version of the MovieList class.
In the add_frame() method in line 25, we call two new
methods which are self.read_csv() and self.create_page() .
In the read_csv() method in line 38, we define a movie path
for the csv data that we will read. Then in the with context
manager we open this file path in read mode. And we name the
returning object as file . In line 41, we use csv.DictReader class
on this file object and get the movie dictionary as: movie_dict =
csv.DictReader(file, delimiter=';'). In line 43, inside the for
loop, we append each movie to the self.movies list.
In the create_page() method in line 45, we call another
method as self.add_header_row() . This new method is
responsible for creating the header row.
In the add_header_row() method in line 48, we loop over the
MovieList.columns list, which stores the column headers.
In line 50, inside the if block, we check the column headers
one by one to set the text , width and other necessary parameters.
In line 66, we place the lbl object, which is tk.Label for the
column header, by calling the grid() method as: lbl.grid(row=1,
column=j, sticky='we', padx=(0, 10)). Here j is the column
index. The value for the row parameter is 1 because we have the
page title on row 0 . Finally, we give an extra 10 text units padding,
padx=(0, 10), on the right of the last column which is
'imdbVotes' .
Now you should see the image below when you run the project:
Figure 10-23: The page title and the column headers in the Movie List
58 lbl.configure(text='#', width=4)
59 elif column == 'Year':
60 lbl.configure(width=8)
61 elif column == 'imdbRating':
62 lbl.configure(text='Rating', width=8)
63 elif column == 'imdbVotes':
lbl.configure(text='# of Ratings',
64
width=12)
65
66 # place in a grid
67 if column == 'imdbVotes':
lbl.grid(row=1, column=j, sticky='we',
68
padx=(0, 10))
69 else:
lbl.grid(row=1, column=j, sticky='we',
70
padx=(0, 1))
71
72 def create_table(self):
73 for i, movie in enumerate(self.movies):
74 for j, key in enumerate(MovieList.columns):
name = 'table_row_' + str(i) + str(j) + '_' +
75
movie['imdbID']
76 if j == 0:
77 # render image
78 self.render_image(movie, i, j, name)
79 else:
80 # print label
81 pass
82
83 def render_image(self, movie, i, j, name):
84 try:
85 # load the image
load = Image .open('images/posters_small/' +
86 movie['imdbID'] + '.jpg')
87 except:
88 # load no image
load =
89
Image.open('images/posters_small/no_image.jpg')
90 finally:
91 render = ImageTk .PhotoImage(load)
lbl_img = tk .Label(self.frame, name=name,
92
image=render, bg=COLORS.ORANGE)
93 lbl_img.image = render
lbl_img.grid(row=i + 2, column=j, padx=(7,
94
0), sticky='we')
tt0111161.jpg
In line 76, we check if the column number ( j ) is 0 or not as: if
j == 0. This column means the first one and it is the image column.
So we will render the movie poster in this column. To do that we
call another method as render_image as:
self.render_image(movie, i, j, name). As you see, we pass the
movie dictionary, the row number i , the column number j and
unique cell name as the parameters.
In line 83, we have the definition for the render_image()
method. There are some missing posters in the posters_small and
posters_large folders, so we have to handle this case. That’s why
we have a dummy image in each folder as no_image.jpg . We will
render this image, if the movie doesn’t have a poster.
In the try block in line 84, we open the image at the specified
path with the image name as: load =
Image.open('images/posters_small/' + movie['imdbID'] +
'.jpg') . If this line runs successfully, that means this movie has a
poster and the finally block will be executed. In the finally block
we render the image as we did before. (See the render_image()
method in the Home class in cell 29, line 49.) Here we use the
grid() method to place the image at row=i + 2 and column=j . We
also add a padding on the left as padx=(7, 0).
If you run the project now, and click on the Movie List menu
button, you should see the movie images on the right frame as
below. It may take a bit long to display the images because we
don’t have any pagination yet, so it tries to load all of the images at
once. We will fix it later.
Figure 10-24: Movie images rendered
Now let’s print other columns in our movie list, which are:
Rank ( # ), Title , Year , Rating and # of Ratings. Here is the
code for it:
134
135 # place in a grid
136 if key == 'imdbVotes':
lbl.grid(row=i + 2, column=j, sticky='we',
137
padx=(0, 10))
138 else:
lbl.grid(row=i + 2, column=j, sticky='we',
139
padx=(0, 1))
140
141 def fill_bg(self, widget, i):
142 # check if the row (i) is odd or even
143 if i % 2 == 1:
144
widget.configure(bg=COLORS.LIST_ODD_LINE)
145 else:
146
widget.configure(bg=COLORS.LIST_EVEN_LINE)
147
148 def create_combo_box_select_event(self, event):
149 pass
150
151 def create_combo_box(self):
152 # each page -> 10 movies
153 # total 250 movies
154 # to upper limit 250 / 10 = 25
values = list(range(1,
155
MovieList.total_num_pages))
pages = ttk .Combobox(self.frame,
156
values=values, width=4)
157 # index of the page (1 minus page_number)
158 pages.current(MovieList.page_number - 1)
159
160 # bind select event
pages.bind('<<ComboboxSelected>>',
161
self.create_combo_box_select_event)
pages.grid(row=self.i, column=2, pady=(15,
162
0))
Pagination Variables:
In the top line of the class definition, we have new attributes on
lines 13 to 15. These are the pagination variables which are class
level (class attributes) and here are their definitions:
page_number : stores the current page number which is
displayed on the screen
movies_per_page : this constant defines how many movies we
have on the screen (10 here)
total_num_pages : keeps the total number of pages that will
be displayed
We will use these attributes to set a proper pagination system in
our movie list.
for j, column in
60 enumerate(MovieList.columns):
61 if column != 'imdbID':
lbl = tk .Label(self.frame,
62
text=str(column),
width=54, height=2,
63
bg=COLORS.BLACK,
64 fg=COLORS.WHITE,
65 font=('Arial', 10, 'bold'))
66 # configure width
67 if column == 'Id':
68 lbl.configure(text='#', width=4)
69 elif column == 'Year':
70 lbl.configure(width=8)
71 elif column == 'imdbRating':
72 lbl.configure(text='Rating', width=8)
73 elif column == 'imdbVotes':
lbl.configure(text='# of Ratings',
74
width=12)
75
76 # place in a grid
77 if column == 'imdbVotes':
lbl.grid(row=1, column=j, sticky='we',
78
padx=(0, 10))
79 else:
lbl.grid(row=1, column=j, sticky='we',
80
padx=(0, 1))
81
82 def create_table(self):
83 for i, movie in enumerate(self.movies):
84 # if i = 2
85 # 10 <= i < 20
start_page_num = (MovieList .page_number -
86 1) * MovieList .movies_per_page
end_page_num = MovieList .page_number *
87
MovieList.movies_per_page
88 if start_page_num <= i < end_page_num:
for j, key in
89
enumerate(MovieList.columns):
name = 'table_row_' + str(i) + str(j) + '_'
90
+ movie[ 'imdbID']
91 if j == 0:
92 # render image
93 self.render_image(movie, i, j, name)
94 else:
95 # print label
96 self.write_label(movie, i, j, key, name)
97
98 # keep the last row
99 self.i = i + 3
100
101 def render_image(self, movie, i, j, name):
102 try:
103 # load the image
load = Image .open('images/posters_small/' +
104
movie['imdbID'] + '.jpg')
105 except:
106 # load no image
load =
107
Image.open('images/posters_small/no_image.jpg')
108 finally:
109 render = ImageTk .PhotoImage(load)
110 lbl_img = tk .Label(self.frame, name=name,
image=render,
111
bg=COLORS.ORANGE)
112 lbl_img.image = render
113 lbl_img.grid(row=i + 2, column=j, padx=(7,
0), sticky='we')
114
115 def write_label(self, movie, i, j, key, name):
lbl = tk .Label(self.frame, name=name,
116
text=str(movie[key]),
height=4, bg=COLORS.WHITE,
117
fg=COLORS.BLACK,
font=('Arial', 10, 'bold'),
118
cursor='hand2')
119
120 # configure width
121 if key == 'Id':
122 lbl.configure(width=4)
123 elif key == 'Title':
124 lbl.configure(width=54, anchor='w')
125 elif key == 'Year':
126 lbl.configure(width=8)
127 elif key == 'imdbRating':
128 lbl.configure(width=8)
129 elif key == 'imdbVotes':
130 lbl.configure(width=12)
131
132 # zebra effect
133 self.fill_bg(lbl, i)
134
135 # place in a grid
136 if key == 'imdbVotes':
lbl.grid(row=i + 2, column=j, sticky='we',
137
padx=(0, 10))
138 else:
lbl.grid(row=i + 2, column=j, sticky='we',
139
padx=(0, 1))
140
141 def fill_bg(self, widget, i):
142 # check if the row (i) is odd or even
143 if i % 2 == 1:
144
widget.configure(bg=COLORS.LIST_ODD_LINE)
145 else:
146
widget.configure(bg=COLORS.LIST_EVEN_LINE)
147
148 def create_combo_box_select_event(self, event):
MovieList.page_number =
149
int(event.widget.get())
150 # clear the table cells
151 self.clear_table(event)
152 # recreate the table
153 self.create_table()
154
155 def create_combo_box(self):
156 # each page -> 10 movies
157 # total 250 movies
158 # to upper limit 250 / 10 = 25
values = list(range(1,
159
MovieList.total_num_pages))
pages = ttk .Combobox(self.frame,
160
values=values, width=4)
161 # index of the page (1 minus page_number)
162 pages.current(MovieList.page_number - 1)
163
164 # bind select event
pages.bind('<<ComboboxSelected>>',
165
self.create_combo_box_select_event)
pages.grid(row=self.i, column=2, pady=(15,
166
0))
167
168 def clear_table(self, event):
169 master = event .widget.master
170 # loop over the children
171 master_children_copy = master .children.copy()
172 for child in master_children_copy:
173 if 'table_row_' in child:
174 master.children[child].destroy()
In cell 39, you see the new form of our LeftFrame class. We
add a new method as selected_button_color . This method will be
responsible for modifying the Home menu button.
In the add_menus() method, in line 64, we check the
menu_key as: menu_key == 'home'. If the menu_key is
‘home’ then we call the selected_button_color() method as:
self.selected_button_color(button.button) . The parameter we
pass is button.button because the button object we create in line
58 is our custom wrapper Button class. And it has an attribute
called self.button which is the tk.Button. That’s why we pass it as
button.button . Because the selected_button_color() method
expects a tk.Button to configure it.
In line 76, you see the definition of the
selected_button_color() method. It takes a tk.Button object and
configures its bg and fg as:
button.configure(bg=COLORS.ORANGE,
fg=COLORS.WHITE) .
And that’s it, now when you app starts, the Home menu button
will display as selected by default. See the image below:
Figure 1-29: Home menu button selected when the app starts
In cell 42, we add the click functionality the movie list. Inside
the write_label() method in line 122, we bind the left click event
to the lbl object as: lbl.bind('<Button-1>', self.movie_click).
Here, '<Button-1>' means left mouse click and self.movie_click
is the event handler we pass to the bind() function.
In line 180, we have the definition of the movie_click()
method. It takes the event as the parameter and in line 182, it
extracts the imdbID from the name of the widget that sends the
event. Here is the code: imdbID = str(event.widget).split('_')
[3] . Remember that in the create_table() method we concatenated
the imdbID to the end of the widget name as: name =
'table_row_' + str(i) + str(j) + '_' + movie['imdbID']. And
here we extract the imdbID .
In line 185, we call a new method, modify_right_frame , by
passing the event and the imdbID . This method is going to
modify the RightFrame .
In line 188, we call another method, modify_left_frame , by
passing the event . This one will modify the LeftFrame .
In line 190, we have this modify_right_frame() method. We
first get the rightFrame object as: rightFrame =
event.widget.master . Then we loop over the children of the
rightFrame and destroy them. This is needed because we want to
clear the current content in the right frame. We will fill it with
MovieDetail in a minute. And in line 197, we call the
MovieDetail class and pass the parameters as:
MovieDetail(self.frame, COLORS.ORANGE,
imdbID=imdbID, movies=self.movies).
In line 200, we have the modify_left_frame() method. The
main idea for this method is to get all the menu buttons and change
their configurations. At the end, it will make the Movie Detail
button as selected.
In line 202, it gets the root window as: root =
event.widget.master.master.master . And in line 203, it loops
over the children of this root object. It finds the LeftFrame ( ch )
in line 204 as: str(child) == '.leftFrame'. Then it loops over the
children of the left frame. Finally it finds the MovieDetail child in
line 206 as: if str(ch) == '.leftFrame.movieDetail'. And it
changes the bg and fg colors of the LeftFrame as selected. It
configures the rest as unselected.
And here is the result when we run the app at this stage and
click any of the movies in the Movie List page:
lbl_img.grid(row=1, column=0,
58 columnspan=2, pady=(0, 10))
59
60 def get_movie(self):
61 for m in self.movies:
62 if m[ 'imdbID'] == self.imdbID:
63 self.movie = m
64 break
65
66 def render_keys(self):
67 for i, key in enumerate(self.movie.keys()):
68 txt = str(key)
lbl = tk .Label(self.frame, text=txt, height=2,
69
width=12, anchor='w')
70 self.fill_bg(lbl, i)
71 lbl.grid(row=i + 2, column=0, padx=(10, 1))
72
73 def fill_bg(self, widget, i):
74 # check if the row (i) is odd
75 if i % 2 == 1:
76
widget.configure(bg=COLORS.LIST_ODD_LINE)
77 else:
78
widget.configure(bg=COLORS.LIST_EVEN_LINE)
79
80 def render_values(self):
81 for i, value in enumerate(self.movie.values()):
82 txt = str(value)
lbl = tk .Label(self.frame, text=txt, height=2,
83
width=96, anchor='w')
84 self.fill_bg(lbl, i)
85 lbl.grid(row=i + 2, column=1, padx=(0, 8))
In cell 43, you see the final form of the MovieDetail class.
Let’s see what has been modified:
In the add_page_title() method in line 28, we first check if the
self.imdbID is None or not. If it’s not None , then we get the title
of the selected movie as self.movie['Title'] and pass it to the text
parameter as: text=self.movie['Title'] . We will see how set the
self.movie attribute in a minute.
self.imdbID != None:
What does it mean, when we say self.imdbID != None. This
means that this class has been called by a click event on a movie.
We know this, because the imdbID for the movie is not empty.
Remember that we got this parameter in the __init__() method.
If self.imdbID is equal to None , this means that no movies are
selected. And the MovieDetail page is called by just clicking on
the menu button (Movie Detail) on the left of the screen. In other
words, it has been rendered as an empty page from the
RightFrame . We will modify the RightFrame for this soon.
In the add_content() method in line 38, we first check if the
imdbID is not None as: if self.imdbID != None. If it’s not
None , then we can render the movie details. So, we call the
necessary methods which are: render_image() , get_movie() ,
render_keys() and render_values() .
In the render_image() method we load the big image for the
selected movie. Remember that we have the path that contains the
imdbID as the file name. But this time we the directory is
images/posters_large .
In the get_movie() method, we just filter the selected movie
from the self.movies list. The value of self.movies comes from
the movies parameter. Then in line 62, we filter the one with the
current as: if m['imdbID'] == self.imdbID. And we set this one
to the self.movie attribute. That’s how we get the selected movie
data.
In the render_keys() method in line 66, we simply render the
keys, which are the column names like; Id, Title, Year, Runtime,
Genre, Director etc. This is the first column of our details table,
column=0 . We also call the fill_bg() method in line 70, to create
a zebra effect on the keys.
In the render_values() method in line 80, we render the values
of the self.movie dictionary. Remember that the actual movie data
is stored as values in the movie object. Again we call the fill_bg()
method to create the same zebra effect on the values.
And we finished defining the MovieDetail class. As the last
thing we will modify the RightFrame class to call the
MovieDetail when the user clicks on the menu Movie Detail
button on the left. Here it is:
15 self.frame = tk .Frame(
16 master=window,
17 name=name,
18 relief=relief,
19 bg=RightFrame.bg_color
20 )
21 self.side = side
22 self.add_frame()
23
24 def add_frame(self):
25 # frame content
26 self.frame_content()
self.frame.pack(side=self.side, fill=tk.BOTH,
27
expand=True)
28
29 def frame_content(self, page_name='home'):
30 try:
31 incoming_frame = self.frame
32 except:
33 incoming_frame = self
34 finally:
35 if page_name == 'home':
36 # add home page
Home(incoming_frame,
37
RightFrame.bg_color)
38 elif page_name == 'movieList':
39 # add movie list page
MovieList(incoming_frame,
40
RightFrame.bg_color)
41 elif page_name == 'movieDetail':
42 # add movie detail page
MovieDetail(incoming_frame,
43
RightFrame.bg_color)
44
# static method -> Class Method (no self
45
parameter)
46 def destroy_children(frame):
47 # destroy the children
48 for child in frame .winfo_children():
49 child.destroy()
And this is the end of our Movie Library Project which was
mainly on Object Oriented Programming. I followed an
incremental development approach while building the project. I
tried to make every step crystal clear for you. That’s why we went
step by step. This project is very similar to a real life Python
project. I hope you enjoyed it.
In the next chapter you will have an assignment related to this
project. You will try to re-build the same project by following the
guidelines I prepared for you.
Assignment 2 - Movie Library with Tkinter
We finished our second project in this book which is Project 2 -
Movie Library. Now it’s your turn to recreate the same project.
This chapter is the assignment on the Project 2.
In this assignment you will create a GUI (Graphical User
Interface) application with Python, which is going to be a Movie
Library that replicates the IMDB Top 250 Movies list.
To complete the assignment, follow the instructions where you
see a #TODO statement. You can find the solutions in the previous
chapter.
The Mockups
This will be a big project with lots of modules in it, and at the
end you will have a fully working app, which you can continue to
develop. Here are the mockups of our project:
Figure 10-1: Home Page
In Figure 10-1 you see the Home Page. This is the initial page
for our Movie Library. On the left you see the menu and on the
right there is the content area. In the menu, we have four pages as
Home, Movie List, Movie Detail and About. We will develop the
first three pages in this project. You can continue with more pages
if you want.
Figure 10-2: Movie List
In Figure 10-2 you see the Movie List page. This page contains
the list of IMDB Top 250 movies. It is paginated and we have 10
movies on each page. We have the small image on the first column
of each row. This images are present in the project directory. The
column headers are Row Number (#), Title, Year, Rating and # of
Ratings. You can click on any row to see the details of that movie.
It will navigate you to the Movie Detail page. The color of the
selected button on the left menu becomes orange when we
navigate.
Figure 10-3: Movie Detail
On the Movie Detail Page, we have all the data about the
selected movie: Id, Title, Year, Runtime, Genre, Director, Actors,
Language, Country, Awards, imdbRating, imdbVotes and imdbID.
On the top of the page we have the Title and the big image of the
movie. Big images are provided in the project directory.
menus.py :
This file contains a dictionary that stores the menu names and
texts. Here is the content of it:
colors.py :
The last Python file in the data folder is colors.py . In which
we define a simple class called COLORS . This class will store the
constants for the colors we will use throughout the project. Here is
its content:
main.py
In most of the Python projects, the entry point is main.py file.
This is where you start the project, create necessary objects and call
related functions to run the project. We will stick to this convention
here, so main.py file is the entry point of our Movie Library.
In our main.py file we will set the structure what you see in
Figure 10-5. We will create the Main Window first, then the Left
Frame and finally the Right Frame. Complete the missing code
in this file:
Packages
We want our Movie Library project to be modular as much as
possible. This is the key for scalability. If you have a good
organization of your files and folders, then no matter the size of
your project, you can still manage it without trouble. When your
project size becomes too big, a proper organization may save your
life. Or it may be just the opposite with a bad organization.
We will define two packages in our project which are: widget
and page . Let’s create them first:
widget Package:
We will create a Python package for our widgets. We want all
of our widgets to be located in the same directory, which will make
things easier when anyone needs to use them. Each widget will be
in a separate folder. And in each folder there will be a class for that
widget. Here are the folders for widgets: button , left_frame ,
right_frame and window .
To create the widget package, in PyCharm right click on the
project name and select New > Python Package. Name the
package as widget and hit Enter. Now you have a package named
widget that has the __init__.py file in it. Remember that, this
__init__.py file is the entry point for packages. We will use it in a
minute.
page Package:
This will be the package for the pages in our project. Each page
will be in a separate folder, in which we will have a class for that
page. The folders for pages are: home , movie_detail , and
movie_list .
To create the page package, in PyCharm right click on the
project name and select New > Python Package. Name the
package as page and hit Enter. Now you have a package named
page that has the __init__.py file in it.
Please see the image below for the packages and folders in
them:
Now that we have our packages created, let’s define the related
classes. We will start by the definition of the Window class.
Window
The Window class will work as a wrapper for the main
window and related functionalities. It will be located in a folder
named window in the widget package (see Figure 10-6). So you
have to create the window folder first, then the class in it. The file
name for the class Window.py which is the same as the class
name it contains. So the full path for the class is:
widget/window/Window.py . Complete the code of the Window
class:
Left Frame
Let’s start to build our left frame. It is going to be the container
for the left menu buttons.
To create the LeftFrame class we will create a folder and
Python file first. In the widget package, create a directory named
left_frame . And in the left_frame folder create a Python file
named LeftFrame.py . This is where we define our LeftFrame
class. Complete the code of the LeftFrame class:
Button Class
Under the widget package create a new folder called button .
In this folder create a new Python file as Button.py . We will
define our Button class in this file. Be careful that, the file name
and the class name are the same, as we did before.
This class will be a wrapper class for the Tkinter Button . Why
do we need a wrapper class? Because, we can add new
functionalities to existing ones that comes from Tkinter and that
will become our own implementation in our project. The wrapper
will give the complete control over the Button objects that will be
created in the project. Complete the code of the Button class:
Home Page
Let’s create the home page of our application. We will create
the all of the pages under the page package. For the home page,
the class name will be in the same name as Home . Let’s start by
creating a new folder named home in the page package. And in
this home directory create a new Python file as Home.py .
Remember by convention, in our project the file names and the
classes are usually the same.
Complete the missing code in the Home class:
Pillow Package:
For rendering the Python logo, which is an image, we need to
install a third-party package that is the Pillow package. Pillow is a
not built-in package in Python. So we have to install it in the virtual
environment of our current project. We will install it in PyCharm.
If you need help on how to install packages in PyCharm please
revisit the Installing Packages section in Chapter 4 – Modules and
Packages. Where you will see the necessary steps in detail. Below
you can see an image for the package you should install. Just click
on Install Package after you find it.
Movie List
In this sub-section we will create the MovieList class which
will be our movie list page. This is the second page in our
application and has a table like structure in it. The data for the
movies comes from the imdb_top_250.csv file which is in the
data folder. This data was acquired by doing web scraping with
Python on the IMDB Top 250 Movies list page.
Let’s start by creating a new folder named movie_list in the
page package. And in this movie_list directory create a new
Python file as MovieList.py . The file name and the class name are
again the same.
Complete the missing code lines in the MovieList class:
76 if j == 0:
77 # render image
78 # TODO - render the image
79 else:
80 # print label
81 # TODO - write the labels
82
83 self.i = i + 3
84
85 def render_image(self, movie, i, j, name):
86 try:
87 # load the image
load = Image .open('images/posters_small/' +
88
movie['imdbID'] + '.jpg')
89 except:
90 # load no image
load =
91
Image.open('images/posters_small/no_image.jpg')
92 finally:
93 render = ImageTk .PhotoImage(load)
lbl_img = tk .Label(self.frame, name=name,
94
image=render, bg=COLORS.ORANGE)
95 lbl_img.image = render
lbl_img.grid(row=i+2, column=j, padx=(7,0),
96
sticky='we')
97
98 def write_label(self, movie, i, j, key, name):
lbl = tk .Label(self.frame, name=name,
99 text=str(movie[key]), height=4,
bg=COLORS.WHITE, fg=COLORS.BLACK,
font=('Arial', 10, 'bold'),
100
cursor='hand2')
101
102 # bind the left click event
103 lbl.bind('<Button-1>', self.movie_click)
104
105 # configure width
106 # TODO - modify the width of columns
107
# TODO - modify background color for odd
108
and even lines
109
110 # place in a grid
111 if key == 'imdbVotes':
lbl.grid(row=i+2, column=j, sticky='we',
112
padx=(0, 10))
113 else:
lbl.grid(row=i+2, column=j, sticky='we',
114
padx=(0, 1))
115
116 def fill_bg(self, widget, i):
117 # check if the row (i) is odd
118 if i % 2 == 1:
119
widget.configure(bg=COLORS.LIST_ODD_LINE)
120 else:
121
widget.configure(bg=COLORS.LIST_EVEN_LINE)
122
123 def create_combo_box_select_event(self, event):
MovieList.page_number =
124
int(event.widget.get())
125 # clear the table cells
126 self.clear_table(event)
127 # recreate the table
128 self.create_table()
129
130 def create_combo_box(self):
131 # each page -> 10 movies
132 # total 250 movies
133 # to upper limit 250 / 10 = 25
values = list(range(1,
134
MovieList.total_num_pages))
pages = ttk .Combobox(self.frame,
135
values=values, width=4)
136 # index of the page (1 minus page_number)
137 pages.current(MovieList.page_number - 1)
138
139 # bind select event
pages.bind('<<ComboboxSelected>>',
140
self.create_combo_box_select_event)
141 pages.grid(row=self.i, column=2, pady=(15,0))
142
143 def clear_table(self, event):
144 master = event .widget.master
145 # loop over the children
146 master_children_copy = master .children.copy()
147 for child in master_children_copy:
148 if 'table_row_' in child:
149 master.children[child].destroy()
150
151 def movie_click(self, event):
152 imdbID = str(event.widget).split('_')[3]
153
# modify the right frame (clear the right
154
frame)
155 # TODO - modify Right Frame
156
157 # modify the left frame
158 # TODO - modify Left Frame
159
160 def modify_right_frame(self, event, imdbID):
6 """
7
8 from .home.Home import Home
9 from .movie_list.MovieList import MovieList
Movie Detail
Now it’s time to develop the Movie Detail page. When the user
clicks on a movie in the Movie List, we want to display the details
of that movie in a new page. This new page will be the
MovieDetail class and the user will be navigated to that page on
click.
Let’s start by creating a new folder named movie_detail in the
page package. And in this movie_detail directory create a new
Python file as MovieDetail.py . The file name and the class name
are again the same.
Complete the missing lines in the MovieDetail class:
42
43 def render_keys(self):
44 for i, key in enumerate(self.movie.keys()):
45 txt = str(key)
lbl = tk .Label(self.frame, text=txt, height=2,
46
width=12, anchor='w')
47 self.fill_bg(lbl, i)
48 lbl.grid(row=i+2, column=0, padx=(10, 1))
49
50 def fill_bg(self, widget, i):
51 # check if the row (i) is odd
52 if i % 2 == 1:
53
widget.configure(bg=COLORS.LIST_ODD_LINE)
54 else:
55
widget.configure(bg=COLORS.LIST_EVEN_LINE)
56
57 def render_values(self):
58 # TODO - render the values
And this is the end of our assignment on Movie Library. For the
solutions you can check Chapter 10 Project 2 - Movie Library.
12. Final Exam
The Final Exam includes the topics we have covered in this
book. It is a multiple choice exam with 20 questions. Each
question is 5 points, which makes 100 points in total. You
should get at least 70 points to be successful. The duration is
120 minutes.
Topic Questions
Exception
3
Handling
Modules &
3
Packages
Format Operations 2
File Operations 3
OOP 7
Tkinter 2
Total 20
def total_likes():
posts = [{ 'Image': 4, 'Like': 20, 'Comment': 12},
{'Like': 15, 'Comment': 8, 'Share': 10},
{'Image': 7, 'Comment': 16, 'Share': 37},
{'Image': 6, 'Like': 10, 'Comment': 9}]
total = 0
return total
sum_of_all_likes = total_likes()
print(sum_of_all_likes)
A def total_likes():
posts = [{ 'Image': 4, 'Like': 20, 'Comment': 12},
{'Like': 15, 'Comment': 8, 'Share': 10},
{'Image': 7, 'Comment': 16, 'Share': 37},
{'Image': 6, 'Like': 10, 'Comment': 9}]
total = 0
try:
for post in posts:
total = total + post[ 'Like']
except:
raise Exception('Error in summation.')
return total
sum_of_all_likes = total_likes()
print(sum_of_all_likes)
B def total_likes():
posts = [{ 'Image': 4, 'Like': 20, 'Comment': 12},
{'Like': 15, 'Comment': 8, 'Share': 10},
{'Image': 7, 'Comment': 16, 'Share': 37},
{'Image': 6, 'Like': 10, 'Comment': 9}]
total = 0
try:
for post in posts:
total = total + post[ 'Like']
except:
pass
return total
sum_of_all_likes = total_likes()
print(sum_of_all_likes)
C def total_likes():
posts = [{ 'Image': 4, 'Like': 20, 'Comment': 12},
{'Like': 15, 'Comment': 8, 'Share': 10},
{'Image': 7, 'Comment': 16, 'Share': 37},
{'Image': 6, 'Like': 10, 'Comment': 9}]
total = 0
return total
sum_of_all_likes = total_likes()
print(sum_of_all_likes)
D def total_likes():
posts = [{ 'Image': 4, 'Like': 20, 'Comment': 12},
{'Like': 15, 'Comment': 8, 'Share': 10},
{'Image': 7, 'Comment': 16, 'Share': 37},
{'Image': 6, 'Like': 10, 'Comment': 9}]
total = 0
return total
sum_of_all_likes = total_likes()
print(sum_of_all_likes)
Q2:
Expected Output:
item 1 is not a letter
item ! is not a letter
item 3 is not a letter
item - is not a letter
item * is not a letter
paybon
A def secret_word_in_list(a_list):
word = ""
for i in a_list:
try:
assert int(i)
except:
print("item {0} is not a letter".format(i))
pass
else:
word += i
return word
B def secret_word_in_list(a_list):
word = ""
for i in a_list:
try:
assert i .isalpha()
except:
print("item {0} is not a letter".format(i))
pass
else:
word += i
return word
C def secret_word_in_list(a_list):
word = ""
for i in a_list:
try:
assert i .isalpha()
else:
word += i
return word
D def secret_word_in_list(a_list):
word = ""
for i in a_list:
try:
assert i .isalpha()
except:
raise "item {0} is not a letter".format(i)
else:
word += i
return word
Q3:
A def read_file(path):
try:
file = open(path)
except FileNotFoundError as err:
raise err
else:
print(file.read())
finally:
file.close()
B def read_file(path):
try:
file = open(path)
except:
file.close()
else:
print(file.read())
finally:
raise FileNotFoundError
C def read_file(path):
try:
file = open(path)
except FileNotFoundError:
raise
else:
print(file.read())
finally:
try:
file.close()
except:
pass
D def read_file(path):
try:
file = open(path)
except FileNotFoundError:
print("No such file...")
else:
print(file.read())
finally:
try:
file.close()
except:
pass
Q4:
Which one below will give you the current folder that Python
runs in?
A import os
project_path = os .getcwd()
B import sys
project_path = sys .path
C import os
project_path = os .environ
D import sys
project_path = sys .gettrace()
Q5:
Let's say you want to import the Path class from pathlib module.
Which one below is not appropriate for this task?
A import pathlib
path = pathlib .Path('.')
Q7:
capitals = {
'UK': 'London',
'Germany': 'Berlin',
'Canada': 'Ottawa',
'US': 'Washington'
}
Which one below can print the following text by using the
capitals dictionary?
"Capital of Germany is Berlin."
A country = 'Germany'
s = "Capital of {0} is {1}.".format(country,
capitals[country])
print(s)
B country = 'Germany'
s = "Capital of {1} is {0}.".format(country,
capitals[country])
print(s)
C country = 'Germany'
s = "Capital of {} is {}.".format(capitals[country], country)
print(s)
D country = 'Germany'
s = "Capital of {0} is {1}.".format(capitals[country],
country)
print(s)
Q8:
capitals = {
'UK': 'London',
'Germany': 'Berlin',
'Canada': 'Ottawa',
'US': 'Washington'
}
Which one below can print the following text for all the items in
the capitals dictionary?
Capital of [Country] is [City].
Expected Output:
Capital of UK is London.
Capital of Germany is Berlin.
Capital of Canada is Ottawa.
Capital of US is Washington.
Q9:
We want to get the names of all the folders and files in the
current directory.
Which one below cannot give this?
A import os
from pathlib import Path
B import os
C import os
from pathlib import Path
D import os
from pathlib import Path
Q10:
* K1
* K1_1
* K1_2
* K2
* K3
* K3_1
A import os
os.mkdirs('K1/K1_1')
os.mkdirs('K1/K1_2')
os.mkdirs('K2')
os.mkdirs('K3/K3_1')
B import os
os.makedirs('K1/K1_1')
os.makedirs('K1/K1_2')
os.makedirs('K2')
os.makedirs('K3/K3_1')
C import os
os.makedirs('K1/K1_1')
os.makedirs('K1/K1_2')
os.makedir('K2')
os.makedirs('K3/K3_1')
D import os
os.mkdirs('K1/K1_1')
os.mkdirs('K1/K1_2')
os.mkdir('K2')
os.mkdirs('K3/K3_1')
Q11:
A import os
import fnmatch
[print(file.name)
for file in os .scandir(path)
if file.is_file() and fnmatch .fnmatch(file.name, pattern)]
B import os
import fnmatch
[print(file.name)
for file in os .scandir(path)
if fnmatch .fnmatch(file.name, pattern)]
C import os
import fnmatch
[print(file.name)
for file in os .scandir(path)
if not file.is_file() and fnmatch .fnmatch(file.name,
pattern)]
D import os
import fnmatch
[print(file.name)
for file in os .scandir(path)
if file.is_file() and fnmatch .fnmatch(file.name, pattern)]
Q12:
Expected Output:
Bruce Wayne
Clark Kent
A class SuperHero:
def __init__(self, age, real_name, name):
self.name = name
self.real_name = real_name
self.age = age
print(batman.real_name)
print(superman.real_name)
B class SuperHero:
def __init__(self, name, real_name, age):
self.name = name
self.real_name = real_name
self.age = age
print(batman.real_name)
print(superman.real_name)
C class SuperHero():
self.name = name
self.real_name = real_name
self.age = age
print(batman.real_name)
print(superman.real_name)
print(batman.real_name)
print(superman.real_name)
Q13:
class Vehicle:
def __init__(self, brand, km, age):
self.brand = brand
self.km = km
self.age = age
You will define a child class which inherits from the Vehicle
class.
Its name will be Car.
Car has an extra attribute as 'model'.
And the age of Car increases by 10 instead of 1. (set_age)
Which one below is the correct definition of the Car class?
A class Car(Vehicle):
def __init__(self, brand, km, age, model):
super().__init__()
self.model = model
def set_age(self):
self.age += 10
B class Car(Vehicle):
def __init__(self, brand, km, age, model):
super().__init__(brand, km, age)
self.model = model
def set_age(self):
self.age += 10
C class Car(Vehicle):
def __init__(self, brand, km, age, model):
super().__init__(brand, km, age, model)
def set_age(self):
self.age += 10
D class Car(Vehicle):
def __init__(self, brand, km, age, model):
super().__init__(brand, km, age)
self.model = model
self.age += 10
Q14:
Example:
circle = Circle(8)
print(circle.area())
print(circle.perimeter())
Expected Output:
201.06192982974676
50.26548245743669
class Circle:
def __init__(radius):
self.r = radius
def area(self):
return pi * self.r**2
def perimeter(self):
return 2 * pi * self.r
class Circle(object):
def __init__(self, r):
self.r = r
def area(self):
return pi * r **2
def perimeter(self):
return 2 * pi * r
class Circle(object):
def __init__(self, radius):
self.r = radius
def area(self):
return pi * self.r**2
def perimeter(self):
return 2 * pi * self.r
D from math import pi
class Circle:
def __init__(self, r):
self.r = r
def area(self):
area = pi * self.r**2
def perimeter(self):
perimeter = 2 * pi * self.r
Q15:
Expected Output:
Laser says -> ...zzz...printing...zzz...
Which one below is the correct definition of the Printer class?
A class Printer(object):
def __init__(self, pname, pmodel):
self.printer_name = pname
self.model = pmodel
def __print__(self):
return f '{self.printer_name} says ->
...zzz...printing...zzz...'
B class Printer(object):
def __init__(self, pname, pmodel):
self.printer_name = pname
self.model = pmodel
def __add__(self):
return f '{self.printer_name} says ->
...zzz...printing...zzz...'
C class Printer(object):
def __init__(self, pname, pmodel):
self.printer_name = pname
self.model = pmodel
def __str__(self):
return '{1} says ->
...zzz...printing...zzz...'.format(self.printer_name)
D class Printer(object):
def __init__(self, pname, pmodel):
self.printer_name = pname
self.model = pmodel
def __str__(self):
return f '{self.printer_name} says ->
...zzz...printing...zzz...'
Q16:
class X:
def x(self):
return self.y()
def y(self):
return 'Y'
class Y(X):
def y(self):
return 'Z'
x = X()
y = Y()
print(x.x(), y.y())
A YZ
B XY
C X Z
D YX
Q17:
Q18:
class A:
pass
class B:
pass
class C:
pass
class D(A):
pass
Q19:
A import tkinter as tk
from tkinter import ttk
sting_var = tk .StringVar()
values = [ 'Python', 'JavaScript', 'Java', 'C#']
cmb.pack(padx=20, pady=20)
cmb.mainloop()
B import tkinter as tk
from tkinter import ttk
root = tk .Tk()
sting_var = tk .StringVar()
values = [ 'Python', 'JavaScript', 'Java', 'C#']
root.mainloop()
C import tkinter as tk
from tkinter import ttk
root = tk .Tk()
sting_var = tk .StringVar()
values = [ 'Python', 'JavaScript', 'Java', 'C#']
cmb.pack(padx=20, pady=20)
root.mainloop()
D import tkinter as tk
from tkinter import ttk
root = tk .Tk()
sting_var = tk .StringVar()
values = [ 'Python', 'JavaScript', 'Java', 'C#']
cmb.pack(padx=20, pady=20)
root.mainloop()
Q20:
You want to create a GUI (Graphical User Interface) application
by using tkinter package of Python.
It will render two buttons on the screen.
The texts on the buttons will be: 'Hello' and 'Exit'
When the user clicks the 'Hello' button it will print as;
"Tkinter GUI is fun..."
When the user clicks the 'Exit' button it will close the main
window and exit the program
Which one below is the correct definition of this app?
A import tkinter as tk
def greet():
print("Tkinter GUI is fun...")
def exit():
btn_exit.destroy()
root = tk .Tk()
frame = tk .Frame(root)
frame.pack()
root.mainloop()
B import tkinter as tk
def greet():
print("Tkinter GUI is fun...")
def exit():
root.destroy()
root = tk .Tk()
frame = tk .Frame(root)
frame.pack()
root.mainloop()
C import tkinter as tk
def greet():
print("Tkinter GUI is fun...")
def exit():
root.destroy()
root = tk .Tk()
frame = tk .Frame()
root.mainloop()
D import tkinter as tk
def greet():
print("Tkinter GUI is fun...")
def exit():
root.destroy()
root = tk .Tk()
frame = tk .Frame(root)
frame.pack()
root.mainloop()
ANSWERS OF THE FINAL EXAM QUESTIONS
Question Answer
Q1 C
Q2 B
Q3 D
Q4 A
Q5 B
Q6 C
Q7 A
Q8 D
Q9 C
Q10 B
Q11 D
Q12 A
Q13 B
Q14 C
Q15 D
Q16 A
Q17 C
Q18 B
Q19 C
Q20 D
13. Conclusion
This is the last chapter in this book. We have covered almost all
of the intermediate level concepts in Python and we covered them
in great detail. We started with setting our development
environment, the PyCharm IDE. Then we learned Exception
Handling, Modules & Packages, Format Operations and File
Operations in Python. Finally we covered one of the most
important topics in computer programming which is OOP (Object
Oriented Programming). We built two big projects that are the PDF
& CSV Operations and the Movie Library with Tkinter. Especially
in the Movie Library we build a real world project which you can
use as a reference in your programming life. You also had
assignments after the projects. And finally you got the Final Exam
to test yourself.
Good bye.
Musa Arda