Rick Sekuloski - PYTHON - Master Python OOP Programming With One Guide Only! A Lot of Coding, Practice and Theory Learn Python With Hands-On Projects (2022)
Rick Sekuloski - PYTHON - Master Python OOP Programming With One Guide Only! A Lot of Coding, Practice and Theory Learn Python With Hands-On Projects (2022)
Rick Sekulsoki
Copyright © 2022 Rick Sekuloski
All rights reserved.
ISBN:
PREFACE
WHAT IS PYTHON?
PYTHON INTERPRETER
VARIABLES IN PYTHON
COMMENTS IN PYTHON
DATA TYPES
ROUND()
ABS()
OPERATOR PRECEDENCE
ORDER OF PRECEDENCE:
CONSTANTS IN PYTHON
STRING CONCATENATION
ESCAPE SEQUENCE
FORMATTED STRINGS
HOW STRINGS ARE STORED?
STRING IMMUTABILITY
PRACTICE TIME
LEN()
UPPER()
CAPITALIZE()
LOWER()
FIND()
REPLACE()
JOIN()
PYTHON IN KEYWORD
BOOLEANS IN PYTHON (DATA TYPE)
LIST SLICING
MULTI-DIMENSIONAL LISTS
LISTS METHODS
APPEND() METHOD
INSERT() METHOD
EXTEND() METHOD
POP() METHOD
REMOVE() METHOD
CLEAR() METHOD
INDEX() METHOD
COUNT() METHOD
SORT() METHOD
COPY() METHOD
REVERSE() METHOD
.JOIN()
LIST UNPACKING
DICTIONARY – METHODS
GET() METHOD
KEYS()
VALUES() METHOD
ITEMS() METHOD
CLEAR()
COPY()
POP()
POPITEM()
UPDATE()
SETDEFAULT() METHOD
TUPLE METHODS
COUNT()
INDEX()
COPY() METHOD
CLEAR() METHOD
DIFFERENCE() METHOD
DIFFERENCE_UPDATE() METHOD
DISCARD() METHOD
INTERSECTION () METHOD
INTERSECTION_UPDATE() METHOD
ISDISJOINT() METHOD
UNION()
ISSUBSET()
ISSUPERSET()
SUMMARY
CONTROL STRUCTURES
IF-STATEMENT
IF-ELSE STATEMENT
ELIF
PYTHON OPERATORS
ARITHMETIC OPERATORS
ASSIGNMENT OPERATORS
BITWISE OR |=
BITWISE XOR ^=
COMPARISON OPERATORS
LOGICAL OPERATORS
BOOLEAN CONTEXT
THE BUILT-IN BOOL() FUNCTION
TERNARY OPERATOR
SHORT-CIRCUITING
MEMBERSHIP OPERATORS
PYTHON LOOPS
NESTING LOOPS
WHILE LOOP
ITERABLES
ITEMS() METHOD
VALUES() METHOD
KEYS() METHOD
PRACTICE TIME
PYTHON FUNCTIONS
FUNCTION RETURN
NESTED FUNCTIONS
DOCSTRINGS IN PYTHON
SCOPE
PYTHON INSTALLATION
DEVELOPER TOOLS
CODE FORMATTING
WHAT IS PEP?
PYCHARM INSTALLATION
SUMMARY
CHAPTER 4 – OBJECT ORIENTED PROGRAMMING –
ADVANCED PYTHON
CLASS KEYWORD
__INIT__ CONSTRUCTOR
METHOD OVERRIDING
SUPER() FUNCTION
ID()
DUNDER/MAGIC METHODS
MULTIPLE INHERITANCE
SUMMARY
CHAPTER 5- PHYTON – MODULES AND PACKAGES
PYTHON PACKAGES
PYTHON __NAME__
SUMMARY
OPEN
APPEND
FILE PATHS
SUMMARY
ERRORS IN PYTHON
ERROR HANDLING
EXCEPT BLOCK
FINALLY BLOCK
RAISE AN EXCEPTION
SUMMARY
PROJECTS
PROJECT_1: GUESS THE RANDOM NUMBER
CODING HINTS
DATETIME MODULE
MAIN.PY FILE:
LOGIN.PY FILE:
REGISTER.PY
STUDENT_TASKS.PY
APPENDIX A – PROJECT
SOLUTIONS
PROJECT_1: GUESS THE RANDOM NUMBER SOLUTION:
APPENDIX D: RESOURCES
Preface
This book is a perfect guide for someone that wants to learn Python. You
will find that this book explains complex concepts in an easy-to-understand
manner. After covering the basics, we will shift our focus to some
intermediate features that every Python programmer should know. I’m not a
professional writer but I’m a professional web developer and programmer,
therefore my goal is to teach you programming through perfect examples
and clean and executable code. When I first started learning Python, I was
afraid of how hard this language could be, but I can assure you that Python,
like any other programming language, only requires patience, practice, and
of course a good guide with practical examples. The goal of my book is to
help you bring your current skills to a professional level. If you are looking
for additional reference materials then I can only suggest the Python
documentation. If you have already looked at the Python documentation
then you know it can be long, scary, and overwhelming. Do not worry
because this book will save you months of researching, planning, and
sleepless nights.
You can visit Python’s official websites by clicking on following link:
https://docs.python.org/3/
I would also like to hear from you. If you need to contact me, please reach
out through any of my social media accounts, and please consider leaving a
review with your comment.
Twitter: https://twitter.com/Rick29702077?s=09
LinkedIn: https://www.linkedin.com/in/rick-sekuloski
Facebook:
https://www.facebook.com/theodorecodingwebdevelopment/
YouTube: https://www.youtube.com/channel/UCQanUcCNaBg-IM-
k0u8z0oQ
• Beginners – If you are a beginner, this book will definitely help you
to learn Python
• Teachers/Educators – Are you someone that teaches Python? Then
this book is an organized guide that will help you and your students
learn the new concepts through practical examples and theory
• Developers – Are you a developer but need to refresh your memory?
This is the best reference guide you will find
• Anyone - that is seeking to gain a deep understanding of the Python
language
• Access to internet
• Any of the following: Computer, Laptop, tablet, phone, or an eBook
Reader (kindle)
• An up-to-date browser such as Google Chrome, Firefox, Edge, or
Safari
• I will use a combination of code editors and IDEs, but you can decide
which tool is the most suitable for your needs
If you are using an e-reader to read this book, then please sit back, get
comfortable, and enjoy because I will explain every step and include the
code and screenshots. But if you like to go through the examples and try to
run the code, then please use a computer or laptop. The book will be
divided into chapters, and in each chapter will be a new theory coupled with
examples, output, and screenshots. The first few chapters are easy but at the
same time, they are the most important ones.
https://github.com/RickSekuloski/python_book1
There you will find all the materials (code examples and exercises) that I
will use in this book.
Once the file is downloaded to your computer, you will need to unzip the
content. Please don't try to open or run the code while it's inside the zipped
folders/directories. Ensure you extract the folder into your desired
destination, such as the desktop.
You can Unzip the files using the following programs:
Welcome to the first chapter. This chapter is by far the most important one.
Here, the idea is to try to understand the basic Python features because
without them you will have a hard time understanding the concepts in the
following chapters where more intermediate features will be introduced. In
this chapter, we will start by covering the basics of Python, like what Python
is and how we can run Python code. Although it is called Python basics,
there will be some features that are not that simple to understand, therefore,
please make sure you spend enough time learning the concepts before
moving to the next section.
What is Python?
According to official documentation, Python is ‘An interpreted, object-
oriented, high-level programming language with dynamic semantics. Python
is a general computer programming language, meaning it can be used to
create applications that will solve different problems. Python can be used to
create websites, software, or to even perform data analysis. Learning Python
is simple because it uses simple syntax, therefore, even people that are new
to programming can learn it. Python emphasizes code readability and this
can be achieved because Python supports modules and packages. In simple
words, this means that instead of having one large file with thousands of
lines of code, we can have multiple files that can communicate and share the
code with the main file. This reduces the program maintenance cost and
because Python is free to use, it is a perfect programming language. The
Python programming language was created by Guido ban Rossum and it was
first released on February 20, 1991.
Games development
Web Development
AI and Machine learning
E-commerce
Image processing and graphic design
Scientific computing
Another benefit is that the Python code is interpreted, not compiled. This
means that the code is interpreted line by line during the runtime and doesn’t
require pre-runtime compilation. Because the Python code uses an
Interpreter, it executes the statements line by line and it’s easier to debug and
spot the errors. There are third-party tools that can be used to optimize the
performance and to also to handle errors. One huge benefit is that Python
code is portable and cross-platform functionality is a must in today’s
development. This means that applications created in Python can run on any
operating system like Windows, macOS, Linus, and Unix without
modifications. These are some of the features that make Python one of the
most popular programming languages.
Python Interpreter
As we know from the definition, Python is an interpreted high-level
language. This means Python needs an interpreter to read the code from our
file and execute the instructions from that file. Other high-level languages
like C++ require us to compile the code. The compilation process will turn
the code that is humanly understandable into a code that only machines can
understand called Machine code. The machine code consists of basic-level
instructions that are executed by the CPU. After the compilation process, an
executable file is generated. On the other hand, Python is an interpreted
language, meaning it will not translate the code into machine code but will
translate it into bytecode which will create a file with an extension (.pyc).
The Python interpreter comes when we install Python and we will see how
this is done in chapter 3. This means when we install the official Python
language, we have access to the Python interpreter as well. In short, the
Python interpreter is a program that will read our source code and run it for
us. So, what is the difference between the bytecode file and the compilation
file? Well, the compilation file is a set of instructions that are executed on
the CPU and the bytecode file instructions are executed on a Virtual
Machine known as CPython VM. Because of this (CPython VM), the
bytecode can be executed on any operating system or platform. In simple
words, a developer writes the code in Python and then the code is compiled
into Python bytecode. This will create a file with an extension (.pyc) and the
bytecode is something that we as developers can’t control because it
happens in the background. So, the bytecode is a low-level representation of
the code we write. The VM (Python Virtual Machine) is something we
should not worry about as well because it is part of the Python system and is
used to run our Python files/scripts. What is interesting is that the Python
Interpreter is written and implemented as a C program and that is why it’s
called CPython. Here is the figure that explains the whole process of how
the Interpreter works:
From the figure above, we can see that we write the Python code using
different tools like IDEs and Code editors (we will install them in chapter 3)
and the file is taken by the Interpreter that uses the compiler to create the
bytecode file with the extension (.pyc). This file is taken and executed by
the Virtual Machine that is platform-independent and gives us the option to
use Python on any system we want. In chapter 3, we will see how we can
install Python on our machines and how to use different tools to write and
execute Python code professionally.
As you can see, there are different ways to write, run, and execute Python
but let’s start with the simplest one. There is a website called replit that we
can use for free to write and run Python code. You can even finish my entire
Python book using this website only. Here is a link to the official website:
https://replit.com/
1) The Number one is the main.py file. This is the file that replit creates
for us when we create a new project. As you can see, we need to use
the .py extension to indicate that we are going to write a Python code
2) The number two will let us add a new file to the existing project. Just
don’t forget to add .py extension when you name your file. For
example, you can call or name your file test.py
3) The number 3 allows us to create a new folder where we can keep
other Python files
4) The number 4 is where we write our actual Python code
5) The number 5 or the green play button is to execute the code we have
written in the main.py file
6) The number 6 is where we will see the output after we execute the
main.py file by clicking on the run button
The version number of Python in websites like replit is lower than the
official Python that we download from the official website. For example, in
chapter 3, we will install the latest Python version but I just wanted to show
you that replit uses Python Interpreter as well. If we go to the official
Python.org website and click on the downloads tab from the menu, we will
see something like this:
https://www.python.org/downloads/
As you can see from the figure above, the Python website recognizes my
operating system automatically and gives me an option to download Python
version 3.10.5 for macOS. In your case, depending on how far in the future
you will read this book, you will have a different version. If we go back to
our replit website and click on the shell tab and type: python -V or if that is
not working try python3 –V, you will see that the Python version the
website is using to run our code is not the same as the official Python page:
The version is 3.8.12, an older version of Python but as long as it starts with
the same first number (3) as the official website, we are good to go. This is
everything I wanted to explain about replit website. It is very easy and
straightforward to use so in the next section we will write our first Python
code.
print('Hi everyone!')
After we’ve written the code, we can run or execute it if we press the green
play button on top and make sure the ‘Console’ tab is selected:
From the figure above, we can see that on the right side is where we have the
output ‘Hi everyone!’ Don’t worry about the print function or the code we
wrote because we will get to that part later in this chapter. One question that
I usually get is, how does the replit website provide this output to us? In
Python files like the one we are using at the moment main.py, we write the
python code and it can be only one line or it can be thousands of lines of
code. These lines are individual instructions and the Python Interpreter that
runs in the replit will read the main.py file line by line and convert them to a
bytecode so it can finally use the CPython Virtual machine to produce an
output. Let’s write more code. It’s ok if you don’t understand the code at this
stage but by doing this, we help you to get familiar with the Python
language, therefore under the print() function, write the following code:
If we click on the run button again, we will see the following output:
The input function allows the user to enter text, and I have entered Rick after
the question mark. As you can see, I wrote our first program asking the user
to input something. In the console on the right, I have entered my name (you
can enter your name if you want to) but I haven’t clicked on the return or
enter on my keyboard and because of this the program still runs in the
background and waits for us. We know the program waits because we don’t
have the green play button on top, instead of that, we have a black square or
stop button. This button will disappear as soon as we press return or enter on
our keyboards:
Before I explain something even more interesting, let’s explain the print and
input function. In Python, we have built-in functions that come when we
install the Python language. In this case, Python is installed on the replit
website and that is why we can use these functions. The print() function is
used in Python to print out a specific message on the screen or console. The
message in this case is a simple string but it can be much more complex. The
second function we used is called input() and this just like the print() is a
built-in function that Python allows us to use. The input() function allows
users to input something.
Variables in Python
If we want to make our simple program even more interesting, we can use
variables. We will discuss variables in detail later in this section but you
should know that variables in Python are like containers where we can store
values. In our case, I want the name from the input function to be stored in a
variable like this:
name = 'Rick'
On line three I’m using the print function to display the content of the
variable name to the console and that is why I get the name ‘Rick’ in the
output. To summarize, variables are names we give to the data we want to
store. We need variables because we can store and manipulate different data.
Our variable called name now can be reused again and again in our code.
When we create a variable in the background, the program will allocate
space in the memory where it will store the data. An important thing you
need to know is that the data inside the variable can be changed with a
different value in our program later. For example, after line number three,
let’s write this code:
name = 'Jason'
print(name)
If we run the following code, what do you think will be the output ‘Rick’ or
‘Jason’? Let’s run the file and observe the result:
As you can see, we can change the content of the variable called name to a
different value. There are a few keywords I want you to remember from this
section:
Variable declaration
Initialization
Initial value
The above keywords are not unique to Python only, they have been used in
most programming languages.
Please consider the following statement:
my_age = 10
In this statement, we are defining a variable called my_age and this is called
variable declaration because we are giving the variable a name. We use the
equal sign = to assign a value to the variable and this is the process of
initialization. During the variable declaration, it is advised to give an initial
value or data. In our case, this will be 10, but in Python, we can create
variables without values as well but this is a topic for another section. We
can create multiple variables in one line like this:
first, second = 1, 2
first = 1
second = 2
When we are creating variables and initialize them to a value, we always use
the assignment operator ‘=’. Because of this operator (equal), we can
achieve the binding process between a variable name and value. We will
learn about some rules on how we can name the variables in Python but I
think it is the right time to move and learn other Python features. This was
our warming-up section and I hope you liked it.
Comments in Python
We can write comments in our code if we use the # symbol in front. The
comments are not going to be included in the final output and the Python
Interpreter will ignore them. Comments are very useful in Python because
they will make our code more readable not just for us but for any other
developers that might read our code.
In Python, we can write the single line comment like this:
'''
Hi,
this is multi-
line comment!
'''
The rule of thumb is that the comments we are using in our code must add
valuable information. This information can help you if you read your own
code in the future but can also help other developers understand what your
code is about because not all of the features are easy to understand. Please
do not add comments on every piece of code you write because you will
clutter your code. The comments you write must be short yet concise.
Data Types
Data types are values in Python. Each value in Python has a data type. There
are various data types in Python. I will try to separate the data types in
Python by their importance in different categories and I will start with listing
the core or basic data types first:
Int – integer
Float – floating point number
Complex – this is used beside int and float numbers
Bool
Str – string
Most books will explain the above fundamental data types but in Python,
some data types are beyond the core types. Python allows us to create our
own data types using:
In Python, we also have something called specialized data types. These data
types are not built-in in python and they include:
Packages
Modules
These packages and modules can be used from different libraries and are
used if the above data types are not sufficient for us. Another important data
type is None. The None data type is used when we want to specify the
absence of value, similarly to Null in JavaScript language. The final data
type that I want to mention is called Complex Numbers. The complex
numbers are represented by complex Class. In the following figure you can
clearly see how the data types are divided into categories:
In this chapter, we will cover all of the Python data types but I will not be
following the exact order as in the figure above. There are a lot of other
Python features that are worth mentioning and I think including them as I
explain the different data types will help you to understand Python. I
promise the first couple of sections are the hardest as we can’t move faster
enough because we don’t know the core features of Python. Therefore, I
would like to suggest that you carefully read all of the sections from now
onwards.
If you have noticed, when we write functions in Python, we always use the ()
(parenthesis) or brackets. The brackets are a very important part when we try
to access the function. Without them, the function will not return the result
we expect. In Python, we can use the type() function to determine the data
type. If you have done other programming languages such as JavaScript, you
will notice that we have a lot of similarities between these two languages. In
JavaScript, to determine the type, we use the typeof() function. From the
figure above, we have two variables that have a value from type Integer. Let
us check if this is true using the type() function:
print(type(a))
print(type(b))
If we run the above code in the replit, we will get the following output:
Both variables are from class ‘int’ and please don’t worry about what the
class keyword means at this stage. To summarize, Integers are whole
numbers without decimal parts.
From the figure above, we can see that the output is 0b10, the first part ‘0b’
is a prefix (zero b) to differentiate the binary numbers from numbers in base
10 representation, or in simple words, the prefix denotes a binary literal. The
binary value for the actual number 2 is represented by binary 10. Now that
we know these two data types (int and float), it is time to learn what
mathematical operations we can perform on them.
This operator will return an integer that is rounded down to the nearest
whole number.
Python Modulo Operator (%)
This operator is known as the Modulo operator and the symbol we need to
use is (%). This operator is used to return the remainder when we are
dividing the left operand by the right operand like in the following example:
The result is 1 because the modulo operator will give us the remainder of
this division. If you have read some of my JavaScript books you will see that
we use the modulo operator in JavaScript as well. The modulo operator is
considered an arithmetic operation along with these ones:
+, - , /, *, **, //
In the next section, we will cover what type of math functions we can use
with floats and integer numbers.
Math functions on Int and Float data types
These math functions we will learn in this section are built-in functions. This
means that Python gives us access to these math functions without the need
to import external libraries. The first function we will discuss is the round()
function.
round()
The round function rounds a float number to the nearest integer number. For
example, if we have float number 4.3 and use the round() function, the
output should be 4. Let’s test a few more examples and see what the round()
function will return:
The replit website works similarly to having installed a code editor or as if
we are using an IDE to write Python code. Let me show you why. If I type
round and add the open and close bracket without pressing the run button the
replit website will give me a short description of what this function does in
Python.
This is the exact behavior we will get if we are using a code editor or IDE to
run and execute Python code on our machines.
abs()
The abs() function will return the absolute value of the argument. If you are
not that good at maths, then the absolute function will never return a
negative number:
As we can see, the absolute value of negative ten will be positive ten. There
are more math functions available on the Python official website and we will
not cover all of them because the goal of this book is to teach you Python,
not how to be good at math. If you open the link below, you will see a
complete list of math functions that are included in the math module. But
remember that in order to use them, you will need to learn what modules are
and how to import them and this is something we will cover in-depth in
future chapters. You can click on the link and just have a look at the
functions that are available in Python:
https://www.programiz.com/python-programming/modules/math
Operator Precedence
So far, we have learned that we can perform mathematical operations on the
two Python data types (Integer and Float). In order to achieve this, we need
to use operators like +, -, *, /. Most times, the mathematical expressions are
complex and we use a combination of different operators to get the end
result. This is where the operator precedence takes place. The operator
precedence means that when different operators are combined in one
expression, some of them will have precedence over others. The Python
Interpreter knows these rules and always follows them.
For example, let’s consider this expression:
7–3*2
From our math classes, we know that first, we need to perform
multiplication (3 *2) and then subtraction. The Interpreter knows this rule
otherwise if we perform calculations based on the operator order, we will
have a different result:
Order of precedence:
1) () – brackets will have the highest precedence; we first calculate
whatever is wrapped in the brackets
2) ** – (power of) is done only after the parenthesis ()
3) * , / – then we perform multiplication and division
4) + , - The last precedence is the addition and subtraction
The order of precedence is the same for all programming languages but it is
very important to know it. Without looking at the answer, please calculate
the following expression:
(7 - 3) * 2 + 2**2
The answer:
1st) whatever is in brackets (7-3) is done first because of the order of
precedence. Therefore, the result after subtraction of 7-3 will be 4
2nd) will be the power operator and the result of 2 ** 2 will be equal to 4
3rd) will be a multiplication operator that will use the result of the 1st
operation which is 4 and this will be multiplied by 2 which will give us a
result of 8
4th) Finally, the addition operator takes place and the sum of 8 + 4 is 12
You can run the following code in replit. Just wrap it around a print function
so you can see the output:
Rules for Naming a Variable
We know that the variables are containers where we can store data or values.
But in Python, we need to follow some rules when we name variables. For
example, in Python, the variable name can only contain letters (a-z, A-Z),
numbers, or underscores (_). The first character cannot be a number or
underscore. Here is an example with valid and invalid variable names (the
values of the following variables are from a different data type that we will
discuss later in this chapter):
The other convention I will use is the snake case where we use underscore to
separate the words. Let us rewrite the camel case firstName variable to the
snake case:
# snake case notation
first_name = 'Jason'
You can use any of the naming notations you want but I would suggest
picking only one and sticking to it. It is bad practice to use both naming
conventions in one code. In Python, we have a predefined set of keywords
that are reserved words, therefore we cannot use them to name variables.
How we can see this predefined reserved keyword list? This is very easy in
Python because we can use the help() function and type keywords like this:
help(‘keywords’), and the output is the following:
Let’s now assign a new value and this time it can be a float number:
As you can see, we can reassign the value of the variable easily in Python. In
Python, we can use the value of a variable and assign it as a value to a new
variable. I know this is confusing, but hopefully, this example will clarify
things:
From the figure above, we have a variable called my_weight that has a value
of 78. On line 4, there is another variable called your_weight but its value is
based on the my_weight value minus ten.
Constants in Python
In Python, a constant is a type of variable whose value cannot be changed.
But this is not entirely true because we still can change the value. In
JavaScript for example, when we can create a constant variable, the value
cannot be changed because it will throw an error if we try. When we name
constants in Python, we should follow some new naming conventions. We
need to use all capital letters and underscores if we have a compound name.
For example:
# Constants
PI = 3.14
EARTH_GRAVITY = 9.807
But we can change its value although it’s been declared as constant:
As we can see, the constants just like the normal variable can be reassigned
to new values. What is the point of having them? The rule for all developers
is when you see a code that uses constants, you should never attempt to
change their values because their values are meant to stay the same.
__dunder__
In this chapter, I will just mention that there are variables called dunder and
they are a special type that we should not try to change, modify, or reassign
at this stage. We will learn more about them and what they can do in one of
the following chapters.
Expressions and Statements in Python
An Expression in Python is a sequence or combination of operators and
operands that will produce value. It is important to know that an expression
will always yield a value. An expression is a piece of code that will produce
value. In most of the Python literature (Books or Online tutorials), the
expressions and statements are explained in a complicated way.
In the following figure, the result/value of the current_year – year_born is
the expression:
What are the statements? The statements are more complicated than
expressions because they are the entire line of code:
The green line is a statement because it performs some sort of action. The
action here is the assignment of a value to user_age that is produced by the
expression. That is why the whole line becomes a statement because it’s an
action. In the figure below, I have highlighted the two statements:
num1 += num2
print(num1)
Examples:
num1 = 30
num2 = 10
# Subtraction & Assignment
num1 -= num2
print(num1)
# Output: 20
num1 = 30
num2 = 10
# Multiplication & Assignment
num1 *= num2
print(num1)
# Output: 300
num1 = 30
num2 = 10
# Division & Assignment
num1 /= num2
print(num1)
# Output: 3.0
num1 = 30
num2 = 10
# Floor Division & Assignment
num1 //= num2
print(num1)
# Output: 3
num1 = 30
num2 = 10
# Power & Assignment
num1 **= num2
print(num1)
# Output: 590490000000000
num1 = 30
num2 = 10
# Modulo & Assignment
num1 %= num2
print(num1)
# Output: 0
There is another way to write strings in Python and this method is preferred
when we have a long string that stretches into multiple lines. For example,
when we have a long piece of text that will stretch into multiple lines, we
can then use the following syntax:
# Long string
text = '''
first line
second line
third line
'''
print(text)
As you can see, we need to use the three single quotes in a row and then we
can write the string in different lines. If we run the same piece of code, this
will be the output:
The output is in three lines or the same as the original string, and this cannot
be achieved with a single quote because it will give us a syntax error:
String Concatenation
A string concatenation simply means adding strings together using the
addition operator ‘+’. The + operator will join the different strings. From the
example above we have two variables my_name and my_lastname that have
strings as data type so with the string concatenation, we are able to achieve
the following:
Great but there is no space between the first and last name and this is
because there is no white space in the my_lastname variable so there are two
ways how we can add space between the two strings. The first way is not
something I would suggest you do but it does the job:
As you can see, I have added a space before the first letter ‘R’ in the
my_lastname variable. The output is exactly what we need but there is
another more elegant way to do this and that is by adding a space in between
both variables during the concatenation:
Great! Now you know that we can use the plus operator to combine multiple
strings. There are some interesting scenarios that you need to be careful of
when doing string concatenation.
Example:
# can’t concatenate str to int
greet = 'hi'
five = 5
message = greet + five
print(message)
From the figure, it is clear that we can only concatenate strings together
otherwise we will get typeError. We will explain this in the following section
called type conversion.
If we run this in the browser, the output will be 250, but is 250 an integer
number or string? Just to make things clear, let us rewrite the above code and
store the result from the function str into a variable and then check the type
of the variable:
As you can see, the result or the value 250 is from type string. This means
that the str() function converts the 250 integer number we initially passed
into the str function to string. Because the value of the result variable is
string, we cannot do normal mathematical operations like the following:
# can only concatenate str (not "int") to str
print(result + 10)
Escape Sequence
We have discussed that we can create strings if we wrap the text in single
and double quotes, but what we didn’t discuss is that there are special
characters that require us to take some actions. Some of the characters are
illegal and we need to use an escape character. For example, if we want to
add an apostrophe, we need to use an escape character (\) followed by the
character we want to insert (apostrophe).
Let’s have a look at the following example:
text = 'I'm Rick'
If we have used double quotes to wrap the text, then we can use the
apostrophe:
text1 = "I'm Rick"
print(text1)
If case we need to include the backslash in our string, then we can escape it
like this:
If we want to add a new line in our strings, we need to use backslash \n:
If we want to use double quotes inside a string that has already been
surrounded by double quotes, then you need to use the escape character \”
Formatted Strings
If we want to format the strings, we can use the f-strings. They are called f-
springs because of the f leading character preceding the string literal. We
only need to add the letter f before the string and that will tell the Python
interpreter that we want our string to be formatted. Imagine we are collecting
the user details from the registration form:
# f-strings to format the strings
first_name = 'James'
last_name = 'Bond'
age = 55
print(f'Dear {first_name} {last_name}, thank you for being 007 for so many
years!')
In the f-string, we can grab the values from the variables if we put the
variable names in the curly braces {}. The same can be achieved using the
string concatenation, but if you remember, we need to manually add the
space between the names and this will be much more complex and longer to
write:
print('Dear ' + first_name + ' ' + last_name + ', thank you for being 007 for so
many years!')
How Strings are Stored?
So far, we learned how integer and float numbers are stored in memory. The
strings are stored in memory as a sequence of individual characters. This
means that a single character is stored as a string with a length of 1 and can
be accessed by its index. In order to understand how strings are stored, we
need to look at the following example:
my_name = 'Rick S'
As you can see, we have a variable my_name with type string data. We
know the strings are stored as ordered sequence of characters and the first
character is ‘R’ and it will be stored somewhere in memory at location/index
zero (0). The following figure explains how the string is stored in memory
and each of the characters has its own index starting from zero:
This is very powerful because the way the strings are stored makes it easy
for us to retrieve a specific character based on its location. How we can get
an individual character from a string? We can use the name of the variable
followed by the square brackets where we pass the index value. Remember
the strings start from position zero, not 1. Let us get the first character of the
string above using the following syntax:
# String Indexes
my_name = 'Rick S'
print(my_name[0])
As you can see, we only need to know the index position to retrieve any
character. What will happen if we try to run this code:
print(my_name[4])
It looks like nothing but we actually got the white space back because even
those are stored in memory. On the right side of the following figure, I have
highlighted where the empty space is actually printed:
What will happen if you use an index that is not in the string range? If we do
this, we will get an IndexError and this means that the character we try to
access by that index simply does not exist:
This happens very often. Beginners usually make these mistakes because
they keep forgetting that the first character is stored at index zero (0)
You should know that the character at the stop index will not be included in
the output. In the above example, that would be the white space. If we want
to get the full string, we need to write:
print(my_name[0:6])
From the picture above, we can see that the stop or the end index is 6 but the
indexes for these particular string end at position 5. This will not throw an
error because the end index will never be used in the output.
String-slicing with stepover
Here is the stepover syntax:
[start:stop:stepover]
In the previous section, we have learned how to slice a string based on the
start and stop index but now we can include a third value which is called
stepover. The default value of the stepover is 1 and we don’t have to write it
explicitly. The step over indicates the step size and as I mentioned, the
default value is 1, meaning we are going character by character but if we
increase the size of the step, then we will start skipping and the output will
be different. Let’s take a look at the following example:
# Step over
number_string = '012345678'
print(number_string[0:9:2])
From the example, we can see that now the step value is 2 and it will start
from the first position, then step over 2 times and print 2 then step over 2
more times and print 4, and so on. Here is our output:
Working with string slicing is very interesting because there are different
scenarios that we should be aware of. For example, if we only include the
start index and omit the end index, for example like this:
number_string[ 1 : ]
Can you guess what will happen? Here is the output:
This basically means we need to start at position 1 and then because there is
no stop index it will go until the end. This is perfect when we have a long
string and we don’t know what the last index number is. We use this because
we know it will go on until the end. Another interesting scenario is when we
omit the start index but include the index of the end/stop position like this:
number_string[ : 4]
Can you guess the output? If not, here is the screenshot:
This means starting from zero although the value is missing and going until
index 4 but not including the value at the last fourth position. What will
happen if we omit the first stop and the last end index and only include the
stepover like this:
number_string[ : : 1]
This will print the entire string because it starts at the default position zero,
ends where the string ends, and stepping over will be the default value 1.
Here is another example of when the two (start, stop) indexes are missing:
Great! We have learned a lot about string slicing, so let’s see what will
happen if we include negative values as well:
The output is 8 because in Python the negative index means start at the end
of the string instead of position zero. One of the most common operations
with negative indexes is this one:
But the strings are immutable, meaning we cannot change the individual
characters from the strings. We know that in Python the strings can be
accessed by their indexes, for example, if I want to access the first letter of
the my_text, I need to use the index zero (0) like this:
Great! We got the expected result but can I change the first character to a
new character like this:
my_text[0] = 'K'
print(my_text)
If we run this code, we will get a TypeError saying we cannot assign a new
value to string item:
As we can see from the figure above, string immutability is not possible
because we cannot change the value of individual characters once created.
The individual characters are assigned with indexes and stored in the
memory. The only way we can do this is to completely reassign a new value
to the my_text variable. The Python interpreter will remove the old content
from the memory and add new content and that is why reassigning is
possible. To summarize, we cannot reassign part of the string like individual
characters because the Strings in Python are immutable, but we can assign a
new value to the existing string because Python completely deletes the old
string from memory.
Practice time
So far, we have covered a lot of the basic Python features but we still have a
lot of ground to cover and I think you are ready to write your first mini-
Python program. Imagine you want to create a small program that will ask
the user to input two numbers and calculate the sum of the numbers. How
can we write this program? In order to prompt the user to enter values, we
need to use the built-in function called input(). The way the input() function
works is that it waits for the user to input or in our case to write the numbers.
Here is the entire code:
first_num = input('Please enter the first number:')
second_num = input('Please enter the second number:')
result = first_num + second_num
print(f'the sum of the two numbers is:{result}')
If we look at the code above, the sum is 126 but should be 18, right? What is
happening? If you are not sure why is this happening, we can always check
the type of the variables (first_num, second_num):
The values of the two variables are from type string and when we use the ‘+’
operator, the Python interpreter will think we are trying to make String
concatenation. In order to fix this simple problem, we need to do a type
conversion that is explicit using the built-in int() function:
From the figure above, the result is 18 and you should know that the values
coming from the input() function are strings, not numbers, therefore we need
to convert them to the right data type.
Let’s explore some of the very useful built-in functions you will use as a
developer. The first one is called len(), which stands for length:
len()
This built-in function will return the length or the number of items of an
object. We still haven’t covered what Objects are in Python but they are the
most important thing because basically everything is an Object in Python.
The argument that we pass inside the len() function can be a sequence like a
String. Because we learned a lot about String data type, we can use this
function and pass the string as an argument like this:
#1) lin() - returns the length
text = 'A string'
print(len(text));
If we run the code, the output will be 8:
This means that the len() function will give us the actual length starting the
count from 1 not from zero like with the indexes.
As you can see, the function we are using have only one purpose and that is
to return some value. The len() function is used to return the actual length of
an Object like the String. We have used the term functions but I have not
explained what functions are. You can think of functions as a unit that
includes one or more lines of code or statements. The purpose of a function
is to run a piece of code with the data we provide. The data we provide is
called arguments, and we are passing them directly into the function. As we
know, functions have parenthesis like this (). Inside the parenthesis is where
we pass the arguments or the data. We have seen a few built-in functions but
before we start passing arguments, we need to know what that function
accepts because we cannot pass any data we can think of. We can create our
own functions as well and then we know exactly what data we need to pass
but for the built-in functions we need to read the documentation first and
only then we can start using them with confidence. We will learn how to
create our own functions, how to define them, and how to call them, but at
this stage, we just need to know what they are. To summarize, when we use
a function, we need to know what arguments/data we should provide, for
example, here is the round function:
round(number[, ndigits])
This round() function accepts two arguments (number, ndigits) and if you
read the Python documentation, you will know that the second argument
(ndigits) can be omitted or is an optional argument. A function call is when
we use the function name with the arguments:
The functions that are created by us are not built-in functions, they are called
user-defined functions. Let’s talk about methods. What are methods in
Python? Methods in Python are the exact same thing as functions, there is no
difference between them. The only difference is how they are called.
Methods, in simple words, belong to something or someone. This something
is actually the Objects that we will cover in the future. For example, Strings
have their own built-in methods but these methods can only be called
directly by strings and not as we call the print or input function. So how do
strings call these built-in methods? Strings can call the methods using the
‘dot syntax.’ Let’s go over a few handy String methods starting with the
method called upper.
Upper()
This method will convert the given string to upper case.
Here is one example:
# upper()
text = 'A string'
result = text.upper()
print(result)
As you can see, we use the String that is stored in the variable called text to
call/invoke the method upper(). This can be also written like this:
print('A string'.upper())
The output is:
Now you know the methods are functions but they are called by a specific
Object like the strings. This means we cannot call the upper() method like
this:
upper()
The methods for strings will not work for other data types, they belong to
strings only.
Capitalize()
The next interesting method that is used a lot with strings is the capitalize
method. This method will turn the first character into a capital letter:
#capitalize()
print('hi there'.capitalize())
lower()
This method will convert the given string into a lower case:
#lower()
print('My Name Is Joshua'.lower())
find()
This method will find the first occurrence(index) of the specified substring.
This, in simple words, means that this method will check if a certain
substring exists in a given string:
#find()
result = 'My name is Rick, what is yours'.find('is')
print(result)
replace()
We use this method when we want to replace a part of the string with a new
value. This method returns the entire string. The syntax of this method is
more complex and requires passing two values:
string.replace(oldvalue, newvalue)
The first argument is the substring/value you want to replace and the second
argument is the new value. For example, let’s replace ‘Rick’ with ‘Mick’:
#replace()
result = 'My name is Rick, what is yours'.replace('Rick', 'Mick')
print(result)
Great! It works, but wait, we said strings are immutable but it seems this
method changes the part of a string. What is going on? This is confusing
because of the way I have written the code to show you that the original
string cannot be changed. Let’s re-write the code like this:
#replace()
original_string = 'My name is Rick, what is yours'
result = original_string.replace('Rick', 'Mick')
print(original_string)
print(result)
join()
This method will join or concatenate a list of strings together and create a
new string with the desired delimiter. Let me show you what this means in
the following example:
#join()
result = "-".join(["This", "book", "is", "awesome!"])
print(result)
The join method accepts a List of strings and we will learn about Lists as a
separate data type. There are other useful methods you can find in the
Python documentation, but for now, these are the essential methods you
should know.
Python IN Keyword
Just like the built-in functions and methods in Python, we have reserved
keywords as well. These keywords are known as identifiers and cannot be
used as ordinary identifiers. The keywords must be spelled exactly as they
are. In this section, we will talk about the keyword ‘in’ which is very useful
because it has many applications. The ‘in’ reserved keyword can be used
with Strings like this:
# In Keyword with Strings
long_string = 'Andy is 5 years old!'
print('5' in long_string)
This keyword has many purposes and one of them is to check if the part of a
string is present in the sequence. We are trying to find if ‘5’ is present in the
long_string sequence of characters. As we can see, the value ‘5’ is present
and therefore it returns the output True. This value ‘True’ belongs to another
data type in Python called Boolean. We will cover this data type in the next
section. Here is a list of reserved keywords in Python:
https://docs.python.org/2.5/ref/keywords.html
is_false = False
print(is_false)
The power of Boolean will be seen when we learn how to make decisions in
Python. The decision statements like the if-statements have a condition and
that condition will always be evaluated to Boolean True or False. We can
also evaluate an expression to Boolean like in this example:
#compare values
print(2 > 1)
print (10 == 10)
print(10 < 6)
This topic will be explored more in one of the future sections. Let’s run this
example and see the outputs it creates:
print(bool(0))
print(bool(1))
Boolean zero(0) will return False and Boolean 1 will return True. The
Boolean function will convert the integer values to either True or False and it
will be discussed in more detail in the conditional logic section.
print(my_list)
print(my_list1)
print(my_list2)
As I mentioned, the lists can hold an ordered sequence of data. So far, we
have used the variables to store only a single value but now the lists are
making the variables a complex container where we store multiple data of
different types. As you can see, the variable called my_list can hold an
ordered sequence of integer values. The second variable called my_list1 is
an ordered sequence of values that belong to the String data type. The last
variable has mixed data type and this is normal behavior for storing different
types of data in one single container. When we create lists, we need to use
square brackets. The data inside the brackets are called items or elements.
The items or elements are separated by a comma. This data type (lists) is
exactly the same as the arrays in JavaScript. Lists are a collection of items or
ordered sequence of objects. Lists are also our first Data Structure. The
Data structure allows us to store the data in an organized container and that
is why we need to use those square brackets because we need to contain the
data in one single unit. Data Structure is a very important concept because it
allows us to contain the information in an organized manner in one container
or box. If we use the print function and pass the list variable, we can see that
it will print all of the items in square brackets but how can we access these
individual items/elements from the list? Well, similar to Strings, we can use
the index position. The List indexes start from position zero, just like
Strings:
#individual items
print(my_list[0])
print(my_list1[1])
print(my_list2[2])
If we use an index that does not match the number of the list items, we will
end up getting an IndexError:
The IndexError indicates that the index we used is out of range. The way
the list items are stored in memory is in a sequence or one after another. We
can check the type of the list using the type function like this:
#list type
print(type(my_list))
If we want to know the actual number of items/elements we have in a list,
we can use the len() built-in function. The len() function will return the
actual number of items and don’t confuse them with indexes because they
start from zero and len() starts counting from 1:
#length of list
print(len(my_list))
List Slicing
We already saw how slicing works on Strings and we can do the same with
List items. We can define where the slicing should start and where it should
end. Same as with Strings, the last item will not be included in the output:
Syntax:
#list slicing:
listname[start:stop]
For example, let’s slice the first list and get the first 3 items:
#list slicing
print(my_list[0:3])
We can also include the step size if we want. Let’s try an example where the
step size is two:
# list slicing with stepover
my_list3 = [0,1,2,3,4,5,6,7,8,9,10]
print(my_list3[0::2])
The example above means the slicing should start from position zero, and
because the stop index is omitted, we are basically saying go till the end with
a step size of 2. Are Strings different from the Lists in Python? Yes, they are
different but they also share a lot of similarities. The biggest difference
between both is that Strings are immutable, this means that once a String is
created, we cannot change individual characters. Lists, however, are
mutable. This means that we can change the List individual items like this:
#Lists are mutable
my_list3[0] = -1
print(my_list3)
Same as with Strings, List slicing returns a result that we can store in a
variable and the slicing does not affect the original list. This is good when
we want to create a copy of the original list:
#copy of a list
string_list = ['one','two','three']
string_list_copy = string_list[0:]
print(string_list_copy)
As we can see, we have copied the first list into the new list successfully.
This means that after the copy is created, we can mutate some of the
elements in the string_list_copy without affecting the original list:
Great! The two lists are independent of each other; this means they are
stored in different locations in the memory. Most beginner developers will
try creating a copy of a list using the assignment operator like this:
#create a copy using assignment operator
string_list1 = ['one','two','three']
string_list_copy1 = string_list1
print(string_list_copy1)
Well, this looks like it’s working but it’s not. Now let’s modify the last
element of the newly copied list and print both lists at the same time:
#create a copy using the assignment operator
string_list1 = ['one','two','three']
string_list_copy1 = string_list1
string_list_copy1[2] = 'four'
print(string_list_copy1)
print(string_list1)
What is happening here? We have changed the string_list_copy1 and the
change affected the original list as well. This is happening because the
string_list_copy1 is not a copy of the string_list1 but a reference to the
string_list1. The variables are just a pointer to the values stored in the
memory. This means the string_list1 and string_list_copy1 point to the same
values in the memory:
As you can see, we have accessed the inner list items with the indexes we
passed in the second pair of square brackets. If we use a three-dimensional
list, we need to use another pair of square brackets if we want to access the
inner list items.
Lists Methods
Same as the String data type, Lists have its own methods and we need to use
the dot notation in order to call these methods. There are a few Lists
methods that we can use and here is the link to where you can read all about
them:
https://docs.python.org/3/tutorial/datastructures.html
We will cover the most used ones. Let’s start with the first one called
append()
append() method
We simply use this method to add an element at the end of the list:
# 1 append
my_list = ['one','two','three'];
my_list.append('four')
print(my_list); ['one', 'two', 'three', 'four']
It is important to know that this method will modify the existing array by
adding the new element at the end.
insert() method
This method will insert a new element into the list at the specified location.
This means we need to provide the position/index where we want the new
item to be inserted. For example, let’s add the element ‘five’ at the end of
my_list:
# 2 insert
my_list.insert(4,'five')
print(my_list)
Insert method, just like the append method, will add a new element to the list
at a specified index and will modify the original list.
extend() method
This method takes an iterable as an argument. An iterable means something
we can iterate or loop over. Extend method works in a way that it appends all
of the items from the iterable to the items in the original list. I know it
sounds confusing but here is an example:
# 3 extend
my_list.extend(['six','seven'])
print(my_list)
As we can see in our case, the iterable is a List of two items and it is called
iterable because we can loop or iterate over this List using loops that will be
covered in the later sections of this chapter.
pop() method
This method will remove an item from the end of the List and if we do this
on our existing list, it will pop/remove the item ‘seven’
# 4 pop
my_list.pop()
print(my_list)
If we want to remove a specific item from a list, we can call the pop()
method with an index of the item we want to remove, for example, let’s say
we want to remove the first item and we know this item index is going to be
zero:
# remove the first item
my_list.pop(0)
print(my_list)
remove() method
This method is used when we want to remove an item from a list based on
the item value. For example, let’s say we want to remove the item with the
value ‘four’:
#5 remove
my_list.remove('four')
print(my_list)
This method like the previous ones will modify the existing list but they
don’t return anything else after the operation is done. We can check this if
we wrap the remove method in a print function like this:
As we can see from the figure above, this method returns None but it
modifies the original list. If we try to print the other methods, we will see
that they don’t return any value as well but they change the original list.
clear() method
This method will clear or remove all of the items in the specified list
# 6 clear
my_list.clear()
print(my_list)
index() method
This method will return the position or index of the value we pass into the
method as an argument for:
#new list
new_list = ['one','two','three','four','five']
# 7 index
print(new_list.index('three'))
This simply means finding the item ‘three’ in the list and returning the index
where this item is. This method takes two additional/optional arguments.
These arguments are used when we want to speed up the searching process
because we are saying start the searching from this index zero and stop at
index 3:
# index method with 3 params
print(new_list.index('three',0,3))
As we can see from the figure above, the result will be the same but it will
be a lot faster because we are not searching the entire List to get the item
position. What will happen if the item we are looking for is not in the range
we specified? Well, it will throw a Value Error saying the item we are
looking for is not in the range:
print(new_list.index('three',0,2))
count() method
The count method will count how many times the same value occurs in the
list:
# 8 count
my_list1 = ['one','two','three','one','four']
print(my_list1.count('one'))
In the figure above, the count for the value ‘two’ is two because item two
can be found at position zero and 3 in the my_list1.
sort() method
In order to explain this method, let us create a List with items that are
random integer numbers:
#9 sort
numbers_list = [3,6,7,1,5,2,4]
numbers_list.sort()
print(numbers_list)
From the figure above, we can see that the un-ordered list is now sorted. In
Python, we also have the built-in function called sorted() and this function
produces a new array called the sorted array, but it will not affect the original
one like with the sort() method:
# sorted() function not a method
numbers_list1 = [3,6,7,1,5,2,4]
store_sorted =sorted(numbers_list1)
print(numbers_list1)
print(store_sorted)
This proves that the built-in function does not modify the existing list.
copy() method
We can use this method when we need a copy of some list:
#10 copy
numbers_list = [3, 6, 7, 1, 5, 2, 4]
new_list = numbers_list.copy()
print(new_list)
The new_list will be a copy from the numbers_list and if we want to add
insert or remove elements from the new_list, then this will not affect the
original numbers_list
reverse() method
This method will reverse the entire list without sorting it:
#11 reverse
numbers_list2 = [3, 6, 7, 1, 5, 2, 4]
numbers_list2.reverse()
print(numbers_list2)
This is the last method I wanted to show you but before we start learning
some new Python features, I would like to mention that you can combine
different List methods in your projects, for example, if you need a reversed
list but sorted you can call the sort() method first and then the reverse
method:
# combining different list methods
numbers_list3 = [3,6,7,1,5,2,4]
numbers_list3.sort()
numbers_list3.reverse()
print(numbers_list3)
.join()
This method will take all of the items from a list (iterable) and join the
values into one string. We also need to specify the separator as well:
#.join()
usernames = ['andy','carol','steve','jason']
joined_usernames = ', '.join(usernames)
print(joined_usernames);
List Unpacking
In JavaScript, this is known as the concept of destructuring, but in Python,
this is known as list unpacking and it’s very useful when we want the
values/items from the List to be stored in different variables:
# list unpacking
andy,carol,steve,jason = ['andy','carol','steve','jason']
print(andy)
print(carol)
print(steve)
print(jason)
As you can see now, the variables on the left side of the ‘=’ operator will
have the values from the list items. This is how we can unpack the list items
into variables. But we can unpack only a few values from a List and keep the
rest of the items stored in a single variable name as a list:
andy,carol, *rest = ['andy','carol','steve','jason']
print(andy)
print(carol)
print(rest)
I think we have covered a lot of the basic and some intermediate features of
Python in this section. Let’s learn the rest of the data types in Python.
None in Python (data type)
The None Python is a keyword and is used to define a null value or no value
at all. The None is also a data type from the class NoneType. The None
simply means absence of value, null value, or no value at all. In JavaScript,
we have Null and we have None. Some developers still don’t understand
that this None is not the same as 0 (zero) or empty string and it can be very
useful in some scenarios. For example, you want to create a program and at
the beginning of that program, you need a variable that is only declared but
without any value. We can assign the variables to None like this:
# None Keyword and Data Type
no_value = None
print(no_value)
print(type(no_value))
Before we move to the next section, I want you to remember this about the
None data type:
None is not the same as 0 (zero)
None is not the same as False
None is not an empty String ‘ ‘
Comparing None to None will return True
Comparing None to anything else will always return False
From the dictionary definition, we can see that the keys are on the left side
and values are on the right side of the colon. For example, ‘car’ is key and
‘Ford’ is value in the example above. So how we can access the values from
the dictionaries? Remember that in Lists we used indexes but here we need
to use keys:
# value Ford
print(this_dictionary["car"])
The dictionary is very similar to Objects in JavaScript. The values can be
from any data type, for example, the values can be a combination of integers,
floats, strings, lists, etc:
# dictionary with different data types as values
this_dictionary1 = {
"car": True,
"brands": ['Ford','Toyota', 'BMW'],
"year": 2022
}
print(this_dictionary1['brands'][1])
Now that we know about this new data type, we can go back to Python Lists
because I have something important to discuss. The lists can have values that
are from different data types, including the dictionaries:
# lists with dictionary as data type
new_list = [
{
"car": True,
"brands": ['Ford', 'Toyota', 'BMW'],
"year": 2022
},
{
"car": False,
"brands": ['Ranger', 'Kluger', 'X5'],
"year": 2022
}
]
If we want to access the first element of this list, we need to write this query:
print(new_list[0])
If we want to find what values the key ‘car’ have, we can do this:
print(new_list[0]['car'])
We can also get the car brands:
print(new_list[0]['brands'])
And from this result, I want to get the ‘BMW’ brand using the index number
2
print(new_list[0]['brands'][2])
I know this is getting complex but I can assure you that if you spend some
time thinking about Lists and Dictionaries, you will see that it is very easy.
Another important thing I want to discuss is the dictionary values. The
dictionary values can be from any data type but dictionary keys cannot be
mutable. That is why we have used String as key because we know strings
are immutable. We can also use numbers and Boolean because they are
immutable as well but Python Lists cannot be used as dictionary keys
because they are mutable. For this reason, we cannot use Lists as keys in
dictionaries. The keys are mostly going to be strings that are always
descriptive. The key in the dictionary has to be unique, no duplication of
keys should be allowed like in this example:
this_dictionary1 = {
"car": True,
"brands": ['Ford','Toyota', 'BMW'],
"car": 2022
}
As we can see, the ‘car’ is the key and it’s used in two different places. We
want to avoid getting errors like this because they will crush the program.
There is another way we can create a dictionary using the following syntax:
#second way of creating new dictionary
new_dict = dict(car = 'Tesla')
print(new_dict)
In the above example, we are using the built-in Python function called dict()
to create a new dictionary and this syntax is not that common.
Dictionary – methods
In this section, we will go through some methods that we can use on
dictionaries. Before we delve into the dictionary methods, let’s first talk
about how can we use the ‘in’ keyword. The ‘in’ keyword can be used to
check the existence of a particular key in the dictionary. The ‘in’ keyword is
used to check the key not the value:
this_dictionary1 = {
"car": True,
"brands": ['Ford','Toyota', 'BMW'],
"year": 2022
}
print('year' in this_dictionary1)
print(2022 in this_dictionary1)
If the key doesn’t exist, it will return False like in the figure above because I
have tried to find the year 2022 but there is no ‘key’ 2022. Let’s start with
some useful dictionary methods.
get() method
The get method will take one parameter and this parameter will be the ‘key’
from a dictionary. The get method will return the value of that key:
print(this_dictionary1.get('car'))
If the key we are trying to get does not exist, we will get None. The get
method can accept a second parameter called value:
dictionary.get(keyname, value)
From the example above, the ‘cars’ key does not exist in the dictionary but
will return a list of two items (Ford and Audi) because we provided the
second parameter:
keys()
This dictionary method is used when we want to get all of the keys that exist
in the dictionary. This method will return a view object. This object will
contain all of the keys in the dictionary:
#keys method
print(this_dictionary1.keys())
We can combine the keys() method with the ‘in’ keyword and this will return
Boolean True and False:
# find a key from the view object
print('car' in this_dictionary1.keys())
values() method
This method will return a view object as a List with all of the values in the
dictionary:
# values method
print(this_dictionary1.values())
Same as with the keys, the values method can be combined with the ‘in’
keyword to check if any of the values exists in the dictionary.
print(2022 in this_dictionary1.values())
items() method
The items method is a little bit more complicated to understand because we
haven’t used tuples (another data type) yet. This method will return a view
object that contains a key-values pair of the dictionary in a form of Tuples.
We will talk about tuples soon:
#items method
print(this_dictionary1.items())
clear()
This method will remove all of the key/value pairs from the dictionary. The
clear method does not require anything to be passed as an argument and it
will return the empty dictionary:
# clear method
print(this_dictionary1.clear())
print(this_dictionary1)
copy()
You can use this method to create a copy of a dictionary and the copied
dictionary will be independent of the original dictionary:
#copy method
this_dictionary2 = {
"car": True,
"brands": ['Ford', 'Toyota', 'BMW'],
"year": 2022
}
dict_copy = this_dictionary2.copy()
print(dict_copy)
pop()
This method takes one argument and that is a dictionary key. The pop()
method will remove a value from the dictionary based on the key. If we try
to print this method, it will return the item it was removed from in the
dictionary:
#pop method
print(this_dictionary2.pop('year'))
print(this_dictionary2)
popitem()
The popitem() is used to pop or remove the last key:value pair from the
dictionary. In the previous Python versions, this method removed a random
key:value pair but now it will remove the last.
this_dictionary2 = {
"car": True,
"brands": ['Ford', 'Toyota', 'BMW'],
"year": 2022
}
#popitem
this_dictionary2.popitem()
print(this_dictionary2)
update()
This method is very useful if we want to update a value based on a specified
key. Let’s update the year to 2023:
#update
print(this_dictionary2.update({'year':2023}))
print(this_dictionary2)
What will happen if we try to update a value with a wrong key that doesn’t
exist in the original dictionary? You need to be careful in this case because
the update method will look into the dictionary and if it cannot find the key,
it will think we want to insert a new key/value pair. Let’s say we want to
update the ‘car’ key to a new value ‘False’ but we make a spelling mistake
and instead of the key ‘car’ we typed ‘cars’, this will happen:
print(this_dictionary2.update({'cars':False}))
As we can see, we have the new key/value pair ‘car’:False added at the end
of the dictionary and this is not the result we wanted. That is why you need
to be careful when trying to use the update method.
setdefault() method
The setdefefault () method will return the value of a key if that key is in the
dictionary. If the key cannot be found, this method will insert new key/value
pair
#setdefault
year = this_dictionary2.setdefault('year')
print(year)
In tuples, we use the round brackets instead of the curly brackets we used in
dictionaries {}. A tuple is a collection of data that is ordered and we cannot
change the order of items once the tuple is created. We have already printed
the tuple but if we want to access the individual items, we need to use their
indexes. Same as with Lists, the indexes start from position zero. Same as
lists, to access the items, we need to use the square brackets where we pass
the index position:
#access items
print(new_tuple[2])
Let’s check if the tuples are indeed immutable. In the following example,
let’s modify the element in the position/index 2:
# modify tuples
new_tuple[2] = 4
print(new_tuple)
This is how we can access the values from a dictionary when we have Tuple
as a key:
print(a)
print(b)
print(c)
print(rest)
From the figure above, we can see that the last item (rest) we print is not a
Tuple, it’s a List and we can confirm this if we try to mutate one of its
elements like the last value 6 to a new value 7:
#modify the last rest item because it is a list
rest[1]= 7
print(rest)
Tuple methods
Let’s demonstrate some of the tuple methods. For now, we have only two
(count and index).
Count()
It is a very easy method because it will return how many times the same
value occurs in the Tuple collection.
#count
new_tuple = (1,2,3,4,5,6,1)
print(new_tuple.count(1))
index()
The index method will return the index or position of the element:
#index
print(new_tuple.index(3))
The element 3 index is two, but what will happen if we look for a value that
occurs multiple times in the Tuple? What index will be returned?
When we try index(1), the index will be 0. The same element is in index six
but the index method will return index zero because that is the first
occurrence of the value.
Sets in Python (data type and data structure)
The last data structure in Python is Set. Sets in Python are an unordered
collection of unique elements/objects. Sets are iterable collection that does
not contain duplicate elements. We can create Sets using the curly brackets
just like dictionaries:
# Sets
new_set = {1, 2, 3, 4, 5, 6, 7}
print(new_set)
We can add new items to the collection using the add() method:
new_set = {1, 2, 3, 4, 5, 6, 7}
new_set.add(8)
print(new_set)
The values in the Sets are located in the memory without any specific order
but that will not cause problems when we access them because Sets do not
contain duplicate values.
If for example we have duplicate values in the Set, the len() function will
give us the length of the items without the duplicates because it will count
only the unique values.
clear() method
This method will remove all of the elements from a Set
# clear
new_set = {1, 2, 3, 4, 5, 6, 7, 7}
new_set.clear()
print(new_set)
difference() method
This is a very useful method that returns the difference between two or more
sets as a new set. This does not mean it will include all of the different items
from both sets. It means it will return a new set with items that exist in the
first/original set:
# difference
new_set = {1, 2, 3, 4, 5, 6, 7}
new_set1 = {5, 6, 7, 8, 9, 10, 11}
print(new_set.difference(new_set1))
As you can see, it will return a new set that includes the different items from
the new set and will not include the duplicate items for both Sets.
difference_update() method
This method will modify the existing set. We use this method when we want
to remove the items that exist in both sets. The difference() method returned
a new set without the unwanted items and the difference_update method will
only remove the unwanted items from the original set
# difference_update() method
new_set = {1, 2, 3, 4, 5, 6, 7}
new_set1 = {5, 6, 7, 8, 9, 10, 11}
new_set.difference_update(new_set1)
print(new_set)
discard() method
This method will remove or discard the specified item from the Set:
# discard() method
new_set = {1, 2, 3, 4, 5, 6, 7}
new_set.discard(7)
print(new_set)
intersection () method
Returns a new Set of items that contain the items that are the same between
two or more sets. The new set will have items that exist in both sets:
# intersection method
new_set = {1, 2, 3, 4, 5, 6, 7}
new_set1 = {5, 6, 7, 8, 9, 10, 11}
print(new_set.intersection(new_set1))
If we remove the intersection method and use this shorthand symbol ‘&’ the
result will be the same:
print(new_set & new_set1)
intersection_update() method
This method will remove the items from the first Set that are not present in
the second Set and will update the original first set:
# intersection_update method
new_set = {1, 2, 3, 4, 5, 6, 7}
new_set1 = {5, 6, 7, 8, 9, 10, 11}
new_set.intersection_update(new_set1)
print(new_set)
isdisjoint() method
This method will return either Boolean True or False as a result. It will
return a True only if the Sets we are comparing have different items and it
will return False if at least one of the items from one Set can be found in the
second Set.
# isdisjoint() method
new_set = {1, 2, 3, 4, 5, 6, 7}
new_set1 = {5, 6, 7, 8, 9, 10, 11}
print(new_set.isdisjoint(new_set1))
It returned False because 5,6,7 are the items that are present in both sets.
# isdisjoint() method
new_set = {1, 2, 3, 4}
new_set1 = {5, 6, 7, 8, 9, 10, 11}
print(new_set.isdisjoint(new_set1))
As we can see, the isdisjoint method returns True because none of the items
in both Sets are the same.
Union()
A very easy method to understand because it returns a new Set that contains
all of the items from the original and specified set. Union of all items.
# union method
new_set = {1, 2, 3, 4, 5, 6, 7}
new_set1 = {5, 6, 7, 8, 9, 10, 11}
print(new_set.union(new_set1))
As we can see, the union method creates a new Set with all of the items from
both Sets but it excludes the duplicates. The union method can be replaced
with the straight line | that we can write if we press shift + backslash and the
result will be exactly the same:
print(new_set | new_set1)
issubset()
This method returns Boolean True or False as a result. It will return True if
all of the items that are in the first/original Set are included in the second
Set:
# issubset()
new_set = {5, 6, 7}
new_set1 = {5, 6, 7, 8, 9, 10, 11}
print(new_set.issubset(new_set1))
Issuperset()
This method will return Boolean True if all of the items from the
specified/second Set exist in the original/first Set, otherwise it will return
Boolean False.
# issuperset()
new_set = {5, 6, 7}
new_set1 = {5, 6, 7, 8, 9, 10, 11}
print(new_set.issuperset(new_set1))
So far, the way we executed the Python code was line by line, or as most
developers call it, sequential code execution. In this chapter, we will learn
how to write a code that based on a given condition will skip some
statements and execute other statements. This will make our program
smarter because it will be capable of making choices and decisions. In the
previous chapter, we mentioned that using the loops we can iterate or loop
over iterable such as Strings and Lists, therefore, we will use loops to
execute a series of statements several times, repetitively. Let’s start with
learning the control structures first.
Control Structures
The control structures will direct the order of execution of the statements in
our program. This means that we can break the current flow of executing the
statements. In the previous chapter, we learned about the Boolean data types
(True and False) and in this chapter, we will see how useful they are for the
control structures. The first control structure we will discuss is called if-
statement.
If-statement
This statement is one of the most common statements we can use to control
the flow. To start using the if-statement, we need to use the keyword ‘if’, and
here is the if-statement syntax:
if <expr>:
<statement>
As we can see from the syntax above, after the ‘if’ keyword, there is an
expression or a condition. The condition is evaluated to Boolean True or
False and based on the condition appropriate statement/statements are
executed. Let us create a variable called is_developer which will have a
Boolean value of True:
is_developer = True
This variable will always be evaluated to Boolean True so we can write out
the first if-statement like this:
if is_developer:
print('Wow this must be super exciting!')
Make sure that after the condition/expression, you always add the colon ( : ).
The print function, as you can see, is indented and this means it belongs to
the if-statement body. In the body, we can have one or more lines of code or
statements. Make sure the line after the if is_developer is always indented
because the Python Interpreter relies on indentation to determine the scope
of the code or where the code belongs. If we don’t have a proper indentation,
it will throw an error. In JavaScript, the same if-statement can be written like
this:
If(is_developer){
console.log('Wow this must be super exciting!');
}
In JavaScript, we use curly brackets to define the scope or the body but if we
have only one line of code, the curly brackets can be omitted. Let’s get back
to Python if-statement. How does this statement actually work? Basically,
after the ‘if’ keyword we need to set the condition and it can be very simple
like in our example, or much more complex. The condition must be
evaluated to True in order to execute the statement/statements in the body.
The body is the statement/statements that go right below the if-statement and
it must be indented to indicate that the body belongs to the above if-
statement. Now when the interpreter reads the first line with the if keyword,
it will check if the variable is_developer is True, and in our case, the
is_developer will always be True because we initialized the variable before
the if-statement. Because the condition is true, the interpreter will know that
next it should go into the body and execute the statement. In our case, we
have only one print() function in the body but as I mentioned, we can have
multiple lines of code/statements. When the interpreter executes the
statement/statements in the body, after that, it will exit the if-block. Let us
add one more line after the if-block but this time without indentation and run
this code in:
# If statements
is_developer = True
if is_developer:
print('Wow this must be super exciting!')
print('New line')
The second print() does not have an indentation and this will tell the Python
interpreter that the second print() function does not belong in the if code
block. Notice that in line two from the figure above, next to the if keyword
there is a black triangle facing down so we can click on it and observe what
will happen:
As you can see now, the body is not visible anymore because it belongs to
this if-block and if we click it again, we will get the body of the if-statement.
These are small things that you should know and they are very important. In
the figure below, you will see all of the keywords that we learned so far:
if-else statement
The if-else statement is when we want to set an alternative path, for
example, if the condition is evaluated to true, execute the
statement/statements inside the body or if the condition is evaluated to False,
execute the statements in the else clause. The syntax of the if-else statement
is:
if < expr >:
<statement(s) >
else:
<statement(s) >
Let’s rewrite the previous if-block example to include the else clause, but
this time we need to make the condition be evaluated to False:
# if-else
is_developer = False
if is_developer:
print('Wow this must be super exciting!')
else:
print('What is your profession then?')
We can see that the else clause statement/statements are executed because
the if condition was evaluated to False. Now we have two options based on
one condition. If the condition is evaluated to True, run the statements in the
body or if the condition is evaluated to False, run the statements in the else
clause. In both cases, the indentation and colon are very important.
elif
The if-else statement is great but it only covers two scenarios - if the
condition is true, do this and if the condition is false, execute the else clause
statements. We can do much more than just these two operations because
there is a syntax that allows us to define more than two options. The name of
this is elif conditional statement, a shorthand for else-if. The syntax for elif
is:
if < expr >:
<statement(s) >
elif < expr >:
<statement(s) >
elif < expr >:
<statement(s) >
...
else:
<statement(s) >
Maybe the syntax does look a bit confusing at first but it is not that hard in
reality. The Python interpreter will evaluate the if-expression first, and if
it’s True, it will execute the code below the if-statement, and if the
condition is evaluated to False, it will go to the first elif statement where we
have another condition. The Interpreter will evaluate the condition (in the
elif statement) to True or False and if it’s True, then it will execute the
statements inside the elif block and if the condition is False, it will jump out
and go to the next elif statement where there is another condition. Finally, if
every condition is evaluated to False, the final else-clause
statement/statements will be executed.
Here is one example:
# elif
is_developer = False
is_employed = True
if is_developer:
print('Wow this must be super exciting!')
elif is_employed:
print('Great at least you have a job :)')
else:
print('What is your profession then?')
Let’s explain how the above example is working. The if-condition, as we can
see, will be evaluated to False because the is_developer is set to False and
then it will go to the next elif-statement where the condition will be
evaluated to True. Because the condition in the elif is True, the statement
inside the elif-block will be executed. Inside the elif block, we have only one
line of code or a simple message that gets printed. If we set both of the if-
statement and elif-statement conditions to False and run this code again, we
have:
The else clause statement will be executed. So far in the if and elif
conditions, we have used simple Boolean values that are either True or
False, and this is okay and normal but the condition itself can be a much
more complex expression like this:
x = 10
if x > 11:
print('The value of x is smaller then 11')
elif x > 5:
print('The value of x is bigger than 5')
else:
print('The value of x is undefined')
Understanding Python Indentation
Indentation in Python is very important and it refers to the spaces at the
beginning of the code. If you are a JavaScript developer, then you know the
spacing is not that big of a deal but we (developers) do it anyway just to
style our code. Proper indentation makes the code much more readable and
easier to understand. In Python, the indentation will indicate that the code is
different from the one above, belongs to a different block, and have its own
scope. You will get an error if you avoid indention. The number of empty
spaces is not important as long as there is at least one. If you use more than
one space, make sure you apply the same number of spaces throughout the
entire file, otherwise, you will get an error called IndentationError or
unexpected indent:
As you can see from the figure above, the code below the last else-clause is
without indentation and the Interpreter will underline it in red and throw an
error if we try to run the code. Another interesting thing is that developers
prefer making the indentation by pressing the tab key or simply by adding
multiple spaces. The number of spaces they put is usually four but so far in
replit.com, there are two spaces added for us. This can be changed in the
settings, where you can choose the indent type (spaces or tabs) and set the
indent size:
If you want four spaces to be added, then set the indent type to spaces and
increase the indent size from two to four like in the following figure:
As you can see, indentation is very important in Python.
Python Operators
Operators are very important in all programming languages. Python
classifies the operators into different groups:
Arithmetic
Assignment
Comparison
Logical
Identity
Membership
Bitwise
Arithmetic Operators
The arithmetic operators are used with numbers so we can do some
mathematical calculations:
Assignment Operators
As we know, assignment operators are used when we want to assign a value
to a variable. We have covered the basic assignment operators and
augmented assignment operators in the previous chapter because they play a
very special role in Phyton programming. The function of the augmented
assignment operator is to combine the functionality of the arithmetic and
bitwise operator with the assignment operator. Augmented assignment
operators are nothing but short syntax so we can perform a binary operation
and assign the results to one of the operands.
For example:
a += 7
The above expression is the same as the following one:
a=a+7
Operator Description Example Same as
= Assign; The a=3 a= 3
value of the
right side of
the equal ‘=’
will be
assigned to
the left side
operand
+= Add and a += 2 a = a +2
Assign; This
will add the
right-side
operand with
the left-side
operand and
will then
assign value
to the left
operand
-= Subtract and a -= 2 a=a–2
Assign; This
will subtract
the right-side
operand from
the left-side
operand and
then assign
the value to
the left
operand.
*= Multiply a *=4 a=a*4
AND;
Multiply the
right-side
operand with
the left-side
operand and
then assign
the value to
the left
operand
/= Divide AND; a /= 3 a=a/3
Divide the
left-side
operand with
the operand
on the right-
side and then
assign the
value to the
left operand
%= Modulus a %= 6 a=a%6
AND; This
will take
modulus from
the left and
right operands
and assign the
result to the
left operand
//= Divide floor; a //= 4 a = a // 4
Divide the left
operand with
the right
operand and
then assign
the floor value
to the left
operand
**= Exponent; a **= 4 a= a ** 4
Calculate the
exponent
using
operands and
assign the
value to the
left operand
&= This operator a &=2 a=a&2
will perform
bitwise AND
on operands
and assign the
value to the
left operands
|= This operator a |= 3 a=a|3
will perform
bitwise OR on
operands and
assign the
value to the
left operand
^= Perform a ^= 3 a=a^3
bitwise xOR
on operands
and assign the
value to the
left operand
>>= Perform a >>= 3 a = a >> 3
bitwise right
shift on
operands and
assign value
to the left
operand
<<= Perform a <<=3 a = a << 3
bitwise left
shift on
operands and
assign value
to the left
operand
#Output: 7
bitwise OR |=
m=5
n=3
m |= n
print(m)
bitwise XOR ^=
m=5
n=3
m ^= n
print(m)
# Output:6
Comparison Operators
We can use the comparison operators when we need to compare two values:
Operator Name Example Output
== Equal x=3 False; because 3
y=4 is not equal to 4
print(x == y)
> Greater than x=4 True; because 4
is greater than 3
y=3
print(x > y)
< Less than x=4 False; because 4
y=3 is not less than 3
print(x < y)
>= Greater than or x=4 True; because 4
equal to y=3 is greater or
print(x >= y) equal to 3
<= Less than or x=3 True; because 3
equal to y=3 is greater or
print(x<= y) equal to 3
!= Not equal x=4 True; because 4
y=3 is not equal to 3
print(x != y)
The Code:
# Comparison Operators
x=4
y=3
# == equal
print(x == y)
# Output False
# != Not Equal
print(x != y)
# Output True
Logical operators
The logical operators are very useful because they give us the option to
check multiple conditions at the same time. The logical operators are used to
combine conditional statements:
Operator Description Example Output
and If both a=4 True; because 4
statements are print(a > 3 and a is greater than 3
true, it will <10) AND 4 is less
return true than 10
or Only one of the a=4 True; because
statements needs print(a > 3 and a we only need
to be true to >10) one of the
return True statements to be
true and 4 is
greater than 3 is
evaluated to
True, therefore,
the whole
expression is
evaluated to
True
not Reverse the a=4 False; The inner
result, if it’s print(not(a > 3 expression
True will and a >10)) within the ‘not’
convert to False keyword is
and vice versa evaluated to
True but the
‘not’ keyword
will reverse the
result, therefore,
the final return
will be False
I know the table is self-explanatory but let’s see how we can use the above
operators with conditional statements like the if-statement. Let’s create a
program that will tell us if we can legally drive a car based on our age. For
example, let’s say we have a driver’s license and we are 16 years of age so
we are allowed to drive:
my_age = 16
is_licenced = True
if is_licenced and my_age >= 16:
print('Hop on for a ride')
else:
print('Sorry buddy, I\'m driving today!')
As you can see from the code above, I have used the logical ‘and’ operator
in conjunction with the comparison operator to evaluate the if-condition.
Both sides (left and right) of the ‘and’ logical operator have to be true so the
entire expression can be evaluated to True. We know that is_licenced will be
evaluated to True immediately but for the second part of the expression, we
have used the comparison operator to check whether the driver’s age is
greater than or equal to 16 years. I hope you can see the power of the
operators. If the logical operator in the middle is changed to the ‘or’
operator, then only one of the two statements (left or right) will need to be
evaluated to True for the entire condition to be evaluated to true. Let’s
change just the variable my_age to have a value of 15:
As we can see, because the is_licenced is evaluated to True, the entire
condition will be evaluated as True and therefore the code in the if-block
will be executed.
The output will be True because 6 is greater than 4. But can you guess the
output of this example?
m = 10
if m:
print("True")
else:
print("False")
The output is True, but why? As you can see in the condition, we don’t
actually have an expression so it can be evaluated to either True or False,
just a single variable ‘m’ which has a value of 10 assigned before the if-
statement. The question is, why is this variable in the condition evaluated to
True? The answer lies in the Truthy and Falsy values. In Python, in order for
a value to be evaluated to True or False, it does not have to be part of an
expression and this is based on some rules that are built-in in the language,
and of course, the Interpreter knows these rules. In the following section, I
will discuss the Boolean Context.
Boolean Context
The Boolean context is very important because it requires a value to be in
one of the two states, True and False, therefore, any statement or a value we
have in the if-condition must be converted before in the background to True
or False. I know this can be confusing but in Python, by default, all of the
values are considered truthy except for the following list of values which are
always ‘falsy’:
Constant False
Constant None
Zero (0) of any numeric type
Float 0.0
Complex Oj
Empty list []
Empty tuples ()
Empty dictionaries {}
Empty Sets sets()
Empty strings “”
Empty range(0)
Let’s run the above list of values and check if they are actually considered as
falsy:
The above figure shows how the ternary operator is used to assign a value to
the is_allowed variable based on a given condition (is_old).
The above figure is when the is_old variable is set to Boolean False and that
will make the if-condition be evaluated to False, therefore the else-clause
message will be assigned as a value to the variable is_allowed. As we can
see, ternary operators is not that scary, but it takes time to get used to this
syntax.
Short-Circuiting
In order to explain how short-circuiting works in Python, I would use one of
the previous examples with the driver’s license.
The if condition will be evaluated to True if only one of is_licenced or
my_age >=16 statements are evaluated to true. Short-circuiting is essential
because it will save time and increase our program performance. Therefore,
when the Python interpreter evaluates the expression that involves the ‘or’
operator, it will stop immediately one of the operands returns True. Let’s go
line by line and explain how the Python interpreter works:
1) my_age = 16; The interpreter will assign a value of 16 to the variable
my_age in the first statement
2) The second statement is assigning the value True to the is_licenced
variable
3) The third statement is the if-block:
Now the interpreter will evaluate what is after the if keyword and the
first statement is the is_licenced. Obviously, this one will immediately
be evaluated to True and the Interpreter will go and evaluate what is
next but when it sees the ‘or’ operator, the Interpreter will stop
because the first operand already returned true.
4) Then it will execute the print() function below the if block
This is how short-circuiting works in Python, it saves time and increases the
performance because it can skip the evaluation of the next expression
because at least one returned True.
a = ["apple"]
b = ["apple"]
c=a
print (a is c)
# this returns true because a is same object as c
If we try to check if the list/object ‘a’ is equal to object ‘b’ then the result
will be false because ‘a’ is not the same object as ‘b’:
print (a is b)
print(a is not c)
#this returns false because a is the same as object c
print(a is not b)
# this will return true because object a is not same as the object as b even if
they have the same content/values
print(a != b)
# The comparison operator != will return false because a is equal to b
The output will be False because the empty Strings in Python are considered
as Falsy value or Boolean False and Integer 1 will be evaluated as True, so
we are trying to check for equality between (False == True) and that is why
we got False in the first place:
#2 checking for equality
print(' ' == 1)
print(bool('') == bool(1))
print(False == True)
When we use double equality, the best scenario is to compare values that
belong to the same data type but if they are different, then let’s hope that
Python using the truthy and falsy values rules will solve the problem for us.
Now you know that the equality operator will check for the equality of the
values. On the other hand, the ‘is’ operator will check if the location in
memory where the value is stored is exactly the same between both of the
objects. The ‘is’ operator will not perform type conversion using the bool()
function. Let’s go over a few more examples and see how the ‘is’ is working
compared to ==:
# is vs ==
print(True is True)
print([1, 2] is [1, 2])
And the output is:
The expression (True is True) will be evaluated as True because Boolean
True values will always have only one memory location and it could never
be changed regardless of what we do in our code. The second print will give
us False output, but why? Both Lists look the same. Those two Lists have
the exact same values but every time a new List is created, the values are
stored somewhere in the memory in a new location. Both Lists are in
different locations in memory and although they have the same values, they
are not the same. Remember Lists are not only data type, they are data
structures as well. This means more memory is required to store a data
structure like Lists compared to other data types like Strings, Numbers, and
Boolean values.
Please consider the following example:
list1 = [1,2]
list2 = list1
print(list1 is list2)
The output will be True. The list1 is created and the values are stored
somewhere in the memory in some location. In the next line, we are saying
that list2 is equal to list1. This simply means that list2 will not create a new
memory to store the same values because they are already stored by list1.
Both list1 and list2 will point to the same values stored in the memory. Now
if we change the list2 and add a new item, list1 will also be affected because
they both points to the same values stored in the memory:
list1 = [1,2]
list2 = list1
print(list1 is list2)
list2.append(3)
print(list1)
print(list1)
I hope you now understand the difference between the ‘is’ operator and the
‘==’ operator.
Membership Operators
In Python, we use the membership operator to test if an item is present in the
object. We have used this ‘in’ keyword in our examples but now you know
they are called membership operators:
Operator Description Example Output
in This will return #in True, because
true only if the a = ["first", the ‘second’ is
sequence with "second"] present in the
the specified print("second" list
value is found in in a)
the object
not in This will return #not in True, because
true only if the a = ["first", the item ‘three’
sequence with "second"] is not found or
the specified print("third" not present in the
value is not in a) list
found in the
object
Python Loops
Here comes one of the most interesting features of any programming
language in general. So far, we have covered control structures and operators
that will direct the order of execution of the statements. Loops are very
powerful because based on a specified condition we can make a program to
execute a block of code over and over as long as the condition is True. This
is very powerful because we can run these loops as many times as we need,
and the machines are not going to be tired of repeating the same instructions
because they are not humans. Loops are one of the most essential features
you should know. One of the loops we are going to learn is called a ‘for’
loop and in order to create one, we need to use the ‘for’ keyword.
Here is the for-loop syntax:
for val in sequence:
loop body
In the syntax above, we have used the ‘for’ keyword which is a must and
after the for-keyword, we have a variable name. The variable name can be
any name you want but should be appropriate for the loop. Then we have the
‘in’ keyword and sequence of items. The variable ‘val’ which can be called
‘item’ will be created for each item in the sequence. Remember we
mentioned in the previous chapter that Strings or Lists are iterable
sequences. This means we can iterate over each item inside that sequence.
Let’s do a very basic example where we iterate over a simple sequence of
characters in the String:
As you can see, in the above example, I have used ‘char’ as the variable
name. I decided to name it ‘char’ because it symbolizes each of the
individual characters in the sequence ‘Your name is?’ And it will print each
character in the iterable sequence in order. We can loop Lists as well using
the for-loop:
# Iterate a List
list1 = [1, 3, 5, 7, 9]
for item in list1:
print(item)
Let’s check another example where we loop over the items in a Set:
# Iterate over a Set
set1 = {"apple", "banana", "cherry"}
for fruit in set1:
print(fruit)
We can loop over the dictionaries as well but we will do this in some of the
next sections. As you can see, the for-loops syntax is similar to the if-
statements. With the for-loops, we can loop over a collection of data like
lists, strings, tuples, sets, etc. Same as the if-statements, we need to use
colon ‘:’ after the collection and the code needs to be properly indented. The
indentation is important because it will indicate to the Interpreter that the
following code belongs to the for-block. It is important to know that when
the last item from the collection is reached, the loop will terminate and the
Interpreter will jump out of the loop and will start executing the code that
comes after the for-loop. Let’s use the for-loop to calculate the sum of all of
the items from a list:
# List of numbers
numbers = [1, 2, 4, 6, 5, 8, 11, 3, 14]
Nesting loops
Nested loops are very important because they allow us to nest a loop within
another loop as long as there is a proper indentation. Here is one example of
nesting two for-loops:
# nested for-loops
numbers1 = [1, 2, 3]
letters1 = ['x', 'l']
The output illustrates that in the first iteration of the outer loop, the value
that is printed is 1, which then triggers the inner for-loop and this one will
print the letters ‘x’ and ‘l’ consecutively. Once the last letters of the inner
loop are completed the program will go back to the top of the outer loop
and it will print 2. After printing the value 2, the inner loop runs again and
will print the letters ‘x,’‘l,’ and so on. We can also nest if-statements like
this:
# nested if statements
grade = 80
if grade >= 60:
if grade >= 90:
print("A")
else:
print("Your grade is F")
While Loop
In Python, we have two primitive loops, one is the for-loop we have already
discussed and the second one is the while loop. With the while loop, we can
execute a set of statements as long as the condition we set in the while loop
is evaluated to True.
Syntax:
while test_expression:
Body of while
The syntax of the while loop is very basic and if you understood how the
for-loop works, the while loop will be much easier to understand. So why
do we need the while loop when we have the for-loop? Well, we can use
this loop when we don’t know the exact number of times we need to iterate
or loop. The condition in the while loop is evaluated at the beginning before
the loop can start. The body of the while loop will be executed only if the
condition is True. It is important to know that after each iteration, the test
expression (condition) is checked again. This loop will stop when the
expression evaluates to False. The body of the while-loop in Python is
determined by indentation and in other programming languages, this can be
done using curly brackets like in JavaScript. Let’s go over this example:
# while loop
When looping over a dictionary, the values that are returned will be the keys
from the dictionary data type. But we can use other methods that will return
both keys and values. These methods are popular and used by the
developers:
items() method
This method will get the key/value pairs and return them as a Tuple:
# items method
for item in ford.items():
print(item)
values() method
This method will give us only the values from the dictionary, not the keys:
# values method
for item in ford.values():
print(item)
keys() method
The normal for-loop does the same job as this method, meaning it will only
return the keys of a dictionary but I guess it’s more descriptive like this:
# keys method
for item in ford.keys():
print(item)
But what if we want to get the key/value pairs without getting a Tuple
back? There is a simple trick we can do using the power of unpacking but
we need to use the items() method because it will give us access to both
keys and values:
# unpack the values and keys
for item in ford.items():
key,value = item
print(item)
Another way to get the keys and values from the dictionary is to change the
syntax of the for loop a bit:
# we can get keys & values like this
for key,value in ford.items():
print(key,value)
The last syntax is very common among developers. Before we wrap up how
to loop the dictionaries, I would like to mention that naming the variables
(key, value) is up to you. I have used something that is descriptive but even
this code is perfectly okay and executable:
for k,v in ford.items():
print(k,v)
As you can see, the range can take 3 parameters, the first one is called start
and is optional. This would be an integer number to specify the start
position. If omitted, the default will be zero (0). The second parameter is
the ‘stop,’ and this one is required and will be an integer number that will
specify the end. The final parameter is called ‘step’ and is the same as the
‘start’ parameter, this one is optional as well and it will specify the step
size. The default step size is one. We know that from the parameters the one
in the middle called stop is required so let’s print what this function does
when we supply only one parameter:
# range function
print(range(10))
From the figure above we now have a range that starts from zero and ends
at ten and although we haven’t specified the start, it was added for us. The
range() is very popular when it’s used in conjunction with loops:
# range() with for-loop
for number in range(0, 10):
print(number)
From the figure above, we can see that we have used the range function so
the for-loop can run 10 times. Instead of specifying the variable name like
‘number,’ we can use ‘_’ underscore and it will still be valid:
This means we don’t care about naming a variable but we need the loop to
run a specific number of times specified in the range function. We can even
specify the stepover parameter:
# stepover
for _ in range(0, 10, 2):
print(_)
Enumerate ()
Similar to the range () function, we have a function called enumerate(). The
enumerate () function will take a collection like a Tuple and will return it as
enumerate object. This function will also add a counter/index as the key to
each item in the set. This means that the function allows us to iterate
through a sequence but it will keep track of both element and index. For
example, let’s pass a Tuple collection into this new function:
# enumerate ()
x = ('first', 'second', 'third')
y = enumerate(x)
for item in y:
print(item)
As we can see, the enumerate() function took an iterable as a parameter and
it returned an object with an index which is the key, and now the Tuples
have access to indexes just like the Lists. Instead of printing just the
variable ‘item,’ we can do the following:
for key, item in y:
print(key, item)
Excellent! Now we got back not just the value of the collection but the
index as well. We can achieve the same thing with Strings and Lists:
# Strings
for key, item in enumerate('Rick'):
print(key, item)
# Lists
for key, item in enumerate([1, 2, 3]):
print(key, item)
As we can see, the enumerate() function is very useful because we can
iterate over the collection plus we have access to the index and the value.
# continue
for brand in brands:
if brand == 'bmw':
continue
print(brand)
print('If you read this line the loop is finished')
The third statement is called ‘pass statement’ and it is rarely used because it
doesn’t do anything in particular. The pass statement will be used as a
placeholder for future code. For example, when the pass statement is
executed, it will not do anything but you can avoid getting an error because
an empty code is not allowed. I know this is confusing but let’s check the
following example:
#pass
is_old = True
if is_old:
#no code
print('This will give us an error')
As you can see, we got IndentationError because there is no code after the
if-statement and the Interpreter expects us to finish the if-statement and
write some code. The Interpreter will look into the next line (5), see that
there is no indentation, and will throw an error because the Interpreter is
confused and thinks the print message should go in the body of the if-
statement. Therefore, when we don’t know what to write in the if-block, we
can use the pass statement:
#pass
is_old = True
if is_old:
#no code
pass
print('This will give us an error')
Practice Time
Now is the right time to practice some of the things we have covered in this
chapter and this includes the loops, conditionals, and methods. The exercise
will be to clean a dirty. A dirty list is a list with duplicate values and our
program should create a clean list without any duplicates. Before you see
my solution, I would like you to think, and even attempt to write your own
solution. Here is the dirty list:
#chapter-2 exercise
dirty_list = [1, 2, 3, 2, 4, 1, 5, 6, 5, 5, 7, 8, 1, 9, 10, 9, 8, 3];
Solution:
As we can see from the list above, a few items are repeated so we need to
create a new list and call it clean_list. Hint, there are many different ways
to solve this problem but I suggest using one of the membership operators
to make our life easier.
Here is my solution (code only)
# chapter-2 exercise
dirty_list = [1, 2, 3, 2, 4, 1, 5, 6, 5, 5, 7, 8, 1, 9, 10, 9, 8, 3]
# empty list
clean_list = []
# loop over the items
for item in dirty_list:
# check if the item doesn't exist in the list
if item not in clean_list:
# if item is not found then add it
clean_list.append(item)
print(clean_list)
This will throw a NameError because the function is not defined. The
Python Interpreter will throw the above error because it tries to call a
function that does not exist in the memory. Anytime we write a function
name without the brackets, we use that name to point to a place where that
function is stored in the memory. We can prove this if we print the function
without the brackets:
This means that the hi_function can now receive one parameter called
name. We can add multiple parameters if we separate them with a comma:
def hi_function2(name, lastname):
print('Hi there')
These two parameters can now be used in the function body as local
variables:
def hi_function3(name,lastname):
print(f'Hi there {name}{lastname}')
Now we are using the formatted ‘f’ string to print the values of these local
variables. But where will the values of these two parameters/variables come
from? We can pass them into the function during the function call. Here is a
function call without any parameters:
hi_function()
hi_function4('Rick','Sekuloski', 33)
As you can see from the figure above, we have a function that takes 3
values or arguments and the last one is the person’s age. So in this case the
order of the arguments matters, for example, let us switch the lastname and
age:
So we have invoked the function with the name first, then age, and finally,
the lastname. The function will still work but the output will not make any
sense. Therefore, positional arguments are very important because their
order or position matters. What are the keyword arguments in Python?
Keyword arguments don’t care about the position because the arguments
are labeled with a keyword. Let’s create an example where we use keyword
arguments:
def hi_function5(name, lastname, age):
print(f'Hi there {name}{lastname}. Are you {age} old?')
In the function call, we have a keyword that you can consider as a variable
name and we assigned a value to that keyword. If we look at the output, we
can see that the order is correct, but let’s change the order of the keywords
in the function call and run it again:
hi_function5(name='Rick', age=33, lastname='Sekuloski')
The output is correct again because of the keyword arguments. Keyword
arguments are very good because they will match the parameters in the
function definition. Personally, this is not the best practice, our function call
looks messy. The function call was so much cleaner without these
keywords. This is everything you should know about positional and
keyword arguments in Python.
If we look closely, we will notice that this looks like the keyword arguments
we wrote in the function call, right? Yes, that is correct and this is another
reason why I don’t recommend using the keyword arguments because most
of the time developers mix them with default parameters. The default
parameters are useful because now the parameters have default or starting
value. This is great because if we miss adding a value in the function call,
there will be still an output. Let’s call the function with 3 arguments first:
Did you expect the output from above? If the answer is yes, then
congratulations because this means you understood how default parameters
work in the background. When we call the function with all of the
arguments, the default parameter values will be overwritten by the
arguments we pass into the function call. But let’s now make a function call
with two values only:
As we can see from the figure above, the function call doesn’t have all of
the arguments but in the output, the argument that was missing was
replaced by the default value. That is why the default arguments are so
important because now we called the function without any argument and it
still produced a result at the end:
Function Return
The ‘return’ is a special keyword in Python and is very useful and common
in functions. The idea is that the functions always return a value and we can
use that value to do whatever we want. Let’s create a function to calculate
the sum of two integer numbers:
# sum function with return keyword
def calc_fun(num1, num2):
num1 + num2
calc_fun(3, 4)
The function above will calculate the sum of the two arguments we pass in
the function call but when we run it, the output is not there. I know you are
thinking we didn’t use the print function so we can print the result. We
don’t always have to use the print function to get the value, instead of
printing messages, the function goal is to return a value. We can return the
result like this:
# sum function with return keyword
calc_fun(3, 4)
Good! Now we can wrap the function call into a print function so we can
get the result:
We can also store the return value into a variable and use that variable later
in our code:
Now the value is 8 because we can manipulate the value of the variable.
What you need to know is that when the Interpreter sees the return keyword
in the body of the function, the function will return that value and it will be
terminated. You need to be careful about placing code after the keyword
return in the function body because that code will never be executed, like
the following example:
Nested functions
The nested functions are very interesting because we can have a function
that is defined within another function. Instead of having one function, we
will have an inner/nested function that can access the variables defined in
the enclosing or outer function. I know this might be confusing but let’s
create one simple example so you can understand how nesting works.
# nested function
def outer_fn(num1, num2):
def inner_fn(n1, n2):
return n1 + n2
total = inner_fn(num1, num2)
return total
result = outer_fn(2, 3)
print(result)
The output will be 5 and here is the screenshot:
From the figure above, we have two functions, one is outer_fn which takes
two parameters num1 and num2 and the second function is inner_fn. On
line 8, we call the function with two arguments, and whatever is returned is
stored in the variable called result. When we call that function(outer_fn),
the Interpreter goes to line 2, and now the two function parameters num1
and num2 will have access to the values 2 and 3 respectively. Then it goes
into the body of the outer_fn which will be in line 3. In line 3, we have
another function declaration that takes two new parameters n1 and n2. This
function will be skipped because no one called it yet. The interpreter goes
to line 5 where we have the actual call to the inner_fn with two arguments
which are the parameters from the outer_fn. This is the same as if we wrote
it like this:
total = inner_fn(2,3)
Next, the interpreter goes to line 3 again but now the parameters n1 and n2
will have the values 2 and 3 and inside the function body we are returning
the sum of these 2 parameters. The function returns and goes to line number
6 where it returns the output with the result from the inner_fn. In line 8, the
result variable will have the value 5 that is returned from outer_fn, and
next, it will go to line 10 where it will print the result. That is how the
inner_fn can use and have access to the variables/parameters from the
outer_fn. Remember the key part here played the ‘return’ keyword
otherwise we would never have gotten this result back.
Docstrings in Python
The docstrings in Python are string literals that will appear after the
definition of function, class, method, or even a module. First, we need to
see how we can write a string literal:
''' This is string literal '''
We write the string literals inside triple quotation marks. Let’s now create a
function that will calculate the square of a number:
# docstrings
def num_square(num):
'''This function takes a number and returns the square of that number'''
return num**2
result = num_square(4)
print(result)
As you can see, the len() function will return the number of items in a
container. There are other ways to see what the function does, for example,
we can use the magic or dunder methods. The magic or dunder methods in
Python are the special methods that start and end with the double
underscores:
To summarize, the docstrings are the comments that we want others to see.
These comments should be meaningful and explain what the function does.
As we can see from the figure above, the current year is the first argument
and the year of birth is the second argument. What will happen if we pass
three arguments in the function call instead of the required two? Let us find
out:
We get an error saying we have given 3 arguments and the function
years_old() takes only two positional arguments. This is excellent because
it will help me explain what *args and **kwargs are in Python. The *args
syntax stands for ‘Non-Keyword Argument’ and **kwargs stands for
‘Keyword Arguments’ but why are they useful? Well, we can use *args as
arguments when we don’t know the exact number of arguments we need to
pass in the function. This will prevent the program from crashing like in the
figure above, so they are very useful when we don’t know how many
arguments should be passed to the function. Let’s change the function
declaration by using star *args. This will mean we can use any number of
arguments. Let’s write a new function that will calculate the sum of
numbers:
# args and kwargs
def addition_fn(*args):
print (*args)
addition_fn(1, 2, 3, 4, 5, 6)
As you can see, the addition_fn has 6 arguments, which are numbers from
1 to 6 but in the function declaration we have only used *args as a single
parameter. On the right side, we can see that the arguments we passed in the
addition_fn are accessed through *args. This gives us the ability to call the
built-in Python function called sum(). This function will return the sum of
all of the items from an iterable collection like a Tuple. But *args is not a
Tuple. One way to use *args is without the star in front like this:
Great! Now we have a Tuple as an output so we can call the sum() function
without any problems. Let’s do this and check the output:
As we can see, our logic works and we will get the sum of 21. To
summarize, we use *args when we are not sure how many arguments the
function will take. We can use a different name for *args like *nums. Let’s
focus on *kwargs. Kwargs is a dictionary of keyword arguments and the
double ‘**’ allows us to pass any number of keyword arguments. We can
add keywords in the function call and access them in the function body in
form of dictionaries:
# args and kwargs
def addition_fn1(*args, **kwargs):
print(kwargs)
return sum(args)
Scope
Scope is important in Python. Scope refers to the access we have to the
variables. When we try to access a variable before it is even created, we
will get this error:
This is a NameError because the result variable is not even defined and we
are trying to access it. This is happening because the variable does not live
in that scope. That is why we need to learn about the existence of different
scopes. If we declare and initialize a variable in Python outside of any
function, this means the variable belongs to the global scope. The global
scope is the scope that will provide access to this variable. Anyone can
access a variable declared in the global scope. Let’s declare a variable in the
global scope and also create a function that will print that variable:
# local variable
result = 14.4
def num_func():
print(result)
num_func()
As we can see, the variable called result belongs to the global scope where
it’s been declared and bound to a value of 14.4. Because of the global
scope, we can safely use this variable inside the function body. But the
functions in Python have their own block scope. The function scope means
whatever we have defined in the function body can be accessed and used in
that function only. If we have variables declared in the function and we try
to print them outside of the function body, we will get an error. Here is one
example:
def num_func1():
num1 = 11.4
print(num1)
num_func1()
print(num1)
Line 8 is where we try to print a variable declared inside the function called
num_func1(). This will cause the Interpreter to underline with red. If we run
this code, we will get NameError because num1 is not defined in the global
scope. Can you please guess the output of the following code:
#guess the output
x=1
def trick_me(x):
x=4
return x
print(x)
print(trick_me(5))
As you can see on line eight, we are printing the value of x which is
declared on line two. Do not get confused with the parameter x we passed
into the function because we could have named this parameter differently.
On line nine, I print the value of the trick_me where I pass an argument
with a value of five, but this argument will not make any difference because
line five will create a local variable for the function and re-assign the
variable to a new value of four. I’m saying local variable because that
variable is local for the function. On line six, we return the last value of the
x which will be four. That is why the output of the function trick_me will
be four.
But there is better way of accessing variables that are declared outside the
function and we know this syntax because we have used it before:
This is what we were doing so far, we passed the global variable x as an
argument on line 8 and we can use it in the function body through
parameter x. This is much better than using the global keyword inside the
function, right?
Python nonlocal Keyword
The non-local keyword was added in Python 3 and can be explained if we
use nested functions, therefore we need to go over the following example:
# nonlocal
def outer_fn():
x = 'John'
def inner_fn():
nonlocal x
x = 'hello'
inner_fn()
return x
print(outer_fn())
We can use the non-local keyword like the one on line five so we can work
with variables that are declared in the outer functions. We use the nonlocal
keyword to declare that the variable is not local for that function, meaning it
belongs to the functions that are one level up. When the Interpreter sees the
nonlocal ‘x’, it will automatically know that we don’t want to create another
local variable for that function but to use the variable that is declared in the
outer function. The outer function is the parent of the inner function. On
line five, the non-local variable value will be ‘John’ which comes from line
three of the outer_fn body. Line six is where we overwrite the value of x
from ‘John’ to ‘hello.’ This will modify the value of x for the outer_fn and
when we return the x, the value will be ‘hello.’ What do you think will
happen if we remove line number five? It will print John because the x will
become a local variable for the inner_fn() that will never be used anywhere.
The yellow underline in Python occurs when we assign a variable but never
use it, it’s like a soft warning.
So what are the advantages of having non-local variables in our code? Well,
the first one is that we can now access the variables that are declared in the
upper scope. When we reuse the same variable in the inner function, we
don’t create another variable somewhere in memory so we save memory
space and extra referencing. The disadvantage is that this keyword works
only inside nested structures and we can’t use it to reference global or local
variables.
Chapter 3 Python Installation
After the packages have been downloaded, you can click on it so it will open
the installation wizard:
After opening the package, you need to press the continue button in the right
bottom corner, and on the next page should be the ‘Read Me’ section where
you should scroll till the end (it is a good idea to read the text but it’s not
going to be a big deal if you don’t):
Continue by clicking on the ‘Continue’ button and ‘Agree’ with the license,
then select the disk where you want the installation to happen:
And then it will finally give you the option to install Python:
It will ask for your password or fingerprint so provide your details and you
are done with this process:
Finally, after a while if the installation is successful, you will see this
window:
Click the ‘close’ button and you have now installed the latest version of
Python on your macOS. If the installation was successful, we can close the
previous terminal window or we can even type exit() and press return. Next
is to check the latest version by typing ‘python:’
This was probably not the window you expected to see but this is normal
when it comes to installing Python, the installations are tricky, so type exit():
Finally, we can try again but this time type python3 instead of python and hit
return or enter:
Great! After providing the version number of Python, we have success. In
the future, we might even need to type python4 in order to see if the latest
version was successfully installed. If for some reason you still cannot install
Python, then I suggest opening the following link:
https://osxdaily.com/2018/06/13/how-install-update-python-3x-mac/
Make sure you select the last two options, the first one should be preselected
but the second one saying ‘Add Python 3.10 to PATH.’ It is very important
to select this before you start the installation. Click the install now and
follow the setup wizard:
Make sure you allow the app to make changes to your device so click yes
and the installation should start immediately; after a while, you should see
the following window:
Click on the close button and find the command prompt. If you have an
older version of Windows, you can search for it or if you have the latest
version like me, you can click on the start Windows icon and type
‘Command Prompt’ in the search bar:
This will open the command prompt where you should type ‘python’ and
press enter:
Now we can see that Python version 3.10.5 has been successfully installed
and working. In Windows, we can even use PowerShell. PowerShell is even
more preferable among developers because the same commands that work
on Linux and Mac machines work here as well. So just like we searched for
the command prompt, we can search for Windows PowerShell:
We can open the PowerShell and this is what it will look like:
As you can see, I need to update the PowerShell version as well, but if you
haven’t got PowerShell on your Windows machine, I suggest you download
it because you will be able to use the same commands I use on my Mac.
Type Python on the PowerShell and press enter and you should see the same
version number of Python that we just installed:
Another way to run Python code on your Windows is to install Git Bash or
Git for Windows. This is basically a BASH emulation, which is a shell that
will allow you to use UNIX commands:
To summarize, in order to run Python, you can use the Command Prompt,
PowerShell, or Git Bash. I recommend sticking to PowerShell if you are a
Windows user and updating it if you need to. Now the option we checked to
add the PATH allows us to run the Python command in PowerShell. The
Python installation was super important and now in the next sections, we
will learn how to install different tools so we can write and execute Python
code.
Windows announced you can install Windows terminal if you use Windows
10 or later. This will allow you to use the exact same commands as other
Linux and Mac users.
Install and get started setting up Windows terminal by clicking this link and
following the steps:
https://docs.microsoft.com/en-us/windows/terminal/install
Developer Tools
So far, we executed our code on the website called replit.com but we want to
be able to use some tools to run and write Python code. In the previous
section, we saw that we can use the Command Prompt on Windows and the
Terminal on Mac to run Python code. In the Terminal or Command Prompt,
we run simple scripts that we want to test quickly but developers will use
either Code Editors or IDEs to write and execute Python code. You can write
the code in any text/code editor or even in a word document but we don’t
have the same feeling that we are writing code, it is like we are writing a
piece of text and we can easily make mistakes because of the spelling and
indentation. We know that proper indentation is very important in Python so
no to the word documents. Therefore, a professional developer will need
some tools like code editors:
Sublime
Atom
VS Code
Vim
We can even use IDEs like:
Spyder
PyCharm
Thonny
What is the difference between Code Editors and IDEs? Code editors are
lightweight and allow us to enhance and ease the process of writing the code.
The code editor is used by many programmers because they have built-in
features that can speed up the process of writing the code. For example, they
use Emmet to autocomplete the code and give you suggestions during your
code writing and also has linting that is used to automatically check the
source code for programmatic and stylistic errors. An IDE, on the other
hand, is a more complex set that will combine different features like code
editors, text editors, debuggers, compilers, code testers, code formatting, and
much more. All of these features are under one hood. This is why when we
download the IDEs, the file will be much bigger than that of the editors. I
will not install all of the tools I have mentioned in the figure above but I will
guide you on how to install and use the most important ones. The VS Code
is the most popular code editor today and it is completely free. VS Code
(Visual Studio Code) editor can be downloaded from this website:
https://code.visualstudio.com/
As you can see from the figure above, you need to get the right version for
your Operating System. It will be automatically pre-selected for you, and in
my case, it is the one that says macOS – Universal. If you are using
Windows or Linux, you need to choose the right one for your system. There
are also other versions of VS Code if you click on the link saying ‘Other
downloads.’ They have an amazing get-started page that will help you if you
have any issues. Do not get confused and scared of the screenshots on their
website because they have included a few code snippets that you probably
don’t understand at this stage.
https://code.visualstudio.com/docs
This editor is my favorite and I have used it for all of my projects so far. The
code I have written for my HTML and JavaScript books was written in this
editor. This means we can use code editors like VS Code not just for one
specific language like Python but for all other languages as well. You can
click on the download link and install it on your machine because it is very
easy and straightforward.
Please don’t be afraid of the code, files, and folders in the figure above
because it is only for demonstration purposes. As you can see, we have a
very basic layout with an explorer on the left side which shows all the files
and folders we have opened, and an editor on the right side that shows the
content of the files we have opened. The GUI or graphical user interface is
divided into five different areas (note – all these areas will not be visible in
our initial lunch):
Editor – This is the main area where we can edit our files. We can
open as many editors as we want and they will be displayed side by
side
Side Bar – The side bar contains different views like Explorer, which
can assist us during the coding
Status Bar – The status bar gives the information on what files are
currently being edited
Activity Bar – This area is located on the far left-hand side and it
allows us to switch between different views. It also shows the number
of changes when Git is enabled
Panels – below the editor is where we have the output or debug
information – all the errors and warnings. We can even use the
integrated terminal if we want.
First, we need to click on the ‘Select Python Interpreter’ button and we need
to select the Python version we installed before:
As you can see, my Mac had listed the previously installed versions of
Python and this is good because it gives me an option to select the latest one.
If you remember, the version I installed was 3.10.5 therefore I need to select
that one, but your version might be different so please make sure you select
the Python version you downloaded and installed. If you don’t remember the
version number you can head back to the official website and check the
latest version in the downloads section:
https://www.python.org/
Select the right version and that is it for now. Do not worry about installing
the Linter pylint for now because you have selected the Python interpreter
and we can start writing code, so you can close the message by clicking on
the ‘x’. It is best to close the VS Code completely and open it again. Try to
open the file you have created by selecting the folder where it was saved. So
you need to click on the Open folder blue link:
Then you need to select the right folder and this is what you will get:
As you can see, in my case, the file name is called ‘test.py’ and is located in
the folder called python-exercises. Your file folder name can be different if
you want. Let’s talk about the VS Code editor and what it can do apart from
writing code. The VS Code like many other code editors comes with the
terminal and you can open the terminal by going into the menu and selecting
the terminal:
This will open the terminal:
So what can we do in this terminal? Well, we can run the above file, so make
sure you saved everything before you write in the terminal: python test.py
As you can see, I got Hello World as an output. The file name in my case is
test.py but your file name can be first.py for example. In this case, you need
to write python first.py to get the output. Let’s now install Linter, the
recommendation we had before so if you don’t see it on your screen you can
close the VS Code editor and open it again. When you open your folder/file
back, a message will pop up. What is linter? Linter will help you with the
syntactical and stylistic problems you might have in your Python code. This
will help you a lot when you have spelling mistakes and will let you know if
the Python syntax is correct. Pylint is one of the more popular versions that
do this. There are many other packages available on the market but VS Code
is suggesting this one for our Python code. So let’s just click on the install
button and install it:
Once the installation is completed, you should have a similar message to the
one in the figure above. Important: there shouldn’t be any red errors message
in your terminal. So let’s jump back to the code and try to make a mistake on
purpose. Before we make mistakes intentionally, let’s turn on linting by
going into the menu view>Command Palette:
And this should let you type something in your VS Code like in the figure
below:
Now when we install the Python Interpreter, we installed a lot of other
Python tools in the background that we can now use, so if you start typing
Python: Enable you will get the following list of options:
From the figure above, select the Python: Enable/Disable Linting option,
click on it, and select Enable or On:
After enabling linting, let’s remove the closing bracket of the print function
so we are making a mistake intentionally and finally save the test.py file.
This will immediately create a problem for us because linting will detect that
a bracket is missing and in the PROBLEMS tab/panel we can see a number
1:
From the figure above, you need to locate the marketplace icon and click on
it. From there I would suggest you install some VS Code icon packs that will
make your folder and files look better. You can install dark themes like
drakula official by typing it in the search bar and then clicking on the install
button next to it.
Now your VS Code is ready for writing Python code.
Useful Terminal/Command Prompt Commands
As a future Python developer, you should know how to use the Terminal or
Command Prompt. In this section, we will go through some very useful
commands. On Mac, you open the terminal by pressing command + space
and then you type terminal and press return/enter. On Windows, you need to
look for the start Windows icon and search for Command Prompt. The
terminals/command prompts are used today but they were used a lot in the
past because they run the code. After the GUI (Graphical User Interface), the
usage of the terminals decreased. As a developer, you will use the terminal
to download and run scripts, to communicate with servers and databases.
The commands for the terminal and command prompt are not the same but I
will provide screenshots and tips so you can practice the commands on
Windows, macOS, and Linux. For example, open your terminal or command
prompt and type ‘ls’ on Mac and ‘dir’ on Windows. The ls – (dir for
Windows) will list everything you will have in the current directory:
As you can see, everything I have in this directory is listed. How can we
check which directory we are in at the moment? For Windows, we need to
type ‘cd’ and for macOS, we need to type ‘pwd’:
As you can see from the figure above, it says I’m in the ‘Users/rick/
directory. Everything that is listed above is something I have access to. So
how can I navigate to my Desktop and see what’s there? Well, for both Mac
and Windows, we can write ‘cd Desktop.’ The cd means change directory:
If for some reason you want to get out of the current directory, you can write
‘cd ..’ and this will go from the Desktop back to /Users/rick:
The terminal now has a lot of text and looks messy. You can use the ‘clear’
command on Mac and ‘cls’ on Windows to clean the terminal window:
If for some reason you want to navigate to the root directory you can use this
command ‘cd /’, and don’t forget the forward slash. If you want to list
everything, use ‘ls’ or ‘dir’ for windows:
If you want to get back to the user directory, you can use the following
command: cd ~
I hope you are enjoying this section so far, but we are not done yet. Let’s
create a folder with the name ‘p1’ somewhere on the desktop. We are not
going to use the terminal for this so go to your desktop and create the folder
p1 where we can create subfolders later on. I need to navigate to the desktop
and I’m in the Users directory at this stage ‘cd Desktop’:
If you type ‘ls’ or ‘dir’ you should be able to see what we have on the
desktop:
As we can see from the figure above, I have the folder (p1) that I have
created. Now if I want to create other folders inside this folder, I need to use
the cd command again. So let us type ‘cd p1’ :
You can make sure nothing is inside if you use the ls or ‘dir’ command. If
you want to create another folder inside the p1, you can use the command
called ‘mkdir’ which stands for make directory:
Because we are inside the p1 folder, we can write this command ‘open .’ for
Mac and ‘start .’ for Windows.
This is what you should have in the p1> first > folder:
We can open the file (index.html) if we use the ‘open index.html’ or ‘start
index.html’ command:
The last command will open the file in our browser:
As you can see from the figure above, the HTML file is empty meaning
there is no content. We can open the Visual Studio Code directly from the
terminal like this:
This will lunch the VS Code for us but if we want to open the index.html file
directly, we can type the same command including the file name:
This will open the index.html file in the VS Code so you can start writing
basic HTML code:
Let’s open the index file again by typing ‘open index.html’ and see if the
changes we have made are visible now (save the index.html file before you
try to open it):
As you can see through the terminal, you can do the same things you are
doing with your mouse and keyboard. If you want to change the file name,
for example, from index.html to app.html, you can use the following
command ‘mv index.html app.html’ for Mac users and for Windows
‘rename index.html app.html’:
Let’s delete the folder ‘first’ we created a while ago but to delete the entire
folder together with the app.html file inside, we need to go back one level up
into the p1 file using the command ‘cd ..’:
Now we can delete the first folder using the ‘rm -r first’ command for Mac
and ‘deltree first’ for windows users:
Finally, we can remove the p1 folder because the name is not something I
would normally use or want on my desktop ‘rm – r p1’:
Code Formatting
When you write code, it is a good idea to know that we can format that code.
We have already installed VS Code editor so we can install other extensions
that can help automatically format the code for us. In this section, you will
learn about PEP-8 and the benefits of using it.
You can read more about PEP:
https://peps.python.org/
What is PEP?
The PEP is an abbreviation for Python Enhancement Proposal. PEP-8 is a
document that provides guidelines on how to write code that will follow
proper logic and coding style. The PEP-8 is the most popular one, but there
are many other Enhancement Proposals. The PEP -8 will help us write
beautiful code that is clean, readable, and styled. The PEP-8 is a Style Guide
for Python code and the documentation is very detailed but long (luckily,
you do not need to read the entire documentation):
https://peps.python.org/pep-0008/
In the same test.py file, I have added two basic functions:
The indentation is visible and if we run the code in the terminal we should
have the following output:
But our code is not formatted according to PEP-8 guidelines. If we go in the
menu view > Command Palette and type Format Document and click on it:
Then you will see that the following message says you need to install
Autopep8 code formatter:
Autopep8 is an extension in VS Code that if enabled will style our code
according to PEP-8 guidelines, therefore we should click on the ‘yes’ button
and install the extension:
If you have noticed, the code we have in the test.py file remained the same,
so again view > Command Palette and type Format Document, and click on
it:
Our code as you can see looks a bit different, with more spaces in between
the function declarations. The code is formatted now according to PEP-8.
Why does the code look like this with many spaces between the functions?
Well, according to PEP-8 documentation, there should be two empty spaces
between the functions. After writing code in your file, you should run the
formatter and your code will automatically be formatted for you. Instead of
doing this manually every time, you can automate this process. You can do
format on save if you go to the menu, code> Preferences> Settings, and type
‘format’:
There will be a list of options and under the ‘User’ pannel you can scroll
down and select the Editor: Format On Save and your formatting will be
automatically done. The code formatting will now happen every time you
save your code (Command + S) or Control + S for Windows. If interested in
learning more about pylint, pep8, or other extensions, please read the VS
Code documentation:
https://code.visualstudio.com/docs/python/linting
PyCharm Installation
In this section, you will learn how to install PyCharm on your machines. I
have chosen this IDE because it is the best when it comes to writing,
executing, and formatting Python code. One downside is that as an IDE, it
takes more machine memory because it is a complete package. One of the
many benefits compared to other Code editors is that you don’t have to
install extensions and plugins in order to run/execute Python. Some will
argue that code editors are extremely good because they can be used for
multiple languages. Both IDEs and Code editors have pros and cons but I
would like to give you an option to use whatever you feel is best for you.
PyCharm was developed by the company Jet Brains and they have a free
version that you can use to run your Python code and there is also a full-
fledged professional version that you need to pay for. The paid version will
include extensions and 17 different tools which you don’t need at this stage .
https://www.jetbrains.com/pycharm/
When you click on the download button, it will take you to the download
page where you can choose the right version. It will automatically detect
your operating system but if it doesn’t, please select the operating system
and download the free community version.
My operating system is macOS and the version I should download is called
Community and it is free, open-source, and pure for Python development.
Click the download button and follow the installation wizard. If you are a
Windows user, the installation will be slightly different compared to mine
but in the end, you will have the same IDE. You will also need to use your
email address before you can start downloading the PyCharm:
After you download it, you can start the installation process:
The macOS users will see this screen where you need to drag the PyCharm
CE to the applications, and in Windows, there will be an installer wizard:
After we drag the icon, you can double-click on the PyCharm CE icon so it
can open the IDE. If you have a previous version of PyCHarm, you can do a
clean install and you don’t have to import the settings. After double-clicking
on the icon, it will give you the following window:
Read the JETBRAINS terms of use, tick the box on the left bottom corner
and press the Continue button:
You can click on the Start Tour and you can read all of the tips and tricks so
you can get familiar with PyCharm:
If you want to create a new project, you can select file> New Project:
Make sure you set where you want your PyCharm files to be saved and you
can name your project whatever you like, in my case, I will name it
‘firstproject.’ The Base Interpreter should be the latest Python version you
have installed. For me, this was version 3.10 and it was pre-selected. Finally,
you can create the project by clicking on the ‘Create’ button. If you get this
message after creating the project, you can select ‘This Window’
The project will be created for you and you will have a file usually called
main.py:
As you can see, the firstproject is created and inside we have a file called
main.py with some starting code. If you want to create a new file, you can
select the firstproject folder, right click on it, and then select new file:
Name your file whatever you like, mine will be first.py (make sure you type
the right extension):
Click return/enter and the file will be created inside the firstproject. You can
do further customization to the PyCharm if you go to the menu,
PyCharm>preferences :
Or you can click on the IDE and Project Setting icon and it will lead you
straight to the Preferences section:
I have just set the font size to be bigger so I don’t have to strain my eyes too
much during the coding. Let’s start by writing a piece of code like a simple
print() function with bad spelling:
Finally, how we can run the code we have in the first.py file? Well, you need
to go to the menu and select Run as in the figure below:
And because you have two files, main.py and first.py, it will ask us which
file you want to run:
After you select the correct file, the terminal will be opened at the bottom
with a message that comes from the print function:
PyCharm is a very good IDE because you don’t have to install packages,
extensions, etc. to write and execute your Python code. Please read the
PyCharm documentation and learn some of its features.
What about code formatting in PyCharm?
If you have longer code that is more than one line we can go to
Code>Reformat Code and the code will be reformatted automatically for
you. Here is an example copied from the file we created in the VS Code
section:
As you can see from the figure above, the Reformat code works the same as
in VS Code. These are some links I want you to have in case you want to
read more about PyCharm:
Installation Guide:
https://www.jetbrains.com/help/pycharm/installation-guide.html
Learn PyCharm:
https://www.jetbrains.com/pycharm/learn/
PyCharm Guide:
https://www.jetbrains.com/pycharm/guide/
Summary
In this chapter, you have learned a lot. We started by installing Python on
different operating systems and then we installed different programming
tools. We have installed the VS Code editor which is one of the best code
editors in the market, but if you don’t like editors then I have provided the
best IDE for Python called PyCharm. You don’t have to use them both, it is
up to you to decide which one is the best for you. Everything you have
learned in this chapter will make you a better Python developer. We
shouldn't forget to mention that you have learned the importance of having
clean and formatted code and together with the terminal commands, you are
now becoming a professional developer.
Chapter 4 – Object Oriented
Programming – Advanced Python
Python is an Object-Oriented Programming language. This term is used by
many other languages and it’s fundamental. It’s so important to understand
the concept of having Objects because in Python everything can be
considered as an Object. OOP languages are organized around Objects and
these Objects can be created and modified during runtime. We will learn
where these Objects come from in the next sections but each object is
known as an instance of a class. The Classes in OOP are the templates that
allow us to create objects. In this chapter, we will learn what OOP is and
what its most important features are.
Class keyword
In the previous chapter, we have seen the keyword ‘class’ when we tried to
confirm the data type using the function type():
Abstraction
Encapsulation
Inheritance
Polymorphism
class Student:
As you can see, the Class name starts with a capital letter and this is a
naming convention that you should get used to it. Another rule is that the
class name should be singular, for example, you should not name your
classes like this ‘class Students.’ If the class name needs to consist of two
words then you should use the following convention:
class StudentObj:
As we can see, each of the words starts with a capital letter, which is
different compared to the snake case we used so far. The snake case is used
to join multiple words with underscore like student_obj. After the Class
name, we have a colon ( : ) and here we will need to write the class body.
But before we do this, let’s create an Object for this class. If we leave the
code like this, it will throw an error so let’s add the ‘pass’ keyword and
create our first Object.
On the right side of the figure, we have the output where we have the type
class and ignore the ‘main’ keyword for now because we will explain what it
means in future sections.
From the figure above, we have a few new keywords that I need to explain.
On the left side, we have a Class and this represents what we want the
create, and what kind of properties and methods we want that Class to have.
So, it is a blueprint or template. On the right side with green background
color, we have the Objects. When we want to create an Object from a
particular Class, we need to instantiate that class. The Objects we are
creating are called Class Instances and we can create as many instances as
we want. If you have learned other OOP languages, then you are familiar
with this syntax because they all share the same or similar syntax:
# class template
class StudentObj:
pass
# Create new object by instantiating the StudentObj class
s_obj1 = StudentObj()
print(type(s_obj1))
class Person:
def __init__(self, name):
self.name = name
def greet(self):
print(f'{self.name}')
In Python, the Class methods start with a double underscore __ and they end
with a double underscore as well. Generally, in Python, the methods are
special types of functions. We have covered functions in the previous
chapter so we are using the exact same syntax with the keyword ‘def’ in
front. So, the difference between functions and methods is that methods are
specific to a Class, so they belong to that Class and functions belong to the
global scope. That is why the authors in most literature you will read use
functions instead of methods when they describe Classes, they can be easily
mixed up. In our case, the methods with a double underscore are special and
they are called dunder methods or magic methods. The method at the top of
the Class declaration is known as the constructor method or init(). This
method will automatically be called when we instantiate Objects from the
class Person. We don’t have any objects at this stage but this method will be
called immediately. Let us create a person_obj1 which will be an instance
from the Person class and try to run the following code:
From the picture above, on the right side we have a TypeError saying that
the Person method __init__() is missing the name argument, but how does it
know? Well, we are creating an instance of the Person Class, therefore when
an Object of the Person Class is created, the constructor of that Class will be
called:
person_obj1 = Person()
print(person_obj1)
This will run the constructor function init() and it will try to do the following
assignment:
self.name = name
But the problem is that I never give any arguments during the constructor
call because we called the Class like this Person() without passing an
argument. In the constructor method, after the self-keyword, we are expected
to provide a parameter name:
person_obj1 = Person('Jason')
Now if we run the code again the error will be gone. The ‘self’ keyword we
have in the init() method refers to the current instance of the class
(person_obj1). In our case the self-parameter is a reference to the
person_obj1 which is the only instance of the class, therefore person_obj1
will have access to the Class methods and properties. The ‘self’ keyword can
be named whatever we like but it has to be the first parameter in the
constructor because it’s considered a default parameter of any method in the
class. This is what is happening behind the scenes:
Let’s access that name property from the Object because the Objects are
Instances of a Class and they have access to the Class methods and
properties:
Let’s create another instance/object from the Person class this time with a
different name:
As you can see, we have created another instance of the Person Class and
this instance now has a different name attribute (Carly) and when the
__init__ method is called, the name parameter will assign ‘Carly’ because
the of ‘self’ keyword which will tell the constructor that the parameter
belongs to the person_obj2 Object. So, we have followed the good old DRY
(DO Not Repeat Yourself) principle because we were able to create two
separate instances/objects that called the same class with different name
attributes. The objects accessed the same class and its methods and
properties. If this was not OOP, then we would have to write the same code
for both of the objects. This is making the code to be more dynamic. The
self-keyword is bound to the object that is instantiated. In the Person Class,
the init constructor is very simple, accepting only one parameter but it can
be much more complex, for example, let’s say we want the constructor to be
called with name, last name, and age parameters and we can also change the
greet() function to print all of the details:
Now when we instantiate an Object from the Person class, we need to pass
two additional arguments like last name and age. The name, lastname, and
age are class attributes and they can be accessed by the Object/instance. The
class attributes are properties that the Object has access to using the dot
notation and the property:
print(person_obj1.name)
print(person_obj2.age)
As you can see, the person_obj1 and person_obj2 have access to class
attributes without the brackets because they are not methods. If we want to
access a class method directly, we must use the brackets () like this:
print(person_obj1.greet())
print(person_obj2.greet())
Finally, the person_obj1 and person_obj2 are instances of the class Person
but they are unique and different from each other, please do not confuse
them. We have used the class Person as a template to create/instantiate
objects that are stored in different places in computer memory. We can find
this very easily if we print only:
print(person_obj1)
print(person_obj2)
As you can see, both Person objects are stored in different locations in the
computer memory. This is perfect because now each of these objects can
have its own methods and attributes that will be hidden from the other
object.
In summary, the Class can have attributes and methods. The attributes are
initialized by the constructor method that will have a ‘self’ keyword as a
default parameter. When we create a new object or instance of the class
Person, we immediately call the constructor and initialize all of the
parameters to the values that are unique to that object. Each instance of the
Person class can access the class methods and attributes and they are
independent of each other, meaning they are stored in different memory
locations.
class Person:
# Class Object Attribute
is_person = True
# Constructor
def __init__(self, name, lastname, age):
self.name = name # name attribute
self.lastname = lastname # lastname attribute
self.age = age # age attribute
# Method
def greet(self):
print(f'{self.name}, '
f'{self.lastname}, old:{self.age}')
I hope you now understand why the constructor is called when we instantiate
the object. Please don’t get scared by my Picasso drawing. The constructor
can be much more complex, for example, let’s allow new objects to be
instantiated from the Person Class if they meet a certain condition like an
age limit. For example, let’s add a condition in the constructor so it will
check the age before we create the actual object. Have a look at the complete
code:
class Person:
# Class Object Attributes
is_person = True
# Constructor
def __init__(self, name, lastname, age):
if age >= 18:
self.name = name # name attribute
self.lastname = lastname # lastname attribute
self.age = age # age attribute
# Method
def greet(self):
print(f'{self.name}, '
f'{self.lastname}, old: {self.age}. '
f'Called by the class itself: {Person.is_person}. '
f'Called by the self keyword {self.is_person}')
Very good, now if we change the age to 18 or bigger, the if condition will be
evaluated to true and the object will be successfully created:
# Constructor
def __init__(self, name='John', lastname='Doe', age=18):
if age >= 18:
self.name = name # name attribute
self.lastname = lastname # lastname attribute
self.age = age # age attribute
Now, this allows us to create objects or instances from the Person Class
calling the constructor like this:
person_obj3 = Person()
print(person_obj3.name)
The output from the code above will be John even though we didn’t pass any
arguments to the Person() constructor. The default values we have in our
code are called safeguards so we can have proper control of how we
instantiate Objects from a Class.
person_obj3.date_created('14/05',2023)
The error says we supplied 2 parameters but 3 were given. If you remember,
each method inside the Class should have the self-keyword as a first
parameter, but here we are missing this keyword. The Class method does not
belong to the Object therefore the ‘self’ keyword is no longer needed but we
need to use a keyword that will tie this method to the Class. Therefore, we
use the ‘cls’ which stands for class and is standard nowadays. The Class
method must receive the class keyword as a first argument:
# Class Method
@classmethod
def date_created(cls,today_date,year):
print(today_date,year)
Now the object can call this method:
person_obj3.date_created('11/11', 2023)
So why do we need this kind of method when the object can access it just
like the rest of the methods? Well, this kind of method belongs to the class
itself so you are not required to instantiate the class in order to use it:
This will still print the output because the method is a class method and
doesn’t require an Object to access it. These types of methods are rare in
Python but it is good to know them.
print(Person.is_adult(19))
This will return true because the age (19 >=18) which is evaluated to True:
# Class method
def greet(self):
print(f'The data and functions are encapsulated, \n'
f'My name is: {self.name}, \n'
f'My last name is: {self.lastname}, \n'
f'and I\'m {self.age} old.')
# Private
private String name;
private String lastname;
private int age;
But we cannot do the same thing in Python. In Python, when we want to say
that this attribute or variable is private, we need to write an underscore in
front:
# Method
def greet(self):
print(f'The data and functions are encapsulated, \n'
f'My name is: {self._name},\n'
f'My last name: {self._lastname},\n'
f'and I\' old:{self._age} ')
# Method
def greet(self):
print(f'The data and functions are encapsulated, \n'
f'My name is: {self._name},\n'
f'My last name: {self._lastname},\n'
f'and I\' old:{self._age} ')
class Student(Person):
pass
print(student_obj1.greet())
Output:
This is the syntax for inheritance in Python:
class Student(Person):
The Parent class should be included in the brackets of the child class and that
is all. In this case, the Student class doesn’t have its own properties and
methods but it can have new methods and they will not be shared with the
parent class. This means that the child class can inherit everything from the
parent class and can have its own attributes and methods. We use the pass
keyword whenever we don’t want to include any code. Let’s discuss the
following code:
As you can see, the student class was able to use the parent class Person
__init__ constructor method to initialize the name, lastname, and age and
after that to use the print method so it can print the details of the
student_obj1. So far, our child class inherits all of the properties/attributes
and methods from the parent class but we can add a new constructor for the
child class as well that will have its own attributes. The common attributes
between a Person and a Student are:
name
lastname
age
We can add the following new attributes for the Student class:
class Student(Person):
def __init__(self,name,lastname,age,dob,address):
Person.__init__(self,name,lastname,age)
self.dob = dob
self.address = address
From the code above, the Student constructor will have all of the attributes
from the parent class Person plus two new, the dob (date of birth) and
address. Instead of writing the self.name = name for all of the parent
attributes, we can call the Person init constructor and include the parent
attributes directly in the Student class:
For the Student class, we add self.dob = dob and self.address = address
because they are the new attributes that belong to the student class and they
are not inherited from any other class. The student object now will need to
include the dob and address attributes:
If there was no inheritance, the Student class and Person class would have
looked like this:
class Person:
# Constructor
def __init__(self, name='John', lastname='Doe', age=18):
if age >= 18:
self._name = name # name attribute
self._lastname = lastname # lastname attribute
self._age = age # age attribute
class Student:
def __init__(self, name, lastname, age, dob, address):
if age >= 18:
self._name = name # name attribute
self._lastname = lastname # lastname attribute
self._age = age # age attribute
self.dob = dob
self.address = address
As you can see, we are not following the DRY (Do Not Repeat Yourself)
principle because we are writing the same code for the same attributes twice.
Imagine we have a few more classes like Undergraduate and Postgraduate,
then we would need to repeat the same code four times. I hope you
understand why inheritance is so important in OOP languages.
Method overriding
Method overriding happens when we want to use the same method name in
the child class but we want the output to be different from the parent class
method. Both methods need to have the same name. For example, in our
Parent class, we have the greet() method, which is suitable and works
perfectly for the Person class but although the child class Student can access
it, it cannot use it to print its own two new attributes. Here is the greet()
method from the Parent class:
# Method
def greet(self):
print(f'The data and functions are encapsulated, \n'
f'My name is: {self._name},\n'
f'My last name: {self._lastname},\n'
f'and I\' old:{self._age} ')
The instantiated student_obj1 from the Student class can access this method,
thanks to the inheritance like this:
print(student_obj1.greet())
Great! It works but what about the dob and address attributes that we have
only in the Student, we want them to be included in the output. This is where
method overriding comes into action. We can use the same method name
greet() and create it for the Student class (we should write this method in the
Student class):
# Method overriding
def greet(self):
print(f'My name is: {self._name},\n'
f'My last name: {self._lastname},\n'
f'and I\' old:{self._age}. I\'m student: \n'
f'born at: {self.dob},\n'
f'that lives in : {self.address}')
The output will be:
Perfect! Now we have two methods with the same name in the two classes.
When an instance of person class is created and called the greet() method,
the method created in the Person class will be called but when an instance of
the child class is created and called the greet() method then the Interpreter
will get the method from the student class.
# inheritance
class Person:
# Constructor
def __init__(self, name='John', lastname='Doe', age=18):
if age >= 18:
self._name = name # name attribute
self._lastname = lastname # lastname attribute
self._age = age # age attribute
# Method
def greet(self):
print(f'The data and functions are encapsulated, \n'
f'My name is: {self._name},\n'
f'My last name: {self._lastname},\n'
f'and I\' old:{self._age} ')
class Student(Person):
def __init__(self, name, lastname, age, dob, address):
Person.__init__(self, name, lastname, age)
self.dob = dob
self.address = address
# Method overriding
def greet(self):
print(f'My name is: {self._name},\n'
f'My last name: {self._lastname},\n'
f'and I\' old:{self._age}. I\'m student: \n'
f'born at: {self.dob},\n'
f'that lives in : {self.address}')
print(student_obj1.greet())
Subclass
Derived class
Concrete class
The parent class or the class that other classes inherits can be known as:
Parent
Super
Abstract class
I hope now everything is starting to make sense and here is another example
of inheritance:
# inheritance
class Mammal:
def __init__(self, name):
self.name = name
# eat method
def eat(self):
print(f'{self.name} eats different types of foods!')
# walk method
def walk(self):
if self.name != 'bat':
print(f'{self.name} can walk!')
else:
print(f'The {self.name} is the only mammal that can fly!')
class Dog(Mammal):
def __init__(self, name, breed, legs):
Mammal.__init__(self, name)
self.breed = breed
self.legs = legs
The output:
As you can see in the above inheritance example, we have the Mammal class
which is the parent and the Dog class is the one that inherits. We have used
the inheritance so the Dog class can inherit all of the attributes and methods
like eat and walk from its parent Mammal class. In the Dog class, we also
have method overriding because the ‘eat’ method from the parent class
wasn’t suitable for use on Dog instances. The Dog class can also have its
own method/methods like ‘details’. This method cannot be accessed by the
Mammal class instances.
isinstance()
This is a built-in function in Python that allows us to check if something is
an instance of a class.
Here is the syntax:
isinstance(instance, Class)
print(isinstance(dog_obj1, Dog))
If we run this code, it will give us True because the dog object is an instance
of the class Dog. But if we try to see if the dog_obj1 is an instance of the
Mammal class, what do you think will happen?
print(isinstance(dog_obj1, Mammal))
The output:
This will print True because the dog_obj1 is technically an instance of the
Mammal. After all, the Dog class is the child class or subclass of the
Mammal class. This is confusing but it is very logical because the Dog class
is a subclass of Mammal and therefore every instance of the dog class will
be an instance of the Mammal class as well. If you remember, I mentioned
that in Python, everything is an object. I can prove this if I type the name of
the instance and dot like this:
As you can see, the dog_obj1 has access to all of the attributes and methods
from the Dog and Mammal class because of the inheritance but if I scroll
further, there will be more functions available to this dog_obj1 instance:
Where are these functions coming from? Python, as we know, comes with
pre-built methods and functions and in this case, the dog_obj1 or any
instance will inherit from the base Object class that Python comes with. This
means that dog_obj1 is an instance of Dog class, Mammal class, and Object
class as well. If I try to print this code, the result must be True:
That is why the dog_obj1 has access to all of the methods from the Dog,
Mammal, and Object base classes. The methods are also accessible for
Python Strings, Lists, Dictionaries, etc:
Now we know that although the Mammal class is the first/parent class, it
also inherits from the base Object class as well, therefore we can even write
it like this:
class Mammal(object):
Output:
This proves that the len() function works on different data types like strings,
lists, and dictionaries and it gives us result, so the len() function actually
takes many shapes. Another way to demonstrate polymorphism is to use the
for loop and call the same method but using a different class instance:
animals = [mammal_obj1, dog_obj1]
Both objects in the lists belong to the Mammal-Dog inheritance example and
the result will be:
From the code above, we can see that we have two different outputs because
in the for loop, this is what is happening:
mammal_obj1.eat()
dog_obj1.eat()
super() function
In order to explain the super() function, I would like to give you an example
where we have two classes - Person and Employee. If you want you can try
and write the code yourself based on the description below, but please don’t
get too hard on yourself if you can’t do it.
Description:
The program should have two classes. The first class will be called Person
and the second class will be called Employee. You will need to guess their
relationship so you can write the inheritance correctly. The person class will
have the following attributes:
name
dob (date of birth)
id_number
In the person class, you should have the info() method that will print all of
the Person details in a new line. The class Employee should be able to use all
of the attributes and methods from the parents class but it should also have
new attributes:
salary
position
The employee class will have its own info() method with the same name as
from the Person class so it can print the parent class details plus the new
Employee details as well (method overriding). In the end, you need to create
an instance of the class Person and call the info method then create another
instance of the Employee class and call the info() method.
# Parent class
class Person(object):
def info(self):
print(f'name: {self.name} \n'
f'dob: {self.dob} \n'
f'id: {self.id_number}')
# child class
class Employee(Person):
def __init__(self, name, dob, id_number, salary, position):
# invoking the __init__ of the parent class
Person.__init__(self, name, dob, id_number)
self.salary = salary
self.position = position
def info(self):
print(f'name: {self.name} \n'
f'dob: {self.dob} \n'
f'id: {self.id_number} \n'
f'salary: {self.salary} \n'
f'position: {self.position}')
class Employee(Person):
def __init__(self, name, dob, id_number, salary, position):
The super() is called super because it refers to the parent class (Person) and
the parent classes are also known as super or abstract classes. This is how we
can use the super() to link up the parent and child classes
Code introspection in Python
Introspection allows us to determine the type of object during the runtime.
As we know, everything in Python is considered an Object and every object
has attributes and methods. We also learned that the parent class inherits
from the base class known as Object. In Python, we have a built-in function
that we can use for code introspection. The first function is a well-known
function that we have used so many times so far:
Type()
# 1 type()
str_obj = 'Hi'
list_obj = [1, 5, 7]
int_obj = 10
float_obj = 10.3
dict_obj = {
"type": "1"
}
print(type(str_obj))
print(type(list_obj))
print(type(int_obj))
print(type(float_obj))
print(type(dict_obj))
Output:
dir()
this function will return the list of methods and attributes associated with the
object
print(dir(dict_obj))
The output:
str()
This function will convert everything to a string
# 3 str()
list_obj1 = [1, 2, 4]
print(type(list_obj1))
# convert to str
print(type(str(list_obj1)))
Output:
<class 'list'>
<class 'str'>
id()
This function will return an id of an object
# 4 id()
m = [1, 2, 3, 4, 5]
# print id of m
print(id(m))
Output:
4478974848
https://docs.python.org/3/library/functions.html
Dunder/Magic methods
Dunder or magic methods are very important in Python. We have already
briefly mentioned them but in this section, we will talk about them in more
detail. One example of the dunder method we have used is the __init__
method or known as constructor. The __init__ is a magic method because it
will be called automatically when an instance(object) from that class is
created. Luckily for us, Python provides many dunder/magic methods like
the len(), print(), and []. They are all dunder methods. We have seen from the
previous section that when we use the dir() function on a class instance, the
output will be a list of methods inherited from the base Object class:
print(dir(person_obj))
Output:
list_obj = [1, 3, 5, 7, 9]
The result will be 5 because we have five items in the list above. In the
background, the len() is implemented as a dunder method and Python allows
us to use this kind of method directly on the object or instances of our
classes. I will create a class Car and one instance just to show you how we
can call some of the dunder methods from the figure above:
# car class
class Car(object):
def __init__(self, color, make, year, size):
self.color = color
self.make = make
self.year = year
self.size = size
Now we can call some of the dunder/magic methods on this class like the
__str__() directly on the bmw_car instance like this:
print(bmw_car.__str__())
The __str__() dunder method will return a str version of the object. The str is
the built-in string class. But what do you think will happen if we use the
built-in str() Python function instead of the dunder method?
print(str(bmw_car))
The output will be exactly the same, and why am I showing you this? Well,
the dunder method __str__() allows us to use the Python built-in function
str() and avoid complications. If you want to read about the dunder methods,
you can click on the following link but sometimes Python documentation
can be overwhelming:
https://docs.python.org/3/reference/datamodel.html#specialnames
If you read only a few paragraphs from the link above, you will find out that
we can perform basic customization on these methods and that is what I
want to teach. In short, we can modify the existing dunder methods and
create our own methods. The magic/dunder methods can help us override the
functionality for the built-in functions and make them custom for specific
classes. For example, let’s modify the __str__() dunder method to return
something instead of a memory location:
# car class
class Car(object):
def __init__(self, color, make, year, size):
self.color = color
self.make = make
self.year = year
self.size = size
def __str__(self):
return f'{self.make}'
def __del__(self):
print('Deleted')
It will still run the destructor at the end of the program and it will delete all
of the references to the object. The del is usually not that popular in Python
and you can run into some problems if you are using it because it will not
delete just a single object or variable but all of the references connected to
that object or variable. But it is always nice to know it exists. There is
another dunder method called delete. The __del__ and __delete__ are both
dunder methods in Python. The __delete__ will delete the attribute which is
a descriptor and we can execute it when needed. It will not run at the end of
the program like __del__.
# Calling __delete__
def __delete__(self, instance):
print("Deleted")
Let us talk about another dunder method __call__. The call will give our
class unique features:
def __call__(self):
return 'Called!'
Now our actual object bmw_car can be called using these brackets ‘()’, or
the same way we call the function:
print(bmw_car())
class Car(object):
def __init__(self, color, make, year, size):
self.color = color
self.make = make
self.year = year
self.size = size
self.engine_dict = {
'automatic': 'Yes',
'manual': 'No'
}
Now we can create the dunder __getitem__() method:
Finally, we can call the instance of the class in our case bmw_car like this:
This will give us the value of the key ‘manual’ which is ‘No’. I think you
now understand that we can modify the dunder methods and customize what
they return. It’s not the best idea to modify the dunder methods but
sometimes our code logic requires us to do this. The dunder methods are
indeed magic because they can help us change the way methods return
something for specific classes. This is all about dunder methods but you can
read the documentation if you want to learn more about them. In the next
section, we will focus on another OOP feature called multiple inheritance.
Multiple Inheritance
The inheritance allows us to have a class known as child class that can
inherit the properties and methods from another class. But there can be
different types of inheritances:
1) Single inheritance
2) Multi-level inheritance
3) Multiple inheritance
4) Multipath inheritance
5) Hybrid Inheritance
6) Hierarchical Inheritance
In our case, we want to explore multiple inheritance, what it does, and what
it means in Python. So far, we have done a single inheritance where the child
class had only one parent/base class but the child class can have multiple
parents. The derived (child) class can inherit all of the properties and
methods from multiple classes. Please take a look at the figure below:
We can have multiple parent classes and the child class can inherit all of the
methods and properties defined in the parent classes. This is very powerful
and not all of the programming OOP languages allow it. In the following
example, we will have father and mother class that will be inherited by the
child class:
# Multiple Inheritance
# father class
class Father(object):
def __init__(self, name, age):
self.name = name
self.age = age
def get_name(self):
print(f'Name: {self.name}')
def get_age(self):
print(f'Age: {self.age}')
class Mother:
def __init__(self, name, eyes):
self.name = name
self.eyes = eyes
def get_name(self):
print(f'Name: {self.name}')
def get_eyes(self):
print(f'Color of eyes: {self.eyes}')
def child_info(self):
print(f'Name: {self.name}\n'
f'Last name: {self.personality}\n'
f'gender: {self.gender}\n')
# child object
As you can see, we can do multiple inheritance if we pass the names of the
classes one after the other, separated with commas:
This works just fine, let’s test the get_age() method now:
From the figure above, we can see that we have an attribute error saying that
the ‘Child’ object does not have an attribute for age, and this is true because
this is our Morgan object:
We can solve this by adding age at the end. For example, let’s 2 (for two
years):
As you can see, the child now is 2 years old, and everything works. Let’s call
some of the mother methods and see what will happen:
Same error, the child object doesn’t have the ‘eyes’ attribute and if we add
that attribute the problem will not go away unless we include the Mother
constructor in the child class:
Pros:
a) The child class can inherit functionalities from multiple classes
b) This allows us to create complex relationships between the classes
c) Multiple inheritance syntax resembles how classes are in the real
world, therefore we can better describe the classes and establish
relationships between them
d) Having the ability to use the parents’ methods and attributes is a huge
bonus because any OOP language strives for code reusability, and here
we reuse the code from other classes
e) Building applications using multiple inheritance will take less time
(code reusability) and less memory
Cons:
a) I mentioned that Python is one of the few languages that allow us to
implement multiple inheritance, which can cause a lot of confusion
b) It is really hard to have multiple inheritances where multiple classes
have the same method name
c) The maintenance of the code is much more tasking compared to
applications with single inheritance
# class A
class A:
def __init__(self, length=5):
self.length = length
def info(self):
return self.length
class B(A):
pass
class C(A):
def __init__(self, length=2):
self.length = length
def info(self):
return self.length
# instance of class D
d_ob1 = D()
print(d_ob1.info())
If we sketch this entire relationship, we will end up with this drawing:
If we create an instance of the D class and call the info method, what will be
the output?
# instance of class D
d_ob1 = D()
print(d_ob1.info())
The output will be 2 and not 5, why is this? Well, because the job of the
MRO algorithm is not going to follow our human logic and it will not go
clockwise and get the method from class A directly:
So MRO will consider what is first in line when we have methods or
attributes that have the same name. It will consider the classes from which
the inheritance should happen first:
In Python, there is A method called mro() that will tell us the exact order that
the algorithm will use so all we need to do is to type:
print(D.mro())
Output:
From the figure above, we can see the algorithm order. First, it will check if
the info method is in class D, then it will go to the B class and check, then C,
then A, and finally the base Object class that each of the classes inherits
from. The Method Resolution Order is not easy to understand in more
complex scenarios. Can you guess the following output:
class First:
pass
class Second:
pass
class Third:
pass
From the code above, the order starts with Fifth, then Fourth, and then Third.
But from the figure above, we can see that it starts with Sixth, then it goes to
Fifth and Fourth, but instead of going to Third, it goes directly to First,
Second, and finally Third. This is happening because of one algorithm the
MRO uses called Depth First Search but this is a more advanced concept
and it’s not in the scope of this book. The algorithm for MRO is not the same
one that was used in the previous versions of Python.
Summary
This chapter was packed with information and new concepts. I know this
was the hardest chapter so far for some of you. My recommendation for you
is to not get discouraged because I can assure you if you keep practicing
and reading, you will learn all of the OOP features. Take your time, read
some of the sections again and never give up. Let’s get ready for our new
chapter called modules and I promise, the hardest features are already
covered.
Chapter 5- Phyton – Modules and
Packages
In this chapter, we will learn all about Python modules and packages. A
Python module is simply a file that contains Python code. Inside a module,
we can define classes, functions, and variables. In a module, we organize
related code. So far, we have organized and grouped the code in a few
different ways. The first way is to use files so here are a few of the files I
have created in the previous chapter:
Organizing the code into different files is one way but we can organize the
code into functions as well. As you know, the functions in Python are
treated as single units or blocks so we can call and run them when we want.
The third way to organize the code is to use classes. The classes are
blueprints or templates from which we can instantiate or create objects.
Python has provided us with different ways to organize and group related
code. In the next section, we will talk about how we can use modules to
organize and group the code.
After you have created the folder, you will have a directory called venv,
you don’t have to worry about what it does and what it means at this stage:
When you create new files or modules as we now call them, you should not
create them inside the venv folder but outside in the chapter5 directory.
Let’s create a file called main.py:
The two files that we created are modules, main.py is the first module and
utility.py is the second module. When we are creating the module names,
we need to use the snake_case syntax just as we did for the variables. This
means that if the module name consists of two words, we need to use an
underscore between them. All of the letters should be lowercase:
my_file.py
In the utility.py module, we can write some basic math functions and the
main.py module is the one that will call and use these functions. Let’s write
a few simple math functions in the utility.py module first:
As you can see from the figure above, I didn’t include the extension ‘import
utility.py’ because it is assumed that the module is a Python file. If we print
the utility, what do you think will be the output:
import utility
print(utility)
Output:
As you can see, it will print the actual file path of the utility.py file. It
would be a different path if you are trying this on your machine, or if you
are still using ‘replit’. It will provide you with a different path because that
website will generate a path for you. Now our files/modules are linked and
we only need to use the dot syntax to start using the functions:
As you can see, whatever we have declared in the utility module is now
accessible in the main.py module. Let’s do some basic maths calculations
using the functions from the utility file:
The output is:
What if I have a very big project with a lot of files that I need to import?
Well, you can do multiple imports in the main file like this:
import utility
import utility1
import utility2
import utility3
Python Packages
Imagine that our project is getting bigger and every day we add new
functionality and we want that functionality to be included in a separate
folder or package. In Python, we can create new packages if we do right
click on chapter5 and then select New > Python Package and name the
package ‘exponents’ (you can use a different name if you want to):
As you can see, we can even create a package and even a new directory, but
let’s select the package:
Let’s try to import pow_of inside the main.py file with the import syntax:
As you can see, the module cannot be found. Why we are getting this error?
The package we created called exponents maybe look like a normal folder
or directory but it’s considered a package. The package is on a different
level from the rest of the modules like main and utility. Therefore, the files
from this package can be imported into Python like this:
import exponents.pow_of
Before we do anything, let’s print this and check out the output:
print(exponents.pow_of)
The output will be a path to the file pow_of located in the package
exponents (if you try to run the same example on your computer, the path
will be different):
Finally, let’s check if the function we have declared in the pow_of module
can now be accessed in the main file:
print(exponents.pow_of.pow_fun(num1))
Output:
It looks like everything we have done is working because we got the result
36 and 6 raised to the power of 2 is exactly 36. Okay, now, let’s talk about
the __init__.py file that was created for us when we created the package. If
you are using replit.com, you will not have this file because the website
usually hides these kinds of files. Why does Python create this empty file?
This file is created because it will tell Python that this is a package from
where you need to import files.
And let’s call this module in our main.py module using the import statement
that we learned before:
import exponents.extra_functions.modulo_fun
As you can see, the import line is getting longer and it doesn’t look nice.
Let’s try to use the module_fn in our main.py file:
import utility
import exponents.pow_of
import exponents.extra_functions.modulo_fun
num1 = 6
num2 = 3
print(exponents.extra_functions.modulo_fun.modulo_fn(num1, num2))
This is how all of the files are connected:
And now the modulo_fn can be used directly in the main.py as it was
declared there:
print(modulo_fn(num1, num2))
What will happen if there are multiple functions in the module that we want
to import? Let’s rewrite the import utility statement to this:
print(sum_fun(num1, num2))
print(divide_fun(num1, num2))
print(multiply_fun(num1, num2))
But if we want to import the entire module and use the module like before
to access its functions, we can simply do this:
This approach is better when we want to avoid name collisions. What does
name collision mean? This means that we can have a function with the
same name in different modules and when we import them using the last
approach, it will not create a name collision.
The star at the end means import everything from the utility1 module and
now you can call the functions from this module like this:
print(utility1.sum_fun1(num1, num2))
print(utility1.divide_fun1(num1, num2))
print(utility1.multiply_fun1(num1, num2))
There is no one rule of import syntaxes you must use, just go with the one
you are most comfortable with.
As you can see, I have only one function there to greet the user. Now we
can import this function using the well-known from-import syntax:
Instead of the name long_module, we can even use one letter like ‘u’ and
make it even shorter. Now let’s see if we can use the greet() function:
print(long_module.greet('Kevin Hart'))
By doing this, we can definitely create custom names that will avoid
naming collisions.
Python __name__
I think now it’s the right time to explain what double underscore __name__
means. If you haven’t seen this code so far, double underscore name
(__name__), I can assure you that one day you will find this piece of code
and use it. So, what is Python variable __name__? This variable with two
underscores before and after the variable name has a special meaning for
Python when it comes to modules. To show what it means, I have made a
copy of the previous modules and created a new folder called chapter5.1.
Inside this folder, there are the utility.py and pow_of.py where I have added
at the top the following code: print( __name__):
print(__name__)
__main__
As we can see from the figure above, the name of the file is still __main__
not main_file, but why is this? In PyCharm, when we want to run the code,
we usually click the RUN button and it will ask which file we want to
execute. The file we are selecting will be the main Python, and that is why
the __main__ was printed because the current file we are running is the
main file regardless of what name it has. Now let’s discuss this famous line
that you will see:
if __name__ == '__main__':
This if-statement will be true only if this line is located in the main file, it
will not work in the utility or any other files that are not main. We can now
wrap the rest of the code like this:
This means if the file we are running is the main file in Python, the indented
code will be executed.
The figure above is just a tiny screenshot of the documentation that lists the
modules, and we don’t have to remember all of them, but we need to get
familiar with what Python offers so we can use these built-in features when
we need them.
The link to the documentation:
https://docs.python.org/3/py-modindex.html
You can also find them if you can navigate to ‘python 3.10 library root.’
Yours might be a bit different, but click on it and it will expand and give us
a list of installed packages and modules. As an exercise, let’s import some
of the existing modules like the math module, and try to print where it
comes from:
From the figure above, we can see the actual path where this module is
coming from. Another way to display the list of the available modules is to
use the help command:
help('modules')
Back to the ‘math’ module, if you are interested to know what functionality
this module offers, we can use the help keyword again and provide the
name of the module as an argument like this:
help(math)
The help(math) will give us a complete picture of what is in this module. If
we are interested in knowing only the methods in this module, we can use
the dir() function:
print(dir(math))
Let’s use some of these methods, for example, the log()that will return the
natural logarithm of different numbers:
print(math.log(2.35657))
print(math.log(3))
print(math.log(1))
0.8572071720115416
1.0986122886681098
0.0
We can even use the math.pow() method so it will return the value of x
raised to the power of n.
print(math.pow(3, 3))
Output:
27.0
print (math.sqrt(9))
print (math.sqrt(25))
print (math.sqrt(16))
Output:
3.0
5.0
4.0
So far, we have learned how we can create modules and packages, and how
to import these modules from the packages as well. In the previous sections,
we learned about the built-in library of modules that comes installed with
Python. But there is one more thing I want to mention that makes Python
one of the greatest languages ever. These are the millions of python
modules that are created by various Python developers and shared so we
don’t need to waste time creating them. These modules can be downloaded
and imported into our projects because they are developed by the Python
community. Python developers from all around the world created different
modules that we can use. How can we use other developers’ code? You can
use the ‘pip install’ command that will enable you to use these modules and
pieces of code in your projects in seconds. But where I can find and read
more about what other developers created and shared? There is a specific
website called ‘pypi.org’ that is Python Package Index. PyPi is a repository
of software for Python programming languages that helps us to find and
install software that has been developed by the Python community. Here is
the link and screenshot to the actual Python Package Index (PyPi):
https://pypi.org/
At the moment, we have 611,137 users and over 390,352 projects, and these
numbers will only grow in the future. This shows you the real power of
Python. For example, if you want your website to be faster and all the
images to be optimized, you can search if something like that exists in pypi:
You will probably have a list of different projects and all you need to do is
find a package that suits your project and install it. Not all of the packages
will be suitable and maintained regularly so you need to decide on which of
the packages is the best fit for your project. I have shown you these two
websites (Python Module Index Documentation and PyPi) so you can find
modules and packages that will help you build your program faster.
This will list all of the projects but make sure you click on the one that says
‘One-line Jokes for programmers (jokes as service)’:
As you can see, the pyjokes had a couple of versions and at the moment the
latest version is 0.6. If you open this package, you can find additional
information:
From this page, you can read more about this project, how to install the
pyjokes module with pip, and how to use it. You can download the files if
you want, or visit the GitHub page. GitHub is like a Facebook (meta) or
Twitter for programmers. It’s a social network where you can upload your
work so the other developers can clone and download it. You can also see
some statistics, for example, how many people liked your project (there will
be stars) or how many people forked and used your code in their own
projects. There are different ways we can install this package/module, and
first I will try to show you how you can do it with PyCharm. I have created
a copy of my previous projects and I called it ‘chapter5.3-packages.’ If you
are trying this on your own, you can name your project whatever you want.
Now let’s go to the menu and select PyCharm>Preferences and Select
Project: and then find and select the Python Interpreter:
As we can see on the right side, we have a few packages already pre-
installed like pip and setuptools so all you need to do is locate the plus sign
where you can add or search for a new package, in our case, the package we
need to install is pyjokes:
Make sure the right package is selected and click on the button that says
Install Package. After the installation, you will get a message saying you
have successfully installed the pyjokes package. We can confirm this if we
go back to the Python Interpreter where you could see if the package is
listed:
The package will also be in the External Libraries > Site Packages >
pyjokes:
Let’s now read the quick description on the PyPi website and there it says
we can access the jokes if we first import the pyjokes and then use some of
the two functions (get_joke or get_joke):
As you can see, we can install packages easily with PyCharm but this is not
the way most developers install packages. Another, more professional way
to install packages is to use the terminal or Command Prompt. On my
Desktop I have created a folder called chapter5.4-packages and I have
opened it with VS Code editor and created a file inside this folder called
test.py:
The test.py file is empty for now but let’s add the pyjokes code from
PyCharm:
ModuleNotFoundError because the pyjokes package was installed for the
PyCharm IDE only and we can’t use it everywhere. So how can we fix this?
Well, if we go back to pypi.org we can see there is a pip command to install
packages using our terminals or command prompt:
Copy the command ‘pip install pyjokes’ and open the terminal or command
prompt. If you forget some of the terminal commands, I suggest you go
back and read that section again. Instead of using the built-in terminal in VS
Code editor, I will use the machine terminal and check the current directory
using the ‘pwd’ command:
Now I need to go to the Desktop and in the ‘chapter5.4-packages’ folder
where my test.py file is:
As you can see, we are in chapter5.4 folder. Let’s list all available files
there:
The test.py file is the only file I have in the folder chapter5.4-pakcages.
Finally, we can check the pip version. When we installed Python on our
machines, it also installed the pip. Let’s check the pip version by typing pip
-V, and based on your machine this might work or might give you an error
like this one:
If this is the case, we can type pip3 -V and now we should get the right
version:
For me, this means I can install pykokes using the pip3 instead of the pip
command (pip3 install pyjokes):
After you run the ‘pip3 install pyjokes,’ you should get the following
message ‘Successfully installed pyjokes.’ We can also upgrade package
versions as well. As you can see, I have some warnings in green color
alerting me that there is a new version of pip available:
As you can see, after I run the pip3 install command, I have the following
window:
Let’s now test if the package pyjokes is actually working and for this, we
can use the built-in terminal in VS Code or the machine terminal. I will
show you both ways, here is the terminal output:
If we go back to the VS Code editor and use the built-in terminal, you
should have this output:
VS Code terminal is working as well and that is basically what I wanted to
show you about how to find, install, and run packages from the Python
Package Index website. Finally, if you want to uninstall some of the
packages you have already installed, you can use the command pip3
uninstall and the name of the package. For example, if you want to uninstall
the previous package completely you can use one of the following
commands:
If for some reason you want to install a specific version of a package, you
can also do this from your terminal as long as you know the version
number. Most of the packages will have a different number like the pyjokes
have 0.6.0 and this is called versioning because each new version will bring
something new, maybe some bug was discovered in version 0.5.0 and they
rectified and updated it in the new version 0.6.0. Therefore, the version
numbers mean something, they are not just random numbers. If you need to
install a different version of the package, you can do it like this:
This will install the previous version of pyjokes. You can also list all of the
packages installed using the following command:
pip3 list
Summary
This chapter was one of my favorites because we learned a lot about
modules and packages. I hope by now you see the real power of Python
because it doesn’t matter how great a developer or programmer you are, it
will be super hard to shine if you don’t have a backup from your
community. In the next chapter, we will focus on reading and writing to
files.
Chapter 6 – Working with Files in
Python
In most programming languages, you will find the phrase FILE I/O, but
what does it mean? The letter ‘I’ stands for input and the ‘O’ stands for
output. In this chapter, we will cover how we can work with files in
Python. Python has several functions for handling files like open, write, and
read. The key function for working with files in Python is the open()
function so let’s start with it.
Open
In this chapter, I will use the VS Code editor instead of the PyCharm
because I want you to have a book that uses both development tools. I will
create a folder on my desktop called ‘chapter6.1-files’ and inside I will add
two files, the first one will be a Python file called main.py and the second
will be a text file called text.txt file:
As you can see from the figure above, the text.txt file contains the simple
text ‘Hi there!’ and the main.py file is empty for now. The idea is to use the
text.txt file and open it from our main.py file and if you want to do this, you
can use the open () function that is built-in Python:
Here we will use the function open that takes one argument. The argument
is the file name and we need to pass it as a string or in quotes (single or
double). Whatever the function open() returns, the result will be stored in a
variable called text_file. Let’s run the main file and print what we have in
the text_file (I will use the VS Code terminal):
As you can see from the figure above, we can click on the triangle to run
the code instead of typing in the terminal python3 main.py. Regardless of
how you execute the main.py script, you will have the same output, but be
careful, we can read the file only once because of the open() function.
Python treats the open function as a cursor, therefore if you want to read the
same file multiple times, you will run into some problems. Okay let’s test
this and call the read() function multiple times:
print(text_file.read())
print(text_file.read())
print(text_file.read())
print(text_file.read())
If you run the above code, only the first read function will work and will
produce an output, the rest of the read functions will not produce results.
This means that the contents of the file are read as a cursor, one character at
a time, and at the end of the first read(), the cursor will reach the end of the
file. We can fix this if we use the seek() method where we can pass the
index which will determine the position where the reading should start. If
we want to start from the beginning, the index should be zero:
If your text.txt file has multiple lines of content and you need to read the
first line only, you can use the method called readline(). Let’s add a few
more lines in the text.txt file and test this readline() method:
Let’s call the readline() method:
If you need to read all of the lines, then you can use the method called
readlines() but this method will return a List data type with all of the lines
from the file. The list will also include the newline character ‘\n’ that we
use to create a new line. Let’s use this method and check the output:
As a good practice, we need to close the file at the end because you might
want to use it somewhere else so it’s a common practice to close the
file/files that are opened. To do this, we can call the close() method:
text_file.close()
In the next section, I will teach you another way to open and close a file
using the ‘with’ statement.
Write to a file
Remember at the beginning of this chapter when we printed the file, we had
access to the mode=’r’:
The mode =’r’ stands for reading and we can even specify the mode
directly into the ‘with’ statement like this:
The ‘r’ is the default parameter, we don’t need to specify it, but if you are
interested in writing to a file, we need to use the mode=‘w’, where ‘w’
stands for writing into a file. If we change the mode to write and run the
same file, we will end up with an error because we are trying to read from a
file but the mode is set to write:
Before we fix this problem, please check if your text.txt file content is
there. Well, the text file would be empty because we tried to write to a file
without specifying what needs to be written and that will delete the existing
content, therefore you must be careful when you are using the write mode.
What if you want to read and write in the same file? We can use the
mode=’r+’ if we want to read and write. Okay let’s write some content back
to a text.txt file first:
If we open the text.txt file, we can see that we have successfully written the
new content:
This works perfectly but let’s do some further tests, for example, I don’t
think the content is good enough and I want to write a new text and
overwrite the previous one. I can just simply do this:
Let’s open the text file and check if the new content ‘Hi readers!’ has been
added:
As you can see from the figure above, we have the new content plus some
bits of the old content as well. Why is this happening? The reason this is
happening is because the ‘with’ statement will reset the open() method or
will close it for us so the cursor will be back at zero index and we actually
add the content at the beginning of the old existing one.
Append
When we want to add text or content at the end of the existing content, we
can use the append mode. All we need to add is the letter ‘a’ in the mode
like this:
mode='a'
For example, let’s append the text ‘I love Python’ to the existing file:
And if we open the text.txt file, the text should be added at the end:
As an exercise, copy the main1.py file and create a new file called
main2.py. I would like you to experiment a little bit. In the open() method,
instead of loading the file text.txt, try using a file that does not exist in your
directory/folder, for example, use a file called ‘file.txt’. After you have done
everything, run the file and observe what will happen. Here is how the
main2.py file looks before I click on the run button :
If I run the main2.py file, this will be the output:
As you can see from the figure above, a new file called file.txt was created
with ‘I love Python!’ as content. The big question is why this happened. We
tried to write to a file that doesn’t exist, so the open method will try to find
this file but because it can’t it will create a new file for us and add ‘I love
Python!’. Let’s delete the ‘file.txt’ and change the mode to append and run
the file again. The result will be identical for both modes. Finally, let’s
delete the file.txt and use the read and write mode=’r+’ and run the file,
what do you think will happen? Here is the output:
From the figure above, you can see that no file was created and we got the
FileNotFoundError. This is happening because we are trying to read from a
file that doesn’t exist.
File Paths
In this section, we will discuss the importance of file paths. The paths are
very important and they can get complex sometimes because the files we
are trying to open/load/read are not always located in the same
level/directory as the main file. So far, our files (main.py, main1, main2,
text.txt, file.txt) were located in the same folder but we cannot expect to
have all of the files like this in the same directory or folder. Let’s create a
folder inside the chapter6.1-files and call it ‘scripts’:
Inside this folder, let’s create a file called readme.txt where we add the
following simple text:
Great! Now let’s create a new main3.py file and try to open the readme.txt
file like we have done it so far:
As you can see, we are trying to open a file that does not exist at the same
level as the main3.py file. That is why we are getting the FileNotFound
error. The easy solution is to list the directory name in our case ‘scripts’ in
the actual path and add a forward slash before the file name. I know this
sounds confusing and difficult but here is what you need to write:
If you run the main3.py file after we specified the correct path to the file,
you will get the output we wanted:
If you run the file with the absolute path, you should have the same output.
The absolute path is not that common compared to relative paths because
they are nicer and shorter to write. As a developer, you will see this relative
path:
The dot and forward slash at the front means start looking from the current
folder and that is what I have always used when I’m dealing with the file
paths. Another important syntax is double dots and forward slash which
means move up one directory from the current directory:
This will mean look for a folder called scripts that is outside the
‘chapter6.1-files’ current directory. In order for this to work, I will drag and
drop the scripts folder from chapter6.1 to desktop like in the figure below
and use the double dots forward slash to load the file:
https://docs.python.org/3/library/pathlib.html
If we run this file, the Interpreter will see that we have a try-except block
and it will go inside and try and run the code. If we look at the open ()
method, we can see that the file readme1.txt doesn’t exist in the current
directory but the readme.txt file exists. There will be an error thrown in the
try-block and that error will be handled by the except-blook where we just
print the error message. Python comes with pre-defined default error
messages for different types of errors. Now if we want to access the default
error message, we can use a variable that is declared after the ‘as’ keyword
(err). Think of this variable as a container for storing the default error
message and instead of printing the custom message we can print the ‘err’
variable. We can name this variable differently if we want:
As you can see, whatever we put in the except block was printed because
there was an error opening and reading this file. There is something else
that can be done in the except block and that is to raise the current error like
this:
This is how we can handle different errors and I hope that you have now
learned how to handle any error you might get when working with files.
Summary
Congratulations! We have learned a lot in this chapter. I’m confident that
now you can perform any of the I/O operations and handle the errors using
the try and except block.
Chapter 7 – Error Handling
In this final chapter, we will learn how we can handle errors that we might
get in our code. Being a programmer is challenging, not because of the
amount of code you need to write, but because you need to write good
quality code without errors. Code without errors is something we should
always strive to achieve, but I will tell you this, it’s not that simple and
errors can occur anytime. The key point is what we need to do after we
have identified those errors and what mechanism we should use to handle
them so we can avoid breaking our program. Let’s start learning how to
deal with these errors.
Errors in Python
In our code so far, we have seen few errors and I have made those errors on
purpose because you should not be afraid of them. The most important part
is to learn what they mean and after you identify their meaning you need to
take appropriate actions to handle them. For example, the most basic print
function if not written properly will throw us an error:
# 1 SyntaxError
print(')'
# 2 TypeError
print(1 + '2')
The output:
Another error that you might get is called the name error (NameError). I
have listed these errors in the previous sections but let me give you another
example:
# 3 NameError
result = 1 + two
As you can see, we are trying to calculate the integer value with a variable
that is not being defined. The output:
The index error is an error that is common when we have a list data type
and we try to access an element with an index that is out of the list range.
Example:
# 4 IndexError
list_ob = [1, 2, 3, 4]
print(list_ob[3])
print(list_ob[4])
Output:
# 5 KeyError
car_dict = {
'car': 'Ford',
'model': 'Mustang',
'engine': '5.0',
}
print(car_dict['car'])
print(car_dict['Model'])
Output:
From the figure, I have used the key ‘Model’ instead of the key ‘model’ in
all lowercase letters. This will throw a KeyError because there is no key in
the dictionary named ‘Model’.
The next error is called ZeroDivisionError and this will happen when we try
to divide a number with zero, and for some of you, this might be funny but
believe me, these errors can happen more frequently than you think:
# 6 ZeroDivisionError
print(3 / 0)
https://docs.python.org/3/library/exceptions.html
These errors will crash our program and will not let the rest of the code
finish its execution. At the moment, we have few lines of code and if the
error happens in the first line, the code below will never be executed like in
the following example:
print(1 + '2')
if 5 > 3:
print('5 is bigger than 3')
Although the if statement condition is true, the second print will never be
executed because the error that will halt our program happened in the first
line. The errors that will stop our code from execution are known as
exceptions. The Interpreter will go line by line and when he sees this kind
of problem, it will raise an exception causing the program to stop. This is
why we need to learn how to handle errors.
Error Handling
This mechanism will allow us to continue executing the Python script even
though there has been an error somewhere. To explain the error handling, I
will create another file and prompt the user to enter their date of birth:
# Error Handling
If we enter 1999 and press return or enter, we will get the output we
wanted:
As you can see, the program worked just fine, but let’s run it again and this
time enter a text/string instead of a number:
It worked perfectly for strings as well but is this the output we wanted? The
answer is no because we wanted to get the year as a number and maybe
later, we can do some further mathematical calculations, for example,
calculating if you are eligible to get a driver’s license. So, what is the
solution? We can convert the input into integers and by doing this, we will
always get integers as output not strings:
# Error Handling
From the figure above, you can see that our code is now throwing a
ValueError and this is not what we wanted. Just as in the I/O chapter, we
can use the try-except block to handle errors. We put the code that needs to
be executed in the try block but if there are any errors, the except block will
handle them. Make sure you are using the correct indentation:
# Error Handling
try:
dob = int(input('Enter year of birth: '))
print(dob)
except:
print('You have entered invalid value, please'
' enter a valid number!')
Here is what will happen if the user inputs a string instead of a number:
As you can see from the figure above, we didn’t get the error that will block
and terminate the entire program, instead, we are notifying the user what
he/she has done wrong and what he/she needs to do next. Now even if the
user makes a mistake, the Python interpreter will continue its work and
execute the rest of the statements. Let’s add a new print function after the
try-except block and confirm that the program will continue its normal
execution:
Great! No more red errors in our console but our code is far from perfect.
For example, the user inputs a string, and this error will be caught and
handled in the except block, but if the user wants to re-enter a new number
to fix the previous mistake, he/she will need to run the file again. To fix
this, you can simply use a while-loop and set the condition to True, and
wrap the entire try-except block like this:
# Error Handling
while True:
try:
dob = int(input('Enter year of birth: '))
print(dob)
except:
print('You have entered invalid value, please'
' enter a valid number!')
If we run the code now and if user enters a string this will happen:
Great! Our logic works! Well, it works but not perfectly because this will
continue prompting the user to enter a value even if the user entered a
number:
We can fix this by adding an else clause after the except block so we can
break from this loop using the break statement:
# Error Handling
while True:
try:
dob = int(input('Enter year of birth: '))
print(dob)
except:
print('You have entered invalid value, please'
' enter a valid number!')
else:
break
As you can see, after the user enters a number, the while loop will be over.
Except block
As we saw in the I/O chapter, the except block can accept different built-in
errors and we can print them. Python gives us the option to specify what
types of errors we want the except block to handle. Imagine that after the
user enters the year of birth, we want to find how old the user is by
subtracting the current year from the year of birth. To do this, we need to
use the datetime module that allows us to use dates and times, therefore I
would like you to visit the Python documentation in your free time and go
over the basic functions that this module offers:
https://docs.python.org/3/library/datetime.html
# Error Handling
from datetime import date
current_year = date.today().year
while True:
try:
dob = int(input('Enter year of birth: '))
print(current_year - dob)
except:
print('You have entered invalid value, please'
' enter a valid number!')
else:
break
If we run the file and enter 1999, we would get this output (you might get a
different value from 23 based on the year you are reading this book):
This works and now you can even change the except block to handle only
ValueErrors like this:
We can do a lot of calculations in the try block and if any errors happened,
they will be handled by the except block, but in our case, we only asked it
to handle the ValueError errors. Let’s find out if our application can handle
ZeroDivisionError:
# Error Handling
from datetime import date
current_year = date.today().year
while True:
try:
dob = int(input('Enter year of birth: '))
print(current_year - dob)
print(current_year / dob)
except ValueError as err:
print('You have entered invalid value, please'
' enter a valid number!')
else:
break
I deliberately divided the current year with the year of birth because if the
user now inserts zero (0) as a year, this should raise the ZeroDivisionError:
As you can see, the except block does not handle ZeroDivisionError as we
thought it would, so what is the solution? Well, we can create another
except block to handle ZeroDivisionError like this:
# Error Handling
from datetime import date
current_year = date.today().year
while True:
try:
dob = int(input('Enter year of birth: '))
print(current_year - dob)
print(current_year / dob)
except ValueError as err:
print('You have entered invalid value, please'
' enter a valid number!')
except ZeroDivisionError as err:
print('The age should not be zero!')
else:
break
If we run the application now and a user enters a zero (0), this should be
handled now by the second except block:
Let’s create another function that will divide two numbers. This is the code
for the division function:
# Error Handling
return a / b
result = div_numbers(10, '2')
print(result)
If we run the function as it is, we will get TypeError because the second
operand we are passing into the div_numbers function is from type string.
To handle this, we can simply use the try-except block directly in the
function:
# Error Handling
The output will be the print message that tells us that at least one of the
operands we passed into the function is not number:
In the I/O chapter, I mentioned that we can print the built-in Python error
message if we assigned it to a variable and print that variable which will be
the error object itself:
Now if we run the code with the same operands, you will see our custom
print message plus the entire built-in error that comes from Python:
We as developers sometimes need this error message so we can truly know
what is happening because if we have thousands of lines of code, this will
be hard to find, but now from the error message, we can see that the
problem is located on line 5. We can even use the ‘f’ string like this:
This means that we need to handle this error but instead of writing multiple
except blocks, let’s do this instead:
# Error Handling
result = div_numbers(10, 0)
print(result)
Finally block
We have seen the try-except-else block but there is one more block we can
use when we are dealing with exceptions. This block is called finally and it
will run at the end after everything else has been completed. Here is one
example of try-catch-else-finally block:
# Error Handling finally
try:
number = int(input('Enter a number:_ '))
number >= 3
except (TypeError, ValueError) as err:
print(f'Something went wrong: {err}')
else:
print(f'Everything is ok and the result'
f' is {number * 3}')
finally:
print("The try-except block is finished")
If we run this with a string instead of a number, then the except block
should run and handle the errors:
But even in a case of an error, we can see that the finally block is executed
and the message it’s printed. The finally keyword-block is used to define a
block of code that will run when the try-except-else block is finished.
Whatever code we have in the finally block will run no matter what we
have in the previous blocks.
Raise an Exception
Sometimes as a developer, you need to throw an exception if a specific
condition occurs in your code and Python allows us to raise our own
exception using the keyword raise. Please consider the following example:
try:
number = int(input('Enter a number:_ '))
if number < 0:
raise Exception('Enter a number bigger '
'then zero')
number >= 3
except (TypeError, ValueError) as err:
print(f'Something went wrong: {err}')
else:
print(f'Everything is ok and the result'
f' is {number * 3}')
finally:
print("The try-except block is finished")
And if we run the same code again with a value of -1, we will get the
following output:
Great! Now we know how you can raise your own errors or Exceptions.
We can raise any specific built-in Python errors like TypeError if we list
them in the expect block. This is everything I wanted to teach you in this
chapter and you should know that errors are inevitable in any programming
but a good programmer will know how to handle these errors without
crashing the application.
Summary
Congratulations! I’m always sad when I finish the final chapter. Error
handling is a very important concept and most of the time, it’s overlooked
by programmers and that was why I decided to make it the last chapter. We
have done a lot in this book, we started from scratch and most of you I
assume knew nothing about Python but now that is no longer the case.
From here your path is very simple because now you have enough
knowledge to continue learning new and more advanced Python features.
This was my first book about Python and I hope you liked it because very
soon I will announce my second one which will be much more advanced
than this one. In the introduction chapter, I mentioned that I need you to
connect with me on any of the social media because very often I lower my
books to below one dollar so everyone can get them basically for free.
Before I say goodbye, I would like you to check the Projects section below
and attempt to write some of the coding challenges on your own. We had a
fun ride together and now it’s time to go our separate ways.
Projects
In this section, we will have a few projects that will help you understand
how Python works. You should try and write the coding challenges yourself
but if you cannot, it’s not a problem because the final code will be supplied
at the end in the appendix section. Please read the projects instructions
carefully and try to write the code:
https://docs.python.org/3/library/random.html
The second hint is to use the input function so the user can enter a number
but be careful because the user inputs will not be in a number form, they are
always Strings. This means that the user input needs to be converted from
String to Integer. The third hint is to use a while loop that will count how
many times the user has attempted to guess the random number. Before the
while loop, you need some sort of counter variable like this:
number_of_guesses = 1
This variable will be the actual condition in the while loop:
You will need another if statement that will check the opposite. Inside the if
statements, you need to print the message saying what the user should do
next and again use the input function to initiate the user to enter a new
number. After each fail, we need to give the user a chance to enter the
number again, therefore the input function is a must. The final third if-
statement should be if in case the user entered a number that is the same as
the computer random number. In this case, you should use one of the
following statements (please think about which statement you should use
when the user guessed the number):
Break
Pass
continue
After the while loop, you can have another if-else statement where you
mentioned how many times the user guessed the number or if the user
didn’t guess the number, then you need to print what the random number
was. If you can’t solve it on your own, the answer will be in Appendix A,
Project_1: Guess the Random Number Solution.
Project_2: Teacher, Student App
This project will be a bit harder compared to the first one. The project will
be about coding a simple program that will allow a teacher to be created.
Once the teacher is created, he/she can login into the system and view all
students and their scores. Also, the teacher can add a new student and
update students’ scores. The student data will come from a file called
student_scores.txt. Before we dive into this project, these are some of the
things this project will include:
I/O Files
Hashing Passwords
Teacher Login
Teacher Registration
Adding new Students
Updating Students
Removing files
Updating files
Using the datetime module
This is what the application we are going to write will look like:
As shown in the figure above, we have a teacher that will need to register
first. After the registration, the details will be stored in the teachers.txt file.
The teacher can then login with their name and password and will be able
to:
The student details are located in the student_scores.txt file. Each of the
teacher’s actions for the students will be recorded in the student_scores.txt.
I will guide you step by step on how to write this app but if you feel
confident you can do it on your own, then start coding. Before you attempt
to write your code, these are some features that we haven’t talked about in
this book and you will need them to finish this app:
remove(file.txt')
The second method is called rename and it’s used to rename the existing
file:
rename('old_file_name.txt', new_file_name.txt')
Password hashing
This application will not be a real register and login system but I wanted to
give you an app where you install some packages and this was a perfect
opportunity. You will use the pypi.org website where we can find and install
a package called bcrypt:
https://pypi.org/project/bcrypt/
This is good for password hashing and for example, the teacher will try to
register with the password ‘MyPassword’ as a string. This package will take
this string and using the hashing function will turn it into something:
b'$2b$12$fwBh8IUk4pC917unYjStSuwmJXyOlWH2jCXKOazcXikZS7J1wzF9q'
We can use another function from bcrypt that will compare the original
password with the hashed password so it can let the teacher login to the
system. This application is very simple but when working on larger
applications, the security and password hashing is very, very important. To
install this package, you will need to use the:
or
If you are using the PyCharm IDE, you can create a new project and go the
PyCharm> Preferences then find the project name, select the interpreter,
and install the package:
Datetime module
You can read about the date time here:
https://docs.python.org/3/library/datetime.html
We need datetime module because every time the teacher manipulates the
student file, it should record the time or you can create some sort of
timestamp. Please read the next section where I will provide a few
screenshots from the app I have built for this project.
Screenshots and files
To create this app, I would like to include a few screenshots so you can get
an idea of how to write this app. In this project, I have created four Python
files and 2 text files. The Python files are login, main, register, and
student_tasks. The text files are student_scores and teachers. I have used the
PyCharm to create and run this app:
If we press enter/return the same menu will appear again but if we open the
teachers.txt file, this is what we should have:
The new teacher is created with first name, last name, and the hashed
password. They are all comma-separated values.
Now since the teacher is created, we can try to login:
If we press return or enter, it should get a new menu where we can add a
new student, update student scores, and get student scores:
Because the student’s file is empty let’s select option number 3 and create a
new student:
After we press enter or return, let’s check the student_scores.txt file:
As you can see, we have the student’s name, score, the teacher’s name, and
the timestamp. After this, you can use option 2 to update the student score
from 134 to 199:
If we press enter/return, we don’t need to go check the student_scores file
because we can use option 1 from the menu and it will list everything inside
that file including the changes we have made:
As we can see, the Jason score is being updated from 134 to 199 and just to
confirm the student_scores.txt file is also updated:
That is everything this application does. Next, I will provide from each of
the files the starter code so you can just code the main functions:
while teacher_selection != 3:
print(f'Welcome to Teacher Panel \n'
f'What do you want to do (select a number): \n'
f'1) Login\n'
f'2) Register\n'
f'3) Exit\n'
f'Select a number \n'
f'>')
teacher_selection = int(input(''))
if teacher_selection == 2:
print('************** Teacher Registration ************** \n')
print('Your first name')
teacher_name = input('')
print('Your last name')
teacher_lastname = input('')
print('Your login password')
teacher_password = input('')
# call the teacher_register() function with 3 arguments
elif teacher_selection == 1:
print('************** Teacher Login ************** \n')
print('User Name')
teacher_username = input('')
print('Your Password')
teacher_password = input('')
# call the teacher_login function with 2 arguments
# store the result of teacher_login in the variable bellow login_result
login_result = 0
elif teacher_login_input == 2:
print('Input student details')
print('Student name')
student_name = input('')
print('New student score')
student_score = int(input(''))
# Call the update_student_score with 3 arguments
# As arguments use the variables declared above
# from the user input.
# Don't forget the teacher_username should be the last
# variable
elif teacher_login_input == 3:
print('Enroll new student')
print('Student name')
student_name = input('')
print('Student score')
student_score = int(input(''))
# call add_new_student function with 3 arguments
# remember the last argument is the teacher_username
else:
print('You have entered a wrong choice')
Login.py file:
# import bcrypt, after you have installed the bcrypt
# package and the import bcrypt will not cause errors
import bcrypt
Register.py
import bcrypt
student_tasks.py
from os import remove, rename
from datetime import datetime
import random
number_of_guesses = 1
while number_of_guesses < 3:
number_of_guesses += 1
if player_number < the_number:
print(f'The number we are looking is greater than {player_number}')
player_number = int(input(''))
if player_number > the_number:
print(f'The number we are looking is smaller than {player_number}')
player_number = int(input(''))
if player_number == the_number:
break
if player_number == the_number:
if number_of_guesses == 1:
print(f'You guessed the number in your {number_of_guesses}-st attempt!')
elif number_of_guesses == 2:
print(f'You guessed the number in your{number_of_guesses}-nd attempt')
else:
print(f'You guessed the number in your {number_of_guesses}-rd attempt!')
else:
print(f'The number we were looking was {the_number}')
Main.py
teacher_selection = 0
while teacher_selection != 3:
print(f'Welcome to Teacher Panel \n'
f'What do you want to do (select a number): \n'
f'1) Login\n'
f'2) Register\n'
f'3) Exit\n'
f'Select a number \n'
f'>')
teacher_selection = int(input(''))
if teacher_selection == 2:
print('************** Teacher Registration ************** \n')
print('Your first name')
teacher_name = input('')
print('Your last name')
teacher_lastname = input('')
print('Your login password')
teacher_password = input('')
teacher_register(teacher_name, teacher_lastname, teacher_password)
elif teacher_selection == 1:
print('************** Teacher Login ************** \n')
print('User Name')
teacher_username = input('')
print('Your Password')
teacher_password = input('')
login_result = teacher_login(teacher_username, teacher_password)
elif teacher_login_input == 3:
print('Enroll new student')
print('Student name')
student_name = input('')
print('Student score')
student_score = int(input(''))
add_new_student(student_name, student_score, teacher_username)
else:
print('You have entered a wrong choice')
login.py
import bcrypt
student_tasks.py
def get_student_scores():
try:
with open('student_scores.txt', mode='r') as students:
return students.read()
except FileNotFoundError as err:
print('File cannot be found!')
def update_student_score(student_name, student_score, teacher_username):
try:
now = datetime.now()
date_time = now.strftime("%m/%d/%Y, %H:%M:%S")
tmp = open('student_scores.tmp', mode='w')
original_file = open('student_scores.txt', mode='r')
for line in original_file:
content = line.split(', ')
if student_name in content:
tmp.write(f'{student_name}, {student_score}, Updated by Teacher:{teacher_username},
{date_time}\n')
else:
tmp.write(line)
tmp.close()
original_file.close()
remove('student_scores.txt')
rename('student_scores.tmp', 'student_scores.txt')
register.py
import bcrypt
# hashing function
def hash_password(user_password):
# encode the password
bytePwd = user_password.encode('utf-8')
# Generate salt
mySalt = bcrypt.gensalt()
# Hash password
user_password_hashed = bcrypt.hashpw(bytePwd, mySalt)
return user_password_hashed
Appendix B: My Bestseller books
If you still want to learn more about the basic to intermediate JavaScript,
HTML and CSS then I suggest getting my best seller eBook:
https://www.amazon.com/Web-Design-HTML5-CSS-responsive-
ebook/dp/B097DWX99H/ref=sr_1_1?
keywords=rick+sekuloski&qid=1650700347&sprefix=rick+seku%2Caps%
2C365&sr=8-1
https://www.amazon.com/Master-Modern-JavaScript-Fast-programming-
ebook/dp/B09MB4JTMT/ref=sr_1_2?
keywords=rick+sekuloski&qid=1650700372&sprefix=rick+seku%2Caps%
2C365&sr=8-2
https://www.amazon.com/JavaScript-Zero-Hero-Complete-Programming-
ebook/dp/B09R73RWH2/ref=sr_1_3?
keywords=rick+sekuloski&qid=1650700372&sprefix=rick+seku%2Caps%
2C365&sr=8-3
https://www.amazon.com/JavaScript-Concepts-Exercises-Mastering-
exercises-ebook/dp/B0B4W92N3R/ref=sr_1_8?
crid=1D8WW0MGX5WL0&keywords=rick+sekuloski&qid=1657010993
&sprefix=rick+sekulosk%2Caps%2C290&sr=8-
8&asin=B0B4W92N3R&revisionId=&format=2&depth=1
https://www.amazon.com/HTML-CSS-Complete-Development-
Guide-ebook/dp/B09YNQFRYN/ref=sr_1_4?
crid=1D8WW0MGX5WL0&keywords=rick+sekuloski&qid=165
7010950&sprefix=rick+sekulosk%2Caps%2C290&sr=8-
4&asin=B09YNQFRYN&revisionId=e1f1dcc3&format=1&depth
=1
https://www.amazon.com/JavaScript-Interview-questions-Up-Date-
ebook/dp/B0B5V4ZS51/ref=sr_1_9?
keywords=rick+sekuloski&qid=1661072932&sprefix=rick+sek%2Caps%2
C341&sr=8-9
https://docs.python.org/3/