Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
9 views

Python Programming[1]

Uploaded by

adhityaaggarwal
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
9 views

Python Programming[1]

Uploaded by

adhityaaggarwal
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 441

Python Tutorial - Introduction

Python is a widely used high-level dynamic programming language. It is a


very simple, friendly and easy to learn programming language. It is the
best choice for a beginner programmer. Python source code is also
available under GNU General Public License (GPL).

Who use Python?

As mentioned above, it is a widely used programming language. Some of


the places where Python is used are :

 Google - Python is one of the key language used in google.


 Philips - Philips uses Python for the sequencing language
(language that tells what steps each robot should take).
 Frequentis - Frequentis is the originator of TAPTools, a software
product that is used for air traffic control in many airports. This
particular tool provides updates on the weather and runway
conditions to air traffic controllers. So, you depend on Python when
you fly.
 Quora - Quora also chose Python for its development.
 CodesDope - Yes, we also use Python.
 Battlefield 2 - Battlefield 2 uses Python to implement core elements
of its gameplay such as score-keeping and team-balancing.
 Instagram - Instagram also uses Python for its backend.
 Walt Disney Feature Animation - Walt Disney Feature Animation
is also using Python to make their animation production system
more efficient in scripting.
 NASA - Johnson Space center uses Python in its Integrated
Planning System as the standard scripting language.
 Civilization 4 - The new addition to the legendary strategy game
series has all of its inner logic, including AI, implemented in Python.
 YouTube - We all love youtube and and youtube loves using Python
 DropBox - Dropbox allows us to store, sync, and share almost
anything using the power of Python.
 Pinterest - Pinterest is a visual discovery tool that allows users to
showcase their interests through the posting of pictures.

This list is never-ending, with new additions to it every day, but I hope it is
enough to motivate you to learn Python.

How is Python simple?

I will write a code to print "Hello World" on the screen in C, Java and
Python. Decide yourself which is simpler.

#include <stdio.h>
main()
{
printf("Hello World");
}
Java

class hello
{
public static void main(String []args)
{
System.out.println("Hello World");
}
}

Python

print("Hello World")

How to run Python

Running Python In Windows

1. Download Python3.
2. Install it.

3. Open IDLE (PythonGUI).

To run directly on shell

4. Type the code given below and press Enter and then you can see Hello
World printed on the screen.

print("Hello World")
To run from a file

4. Go to File -> New File


5. Type the Python code given below and save it.

print("Hello World")

6. Go to Run -> Run Module and you will see Hello World printed.
Python Print
Python is known for its simplicity. So, let's tell the computer to say hello to
us. (We are assuming that you have gone through the Introduction
chapter first and know how to write and compile Python programs. If not,
then first go through the Introduction chapter.)

print("Hello World")

Output

Hello World
And that's it!
To print any message on screen, just type print() and that message
within single quotes ' ' or double quotes " " inside print(). Isn't it
simple?

print() is a function which prints something on screen. (You will learn


about functions in later sections)

In the above example, we printed the message by enclosing it within


double quotes " " i.e., "Hello World". Let’s print the same message by
enclosing it within single quotes ' '.

print('Hello World')

Output

Hello World
We got the same output on printing "Hello World" and 'Hello World'.
Let's see some more examples:
print("Hey! I am an upcoming coder")
print("I am going to step into world of programming")

Output

Hey! I am an upcoming coder


I am going to step into world of programming
print("I am loving this. Print, print and print!")

Output

I am loving this. Print, print and print!

x = 10
y = 5
print("sum of", x, "and", y, "is", x+y)

Output

sum of 10 and 5 is 15
You might have guessed what is happening here, yet let me explain a bit.

x = 10 → We are taking a variable x and giving it a value of 10. Yeah,


similar to what we do in Maths.

y = 5 → Similar to x = 10, y is a variable which is assigned a value of 5.

print("sum of",x,"and",y,"is",x+y) → We already know that


the print() function prints something for us but what's inside it is
interesting. Let’s have a look at it.

Take a note that "sum of", "and" and "is" are written inside " " (we
can also use ' ') but x, y and x+y are not. All these six arguments are
separated using commas (,).

Whatever we write inside " " or ' ' gets printed as it is and is not
evaluated. So, 'Sum of', 'and' and 'is' got printed as it is.
But x, y and x+y are not written inside ' ' or " ", as they are variables
and the compiler will print their values.

That's why the value of x (i.e., 10) got printed. Similarly, the value
of y (i.e., 5) got printed and at last, the value of x+y got calculated (i.e.,
15) and printed.

Note that using comma (,) also adds a space. For example, you can see a space
printed between "sum of" and 10 i.e., "sum of 10".

Now, let's get this more clear.

x = 10
print(x)
print('x')

Output

10
x
As you can see, 'x' printed x and x printed 10. This means that 'x' is a simple
alphabetical x and x (without quotes) is a variable with a value of 10.

x = "Hello World"
print(x)
print('x')

Output

Hello World
x
Here also, x is a variable which stores a value of "Hello World", so print(x) printed
Hello World. Whereas, 'x' is simple x and thus, print('x') printed x.
By practice, you will learn these things more effectively. So, make sure to solve
questions from the Practice Section.

Note that there is no difference between ' ' (single quotes) and " " (double quotes) in
Python.

Let's have some fun

Try the following code.

print("a" + "b")

Output

ab
Let’s have a look at another example.

print('Fun'*3)

Output

FunFunFun
The message to be printed is equivalent to "Fun"+"Fun"+"Fun".

This is the Pythonic way to get your things done. These types of features of Python
can be of good use for you in the future.
Let's Do Some Error Analysis

Do you know what a harsh reality is? The more you write code the more
errors you get.

Do you know what makes a programmer a ‘great programmer’? There are


many things but one of the most critical ones is the ability to hunt down
bugs/errors.

So, let’s start with the first step of analyzing an error.

What if we type this :

x = 10
prin(x)

We have learned about print but what is prin? Of course, not something
our Python knows and thus running this code will display the following
error.

Traceback (most recent call last):


File "<stdin>", line 1, in <module>
NameError: name 'prin' is not defined

Let’s analyze this error and extract some information which is useful for
us to fix this.

This is telling us that there is an error in line 1.

NameError: name 'prin' is not defined → 'prin' is nothing for our


program because it is not defined anywhere in our program. That's why it
is giving us an error.

Name Error is a type of error. Python has few pre-defined error types. You
can also make your own. We will look at it in a later chapter.
Go back to your code and then to that line (prin(x)) and fix it.

In future, when writing long code, make sure that you run your code from time to time
while writing instead of completing your whole code and running at last. This will
make debugging your code a lot easier.

Python Basics
Now that you know how to print any value on the screen, let’s look into
some basics and terminologies you should know while learning Python.
This is just an introductory chapter to make you familiar with the common
terminologies and basic concepts.

Python Statements

A program is nothing but just a set of instructions that are executed by the
computer to perform some task. In Python, the instructions that can be
executed by a Python interpreter are called statements.

Look at the following code.

num = 10
print('num =', num)

This code has two statements. The first statement num = 10 assigns a
value of 10 to a variable num, and the second statement print('num =',
num) prints num = 10.

Python Multiline Statements


Sometimes a statement can get lengthy and we might want to break it into
multiple lines just to make it more readable. We can do so by using
the line continuation character (\).

Consider the following statement.

num = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10

This statement can be written over multiple lines as follows.

num = 1 + 2 + 3 + \
4 + 5 + 6 + \
7 + 8 + 9 + 10

Look at another example in which a long text is divided into multiple substrings that
are written over different lines.

message = "Hello learners, Python is a wonderful language to code in. " \


"This text is written over multiple lines to improve readability " \
"but it will be printed in a single line on screen."

print(message)

Output

Hello learners, Python is a wonderful language to code in. This text is written over
multiple lines to improve readability but it will be printed in a single line on screen.

Parentheses ( ), brackets [ ], and braces { } automatically applies line


continuation to anything written inside them and we don’t need to explicitly
use line continuation character (\). The multiline statements in the above
two examples can be rewritten using parentheses as shown below.

num = (1 + 2 + 3 +
4 + 5 + 6 +
7 + 8 + 9 + 10)
message = ("Hello learners, Python is a wonderful language to code in. "
"This text is written over multiple lines to improve readability "
"but it will be printed in a single line on screen.")

Python Multiple Statements in a Single Line

In Python, we can write multiple statements in a single line by separating


them by semicolons (;).

a = 10; print(a); print('Hello World')

Output
10
Hello World
But this is generally not practised by Python programmers and you should
avoid it too.

Python Comments

Do you know, you can include some comments in your code which won't
be compiled and your computer will simply ignore them while running your
code. They are written to make our code more readable. Let’s look at them
for better understanding.

Python Single Line Comments


Single line comments always start with '#'. The next line is not a part of
the comment.

# I am using a variable x
x = 10
print(x)

Output

10

Python Multiline Comments


You can also write comments which extend upto multiple lines. Multiline
comments are written within 3 single quotes ''' ''' or 3 double
quotes """ """.

'''
Multiline comment
Using variable x and y.
'''
x = 10
y = 5
print(x+y)

Output

15

Why to use Comments?

As mentioned earlier, it makes your code more understandable. Assume


that you have written a software and after releasing it, you hired a few
good programmers for its maintenance. Without comments, it would be a
very difficult job for them to understand your code. And most of the time it
happens that the person who has written a code is not the one who is
going to modify it. So, make a habit of writing good comments.

It is also not recommended to write too many comments. A clean and good code can
be understood without comments.

Python Keywords

There are few words that are used by the Python language itself. So, we
can't use those words for the name of our variables as they are reserved
for Python. For example, take the function which we have been using till
now - print. The name ‘print’ is used by Python itself and thus we can’t
use it as our variable name. Doing so will give us an error.

Here is the list of a few keywords reserved for Python.

and assert as

async await break

class continue def

del elif else

except finally for


from global if

import in is

lambda nonlocal not

or pass raise

return try while

with yield True

False None

Python Identifiers

An identifier is a name we give to entities like variables, functions, classes,


etc. We will learn about these entities in later chapters. For now, just
remember that whenever we define an entity, we assign it a name which
is called an identifier. For example,

x = 10

Here, x is an identifier.
Rules for writing Python Identifiers

1. Identifiers can be a combination of lowercase (a - z) and uppercase


(A - Z) letters, digits (0 - 9) or an underscore (_). Special characters
(*, %, #, !, @, $, etc.) cannot be present in identifiers. Eg,
Message1, num_max are valid identifiers and num@ is an invalid
identifier.
2. Identifiers cannot start with a digit. Eg, 2num is an invalid identifier.
3. In Python, identifier names are case-sensitive, i.e. num and Num
will be treated as different identifiers.
4. Keywords cannot be used as identifiers. Eg, break is an invalid
identifier.

Good Practices to Name Python Identifiers

There are some good practices to name identifiers which should be


followed but are not mandatory.

1. Identifiers should be kept meaningful so that understanding


code is easier. Therefore, instead of using names like
,x, y, a, b, etc for identifiers, use some meaningful names. For
example, if we are storing the product of two numbers in a
variable, then the name of that variable should be kept
as product.
2. Names of variables and functions should contain only
lowercase letters. For example, name, display().
3. If you want to name a variable or function with multiple words,
then name it as multiple words separated by underscores. For
example, first_name and last_name.
4. Class names should start with capital letters. For
example, Person, Student, Books, etc.
5. If you want to name a class with multiple words, then name it
in CamelCase format. For
example, StudentRecord, BookAuthor, etc.
6. Avoid putting underscore (_) or double underscore (__) at the
start and end of identifier names, because these have some
special meaning in Python.

7. Python Variables

8.
9. From the previous sections, you must have got a basic idea
about what a variable is. In short, it is similar to what we use
in Maths but a lot more than that.
10. Basically, a variable is used to store a value. You can
think of a variable as storage that has a name and stores
some value.

Consider the following statement.

a = 10

Here, a is a variable that stores a value of 10.


Reassigning Values to Variables in Python

11. We can change the value of a variable anytime.


12.
13. # declaring a variable count
14. count = 1
15. print(count)
16.
17. # reassigning value to the variable count
18. count = "Hello World"
19. print(count)

20. Output
21. 1
Hello World
In the above example, a variable count is assigned a value of 1 and its value is
printed. Then the variable is reassigned a value of “Hello World” making its value
equal to “Hello World”.

22. Assigning Values to Multiple


Variables in Python
23.
24. Look at the following example in which values are assigned to the
variables a and b.
25. a=1
26. b=2
27. print(a,b)

28. Output
29. 1 2
30. We already know how to do this. But we can also assign the values in
the above example as shown below.
31. a, b = 1, 2

32. If multiple variables have to be assigned the same value, then we can
do that in a single line as follows.
33. a = b = "Blue"

34. Cool, right?


35. Python Swapping
36.
37. Swapping means interchanging the values of two variables. eg - if x is
10 and y is 5 then after swapping x will be 5 and y will be 10.
38. So, let's see how to do this.
39. x = 10
40. y = 5
41. x,y = y,x
42. print(x)
43. print(y)

44. Output
45. 5
10
46. And it's done. Yes, we know it is easy and that's why we love Python.
47. Deleting Variables in Python
48.
49. We can delete Python variables using the keyword del.
50. color = "Blue"
51. del color

52. In this example, del color deletes the variable color. After deleting, if
you will try to access or print the variable color, it will result in an error.
53. Memory Management in Python
54.
55. In Python, when we use a variable, it is stored in the memory of the
computer. For example, two variables having values 5 and 10 will be
stored in two different memory locations by Python.
56. If a variable is assigned a particular value, then that variable points to
the memory location of that value. Consider the following example.
57. a = 10

58. Here, the value 10 is stored in some memory location and the variable
a points to the memory location where 10 is stored.
59.
60. We can check the memory location of a value by using
the id() function.
61. a = 10
62. print(id(10)) # printing memory location of value 10
63. print(id(a)) # printing memory location of value assigned to variable
a

64. Output
65. 9079296
9079296
66. id(10) returns the memory location where the value 10 is stored
and id(a) returns the memory location of the value assigned to the
variable a. Both returned the value 9079296 which is the memory
address where 10 is stored.
67.
68. This id will be different every time you run your program because a
different memory in the CPU will be allocated everytime.
69. Look at another example.
70. a = 10
71. b = 20
72. print(id(a)) # printing memory location pointed by variable a
73. print(id(b)) # printing memory location pointed by variable b

74. Output
75. 9079296
9079616
76. From the output, we can see that the variables a and b point to the
memory locations 9079296 and 9079616 respectively. This also
means that the values 10 and 20 are stored in the memory locations
9079296 and 9079616 respectively.
77. We can also point two variables to the same memory location.
78. var1 = 10
79. var2 = var1
80. print(id(var1)) # printing memory location pointed by variable var1
81. print(id(var2)) # printing memory location pointed by variable var2

82. Output
83. 9079296
9079296
84. In this example, var2 is assigned the value of var1 and so both the
variables point to the same memory address.

Python Data Types

In previous chapters, we dealt with variables many times. We used them to store
values like 10 (which is an integer), “Hello World” (which is text) and many more. In
fact, we can use variables to store many more types of data like a list of integers,
decimal values, etc.

The different types of data like an integer and a text are not the same for us and
neither for Python or a computer. In fact, different types of data also need a different
amount of space to get stored in memory. For example, the amount of memory
needed to store a text value and an integer value is different. Thus, Python has
different built-in data types to handle different data. We will look at those different
types of data in this chapter.
type() Function in Python

Python also provides a function type() to get the type of any data. Let’s look at an
example.

print(type(10))

Output

<class 'int'>

We passed the value 10 to the type() function and printed the result. We got the
output as <class 'int'>. This means that the data type of 10 is int (integer).

Let’s check the data type of 10.5.

print(type(10.5))

Output

<class 'float'>

Here we got the output as <class 'float'>, which means that data type of 10.5 is float
(a number with decimal).

Let’s look at some important data types in Python.

1. Numbers
2. Boolean
3. String
4. List
5. Tuple
6. Dictionary
7. Set

We will go through only a brief introduction of the String, List, Tuple, Dictionary and
Set data types because these are explained in detail in separate chapters.

Python Numbers

Numbers consist of all the numeric values. Numbers in Python are classified into the
following data types.

 int - Integers don’t have decimal and can have any length as long as the
required memory is available. For example, 3, 855, etc.
 float - Floating point numbers are the ones having decimal. For example,
2.564728.
 complex - Complex numbers, as in Mathematics, have the form a + bj,
where a is the real part and b is the imaginary part. For example, 2 + 3j, 6j.

a = 56
print(a, "has a type", type(a))

b = 56.48
print(b, "has a type", type(b))

c = 5 + 6j
print(c, "has a type", type(c))

Output
56 has a type <class 'int'>
56.48 has a type <class 'float'>
(5+6j) has a type <class 'complex'>

In the above example, variables a, b and c are given values of type int, float and
complex respectively.

Note that 8 is an int but 8.0 is a float.

Python Boolean

A Boolean data type consists of two values - True and False. These two values are
also the keywords reserved by Python.

print(True, "has a type", type(True))


print(False, "has a type", type(False))

Output

True has a type <class 'bool'>


False has a type <class 'bool'>

We can see that the type of True and False is bool (bool is for Boolean). We can
also assign any variable a value of True or False. See the example given below.

a = True
b = False
print(a)
print(b)

Output

True
False

Python String
String is a collection of characters. In simple English, it is a letter, word, sentence or
a collection of sentences. You will go through a whole chapter on string later. So,
leave it for that time. For now, just understand that a string is a collection of
characters and these are written within ' ' or " ". So, Hello World written within '
' i.e. 'Hello World' is a string.

print("Hello World", "has a type", type("Hello World"))


print('Hello World', "has a type", type('Hello World'))
print('word', "has a type", type('word'))
print('a', "has a type", type('a'))
print('@', "has a type", type('@'))
print('23', "has a type", type('23'))

Output

Hello World has a type <class 'str'>


Hello World has a type <class 'str'>
word has a type <class 'str'>
a has a type <class 'str'>
@ has a type <class 'str'>
23 has a type <class 'str'>
Note that anything written within ' ' or " " is a string. In the above example, ‘@’ and
‘23’ are also of type str (str is for String).

Look at another example.

a = "23"
print(a, "has type", type(a))

b = 23
print(b, "has type", type(b))

Output

23 has type <class 'str'>


23 has type <class 'int'>

You can see that the data type of the value assigned to a is str because it is written
within "".
Even a digit written within ' ' or " " is treated as a string and not as an integer or a
float. For example, print(type("8")) will give us <class 'str'>.

There are other data types like list, tuple, dictionary and set which are used to store
a collection of data rather than a single data. You will learn about them in detail in
later chapters. Here, we are just going to show you how to declare them and check
their data type.

Python List

A list is a collection of items.

The items in a list are enclosed within brackets [ ] and separated by commas.

a = [2, "Hello", True, 100, 2]

Here [2, "Hello", True, 100, 2] is a list.

a = [2, "Hello", True, 100, 2]


print(a, "has type", type(a))

Output

[2, 'Hello', True, 100, 2] has type <class 'list'>

Python Tuple

Tuples are the same as lists. The difference is that tuples are immutable while lists
are not. This means that once tuples are created, they cannot be modified.

The items in a tuple are enclosed within parentheses ( ) and separated by commas.

a = (2, "Hello", True, 100, 2)

Here (2, "Hello", True, 100, 2) is a tuple.

a = (2, "Hello", True, 100, 2)


print(a, "has a type", type(a))

Output

(2, 'Hello', True, 100, 2) has a type <class 'tuple'>

Python Dictionary

A dictionary is a collection of data in the form of key-value pairs. It can be


understood as a mapping structure where keys are mapped to values.
Let’s just look at an example, you will go through an entire chapter on dictionary later
on.

fruit = {'mango':40,'banana':10,'cherry':20}

Here, the dictionary has three items - 'mango':40, 'banana':10 and, 'cherry':20

In the first item, the key is mango and the value is 40. Similarly, there is a key-value
pair in the other two items as well.

fruit = {'mango':40,'banana':10,'cherry':20}
print(fruit, "has type", type(fruit))

Output

{'mango': 40, 'banana': 10, 'cherry': 20} has type <class 'dict'>

Python Set

A set is an unordered collection of unique and immutable items. This means that the
items in a set cannot be repeated and cannot be changed after the set is created.
We can also perform mathematical operations like union, intersection, etc., with sets.

The items in a set are enclosed within braces { } and separated by commas.

a = {1, 2, 3}

Here {1, 2, 3} is a set.

a = {1, 2, 3}
print(a, "has type", type(a))

Output

{1, 2, 3} has type <class 'set'>

Type Conversion

Suppose we are writing some code and we have a variable having a value 10 (an
integer) and at some point of time we want it to be a string i.e., “10”. Or a more
practical case would be to convert a float (10.2) to an integer (10). We can easily do
so in Python. Let’s see how.

Python provides a list of functions for converting from one data type to another.

Python int()
It converts a value into an int.

Floating point numbers can be converted to integer.

print(int(4.6))

Output

Floating point number (4.6) is converted to int (4). Notice that 4.6 is not rounded off
to 5, but the highest integer less than or equal to 4.6 is returned.

If a Boolean value is converted into an int, then True returns 1 and False returns 0.

print(int(True))
print(int(False))

Output

1
0

If a string consists of only numbers, then it can be converted into an integer.

print(int("10"))

Output

10

However, if a string consists of some non-numeric value, then it will throw an error
on converting it to integer.

print(int("Hello"))

Output

Traceback (most recent call last): File "<stdin>", line 1, in ValueError: invalid literal for
int() with base 10: 'hello'

Python float()
It converts a value into a float.

Integers can be converted to float.

print(float(46))

Output

46.0

If a Boolean value is converted into a float,


then True returns 1.0 and False returns 0.0.

print(float(True))
print(float(False))

Output

1.0
0.0

If a string consists of only numbers, then it can be converted into a float.

print(float("10"))
print(float("4.6"))

Output

10.0
4.6

However, if a string consists of some non-numeric value, then it will throw an error
on converting it to float.

print(float("Hello"))

Output

Traceback (most recent call last): File "<stdin>", line 1, in ValueError: could not convert
string to float: hello

Python str()
It converts a value into a string.

It can convert numbers, boolean values and also list, tuples, dictionaries and tuples
into strings.

a = str(46)
print(type(a))

Output

<class 'str'>

From the above example, we can see that the integer (46) got converted into a string
(‘46’).

print(str(46))
print(str(4.6))
print(str(True))
print(str([1, 2, 3, 4]))
print(str({'a':'1','b':'2'}))

Output

46
4.6
True
[1, 2, 3, 4]
{'a': '1', 'b': '2'}

Python bool()

It converts a value into a boolean.

If a non-zero value is passed to the bool() function, then it returns True. Otherwise,
if a null value (0, 0.0, etc), a False value (value or expression equivalent to False),
None or an empty sequence or mapping ([ ], { }, ( ), etc) is passed to the function,
then it returns False.

print(bool(0.5))
print(bool(True))
print(bool(False))
print(bool(0.0))
print(bool(None))
print(bool("Hello"))
print(bool([ ]))

Output

True
True
False
False
False
True
False

Apart from these functions, there are functions list(), tuple(), dict() and set() to
convert values to list, tuple, dictionary and set respectively which we will cover in
their respective chapters.

Python type() and Python isinstance()

As you have seen, the type() function is used to determine the data type of a
variable or a value.

print(type(10))

Output

<class 'int'>
In the above example, we can see that the data type of 10 is int.

However, you must be wondering why we are getting the type as class 'int'. What
is the significance of ‘class’?

In Python, data types are predefined classes and variables/values are the objects of
these classes. Therefore, in the above example, int is a class and 10 is an object
of int. Don’t worry if you are not getting this. You will learn about classes and objects
later. For now, just keep in mind that int is a class and 10 belongs to the class int.
Similarly, str is a class and “Hello World” belongs to the class str, list is a class
and [1, 2, 3] belongs to the class list, and so on.

isinstance() function is used to check if an object belongs to a particular class.

Look at the following example.

print(isinstance(3, int)

Output

True
isinstance(3, int) returned True because 3 is of type int or we can say that 3
belongs to the class int.

Let’s see some more examples.

a = 3
print(isinstance(a, float))

b = "Python"
print(isinstance(b, str))

c = True
print(isinstance(c, bool))

Output

False
True
True
Python Input

Now, you know how to print something on the screen and not just that, you also
know about the different data types available in Python and how to use them. Let’s
move forward with this chapter and learn a new Python concept.

Till now, we have assigned values to variables in the code. However, there can be
times when we want the user to enter the value which has to be assigned to a
variable. Take an example of a calculator in which the user enters the values to be
added or subtracted.

input() function is used to take input from the user. So, let’s see how to take values
entered by the user.

name = input()

Output

xyz

The statement in the above code will read the value entered by the user (xyz) and
will assign it to the variable name.

We can also print a message on the screen while asking the user for input by
passing that message to the input() function.

name = input("What is your name >>>")

Output

What is your name >>>xyz

In this example, the message “What is your name >>>” will get printed on the screen
and the value entered by the user after that will be assigned to the variable name. The
message displayed on the screen while taking input is helpful to understand what
type of input the user is expected to enter. Therefore, it is a good practice to display
such messages while taking input.

Let’s look at some other examples.


print("Enter your name")
x = input()
y = input("age >>>") #age>>> will be printed before input
print("Your name is",x,"and","and your age is",y)

Output

Enter your name


xyz
age >>>20
Your name is xyz and and your age is 20

print("Enter your wish")


wish = input()
print("May your wish come true!")

Output

Enter your wish


I want to be greatest coder ever
May your wish come true!

What does input() do?

x = input(">>>")
print(x)

Output

>>>10
10

If the user enters 10, then the first statement is equivalent to x = '10'.

We gave 10 to input(). You can think that input() becomes 10. So, x =
input() will be equivalent to x = 10 after the input is given.
Taking String Input in Python

By default, any input entered by the user is of type string.

num = input()
print(type(num))

Output

10
<class 'str'>

As you can see, the user entered a number 10 as input but its type is str. Therefore,
any input entered by the user is always read as string.

Taking Integer Input in Python

We just saw that input is always read as a string. So what if we want to read an
integer?

We can do this by converting the string input to int using the int() function.

x = int(input("Enter an integer >>>"))


print ("You have entered", x)

Output

Enter an integer <<<12


You have entered 12

int() changes a string to an integer. For example, int('12') will give us an integer
12.

In the above example, we are taking the input from the user as a string with
the input() function and then we are using int() to change it to an integer. So, if a
user enters 12, then input() will become '12' (a string) and the above
statement int(input()) will become int('12') and we will get 12 as an integer.
We can also break the above example in one more step to understand better.

x = input("Enter an integer >>>")


y = int(x)
print("You have entered", y)

Output

Enter an integer <<<12


You have entered 12

Here, we passed 12. Thus, x became '12' and then int(x) turned it into an integer.

Taking Float (Decimals) Input in Python

Similar to taking input of integers, we can use float() to take an input from the user
and change it to a float.

x = float(input())
print(x)
print(type(x))

Output

12.32
12.32
<class 'float'>

It is similar to taking input of integers. We entered 12.32 to input() which


returned '12.32' (a string). Thus, the
expression float(input()) became float('12.32'), and that gave us a float 12.32.

Let your computer do some maths for you

import math
print(math.sin(30))
print(math.cos(10))
print(math.pow(2,3))

Output

-0.9880316240928618
-0.8390715290764524
8.0

import math → This will include Python's inbuilt directory 'math'. It contains many
mathematical functions for our use. import is a keyword used to import any available
directory.

math.sin() computes sine of a given angle.

math.cos() computes cosine of a given angle.

math.pow(a,b) computes a raised to the power of b (a^b).

We have imported 'math' and these functions are provided by it, so 'math.sin()' can
be understood as the sin() function from the 'math' directory.

Python Operators

We have learned about different data types, showing their value on screen and also
about taking input from a user. That was all the basics of Python. From now we will
gradually proceed to more programmatic concepts of Python.

This chapter is about performing operations like addition, subtraction, etc similar to
what we do in Maths.

In Python, there are symbols which are used to perform certain operations on
variables. These symbols are known as operators. For example, (+) is an operator
which is used for adding the values of two variables.

Let's see different types of operators in Python.

 Arithmetic Operators
 Relational Operators
 Logical Operators
 Assignment Operators
 Identity Operators
 Membership Operators

Python Arithmetic Operators

Arithmetic Operators are the type of operators which take numerical values as their
operands and return a single numerical value.

Let's take two variables a and b having values 3 and 2 respectively.

Operator Description Example

+ Adds operands a+b=5

- Subtracts right operand from left operand a-b=1

* Multiplies both operands a*b=6

/ Quotient of division of left operand by right operand a / b = 1.5 (floa

// Quotient of division of left operand by right a // b = 1 (int)

% Remainder of division of left operand by right operand a%b=1


** Left operand raised to the power of right operand a ** b = 9

a = 3
b = 2
print("sum =", a + b)
print("difference =", a - b)
print("product =", a * b)
print("quotient =", a / b)
print("quotient (integer) =", a // b)
print("remainder =", a % b)
print("power =", a ** b)

Output

sum = 5
difference = 1
product = 6
quotient = 1.5
quotient (integer) = 1
remainder = 1
power = 9
a, b = 3, 2 → Variables a and b are assigned values 3 and 2 respectively.

print("sum =", (a + b)) → sum = got printed as it is because it is enclosed within "
". After that, the expression (a + b) got evaluated and its value (3 + 2 = 5) got
printed. Thus, sum = 5 got printed. Similarly, other statements got evaluated and
printed on the screen.

We can also introduce a third variable to store the result of an operation as done in
the following example.

a, b = 3, 2 # assigning values to variables a and b


c = a + b # storing sum of a and b in c
print("sum =", c)

Output

sum = 5
Difference Between / and // Operators in Python

Both / and // operators divide the operands and return the quotient. The difference
is that / returns the quotient as it is while // returns the quotient by truncating its
fractional part and returning only the integer part.

a, b = 5, 3 # assigning values to variables a and b

print("quotient =", a / b)
print("quotient (integer) =", a // b)

Output

quotient = 1.6666666666666667
quotient (integer) = 1
// operator only truncates the fractional part of the quotient and not rounds off the
quotient to the nearest integer.

Another difference between the two operators is that / always return a float (even if
the operand is perfectly divisible) and // always return an integer.

a, b = 4, 2 # assigning values to variables a and b

print("quotient =", (a / b), "of type", type(a / b))


print("quotient (integer) =", (a // b), "of type", type(a // b))

Output

quotient = 2.0 of type <class 'float'>


quotient (integer) = 2 of type <class 'int'>
As you can see, / returned the quotient as 2.0 which is of type float,
whereas // returned the quotient as 2 which is of type int.

Python Relational Operators


Relational Operators check the relationship between two operands. They
return True if the relationship is true and False if it is false.

Following is the list of relational operators in Python.

Again, take two variables a and b having values 3 and 2 respectively.

Operator Description Example

== Equal to (a == b) is False

!= Not equal to (a != b) is True

> Greater than (a > b) is True

< Less than (a < b) is False

>= Greater than or equal to (a >= b) is True

<= Less than or equal to (a <= b) is False

== → It is used to check if two values are equal or not. It returns True if the values
are equal, otherwise it returns False.

print(2==2)
print(2==3)

Output

True
False
!= → It is just the opposite of ==. It is True if both values are not equal and False if
they are equal.

print(2!=2)
print(2!=3)

Output

False
True
> → Similar to > of mathematics. It is used to check if one value is greater than
another or not.

print(2>3)
print(2<3)

Output

False
True
>= (greater than or equal to) → It is similar to > but will be True even if the values are
equal. It is similar to >= of Maths.

print(2>=3)
print(2>=2)

Output

False
True
a = 3
b = 2
print("(a == b) :", a == b)
print("(a != b) :", a != b)
print("(a > b) :", a > b)
print("(a < b) :", a < b)
print("(a >= b) :", a >= b)
print("(a <= b) :", a <= b)

Output
(a == b) : False
(a != b) : True
(a > b) : True
(a < b) : False
(a >= b) : True
(a <= b) : False
In the above example, since the value of a is not equal to that of b, therefore (a ==
b) (equal to) returned False and (a !=b) (not equal to) returned True.

Since the value of a is greater than that of b, therefore (a > b) (greater than) and (a
>= b) (greater than or equal to) returned True whereas (a < b) (less than) and (a
<= b) (less than or equal to) returned False.

Difference Between = And == in Python

Although = and == seem to be the same, they are quite different from each other. =
is the assignment operator while == is the equality operator.

= assigns values from its right side operands to its left side operands whereas ==
compares values.

x = 10
print(x == 10)

Output

True
By writing x = 10, we assigned a value 10 to x, whereas by writing x == 10, we
checked if the value of x is equal to 10 or not.

name = "Sam"
print(name == "Sam")
print(name == "Aam")

Output

True
False
= assigns a value "Sam" to the variable name and == checks whether the value of the
variable name is "Sam" or not.

Python Logical Operators

Look at the following table in which Exp1 and Exp2 are the two operands.
Exp1 Operator Exp1 Output (Boolean)

True and True True

True and False False

False and False False

True or True True

True or false True

False or False False

So, if we use and as an operator with any two operands and if both of them are True,
then the result is True. Otherwise, the result is False.

If we use or as an operator and if any of the two operands is True, then the result
is True, and if both the operands are False then the result is False.

and can be understood as both (both first and second)

or can be understood as either (either first or second or both).

and and or of programming are very much similar to English words 'and' and 'or'.

In English,

A and B - Both A and B.

A or B - Either A or B.

In programming also,
A and B - Both A and B.

A or B - Either A or B or both.

Again assume the value of a to be True and that of b to be False.

Operator Description Example

and If both the operands are True, then the condition becomes True (a and b) is False

or If any one or both the operands are True, then the condition becomes True (a or b) is True

It is used to reverse the condition. So, if a condition is True, not makes it not(a) is False, no
not
False and vice versa. True

x = 10
y = 20
print(x == 10 and y == 20)
print(x == 3 or y == 20)

Output

True
True
Here, x is 10 and y is 20. So, x == 10 is True and y == 20 is also True. Therefore, x
== 10 and y == 20 is also True because both the operands are True (and is used as
the operator).

In the next statement, x == 3 is False but y == 20 is True. Therefore, x == 3 or y


== 20 is True because at least one operand is True (or is used as the operator).

x = 10
y = 20
print(not(x == 10 and y == 20))
print(not(x == 3 or y == 20))
Output

False
False
not operator converts True to False and vice versa.

This is the same example as the previous one. We just used not here and saw the
answers get reversed.

Python Assignment Operators

Assignment Operators are used to assign values from its right side operands to its
left side operands. The most common assignment operator is =.

If we write a = 10, it means that we are assigning a value 10 to the variable a.

We can’t use = to assign the value of the left side operand to the right side operand.
For example, 10 = a would give us an error because we are trying to assign the
variable a (right side) to 10 (left side) and this is invalid as 10 is a constant and we
can't change its value to something else, a in this case.

There are more assignment operators which are listed in the following table.

Operator Description Example

= Assigns value of right operand to left operand C=A+B

Adds the value of right operand to left operand and assigns the final C += A is same as
+=
value to the left operand +A

Subtracts the value of right operand from left operand and assigns the C -= A is same as
-=
final value to left operand -A
Multiplies the value of right operand to left operand and assigns the final C *= A is same as
*=
value to left operand *A

Divides the value of left operand by right operand and assigns the C /= A is same as
/=
quotient to left operand /A

Divides the value of left operand by right operand and assigns the C //= A is same as
//=
quotient (integer) to left operand // A

C %= A is same a
%= Takes modulus using two operands and assigns the result to left operand
C%A

Calculates left operand raised to the power right operand and assigns the C **= A is same a
**=
result to the left operand C ** A

To understand their use, consider the value of a variable n as 5. Now if we write n +=


2, the expression gets evaluated as n = n + 2 thus making the value of n as 7 ( n = 5
+ 2 ).

We said that n+=2 is evaluated to n = n+2 but n = n+2 is evaluated? This is little
different to what happens in normal mathematics. Here, the value of n + 2 is
calculated first (using the old value of n).

So, n+2 is calculated first and thus it will become 5+2 i.e., 7.

Thus the expression will become n = 5+2 or n=7.

So the calculated value is assigned to n, changing the value of n (making n = 7).

The decision of evaluating n+2 first is not a random one. Python has preset rules of
which operation it is going to perform first which you will learn later in this chapter.

Let's look at an example.

a = 7
a += 4 # equivalent to a = a + 4
print("Value of a after a += 4:", a)

a -= 4 # equivalent to a = a - 4
print("Value of a after a -= 4:", a)

Output

Value of a after a += 4: 11
Value of a after a -= 4: 7
In the above example, initially, the value of a is 7.

The expression a += 4 got evaluated as a = a + 4 thus making the value of a as 11.


After this, the expression a -= 4 got evaluated as a = a - 4 thus subtracting 4 from
the current value of a (i.e. 11) and making it 7 again.

Python Identity Operator

These are used to check if two operands (values) are located in the same memory.
There are two identity operators in Python - is and is not.

Operator Description

is Returns True if the operands refer to the same object. Otherwise returns False.

is not Returns True if the operands do not refer to the same object. Otherwise returns False.

In the chapter Variables, we saw that everything in Python is stored in a different


memory location. is operator is used to check if the operands belong to the same
memory location.

Also note that two variables having the same value does not necessarily mean that
their values are stored in the same memory location.
Look at the following example.

a = [1, 2, 3]
b = [1, 2, 3]
print(a == b)
print(a is b)

Output

True
False
The variables a and b store two separate lists having the same value. Since these
are two separate lists, these are stored in different memory locations. a ==
b returned True because the == operator compares the values of the operands and
both the lists have the same value. a is b returned False because the is operator
compares the memory location of the operands and both the lists are stored in
different memory locations.

Now consider another example.

a = [1, 2, 3]
b = a
print(a == b)
print(a is b)
print(a is not b)

Output

True
True
False
The variable a is assigned a list which is stored in some memory location.
Therefore, a points to the memory location of this list. On writing b = a, b also started
pointing to the memory location which a is pointing to. Since both a and b are
pointing to the same memory location where the list is stored, a is
b returned True and a is not b returned False.

Python Membership Operators


These are used to check if a value is present in a sequence like string, list, tuple, etc.
There are two membership operators in Python - in and not in.

Operator Description

in Returns True if the value is present in the sequence. Otherwise returns False.

not in Returns True if the value is not present in the sequence. Otherwise returns False.

a = "Python programming"
print('on' in a)
print('p' not in a)

b = ['Hello', 1, 2, 3]
print(2 in b)

Output

True
False
True
'on' in a → Returned True because ‘on’ is present in "Python programming".

'p' not in a → Returned False because ‘p’ is present in "Python programming".

2 in b → Returned True because 2 is present in ['Hello', 1, 2, 3].

We can also test for a dictionary with membership operators, but the search happens
only for the key, not for the value.

a = {1: 'Blue', 2: 'Green', 'default': 'Orange'}


print(2 in a)
print('Blue' in a)
Output

True
False
a is a dictionary having 1, 2 and ‘default’ as keys and ‘Blue’, ‘Green’ and ‘Orange’ as
the corresponding values.

2 in a → Returned True because 2 is a key present in the dictionary a.

'Blue' in a → Returned False because 'Blue' is not a key present in the


dictionary a. Remember that we search for just the keys, not the values.

We will be looking at more examples of Identity and Membership operators in later


chapters.

Precedence of Operators in Python

In Maths, you might have learned about the BODMAS rule, but that rule is not
applied here. If we have written more than one operator in an expression, then the
operation that should be done first is governed by the following rule :- Expression
inside brackets '()' are evaluated first. After that, the following table is followed
(The operator at the top has the highest precedence (priority) and that at the bottom
has the least precedence (priority)):

Operator Associativity

** Right to left

* / // % Left to right

+= left to right
== != > < >= <= is is not in not in Left to right

not Right to left

and Left to right

or Left to right

= += -= *= /= //= %= Right to left

Let's consider an expression

n=4*8+7

Since the priority order of the multiplication operator ( * ) is greater than that of the
addition operator ( + ), so first 4 will get multiplied with 8 and after that 7 will be
added to the product.

Suppose two operators have the same priority order in an expression, then the
evaluation will start from left or right (if associativity is left to right) and from right to
left (if associativity is from right to left) as shown in the above table.

For example, take the expression.

10 / 5 - 2 * 3 + 8

Since the priorities of / and * are greater than those of + and -, therefore / and * will
be evaluated first. Since / and * have the same priority order, so these will be
evaluated from left to right simplifying to the following expression. (In the expression,
/ is written at left and * at right)

2-2*3+8

After /, * will be evaluated resulting in the following expression

2-6+8
Again - and + have the same precedence, therefore these will also be evaluated
from left to right i.e. first 6 will be subtracted from 2 after which 8 will be added
resulting in 4.

Hence we will get the result as 4.

Remember the expression n = n + 2 used earlier in this chapter? Now you can
understand that because the + operator has higher precedence than the = operator,
the value n + 2 is calculated first making the expression n = 7. Also, the = operator
has associativity from right to left, and thus the value on the right side of the =
operator is assigned to the variable on the left side i.e., 7 is assigned to n, making
the value of n equal to 7.

Python If and Else

This chapter starts your real coding part. We are well equipped with all the basics we
needed and now we can directly dive into the real programming logics.

Many times, we need to first see the conditions and accordingly make a decision.
For example, if it is raining, we will take an umbrella, otherwise not. Similarly, if a
number is divisible by 2, it is even, otherwise, it is odd.

Similarly, we can implement logics like if a user is admin then allow few features,
otherwise not.
We make such types of decisions in Python using if...else.

Python if Statement

Again take the example of raining. If it is raining, a person will take an umbrella.

In Python, such types of decisions are taken using an if statement. Let's first have a
look at its syntax.

Python if statement Syntax

if condition:
statement
statement
...

We first write if condition:. This condition is based on the decision that we will be
making.

If the condition is True, then only the body of if is executed.

Let’s take an example.

Python if statement Examples

a = 2
if a == 2:
print("a is 2")

In this example, the condition of the if statement is a == 2.

The body of the if statement consists of the statement print(“a is 2”). This
statement will get executed only if the condition is True.
Since the value of the variable a is 2, the condition a == 2 became True and the
statement body of if got executed and thus a is 2 got printed on the screen.

If the value of a was not 2, then the statement in the body of if would not have got
executed.

Let’s take one more example.

print("Enter your name")


name = input()

if(name=="Sam"):
print("Your name is Sam")
print("It’s a good name")

Here both statements are inside the if block. But how did we decide which is inside it
and which is not? Notice the space before the statements print(“Your name is Sam”)
and print(“It’s a good name”). It is called indentation.

Also notice a colon : after if.

All the statements after colon (:) which are indented using space are body of the if
statement. We can use space or tab to provide indentation. You can also set this up
in the text editor you are using.
Now, a statement after if block which is not indented using tab or space will not be a
part of if.
Let’s take an example.

age = int(input("Enter age"))


if age >= 18:
print("Your age is 18+")
print("You are eligible to vote")

print("This statement is outside the body of if statement")

Output

Enter age20
Your age is 18+
You are eligible to vote
This statement is outside the body of if statement
Here, the condition of the if statement is age >= 18.
The body of the if statement consists of print("Your age is 18+") and print("You
are eligible to vote"). These two statements will get executed only if the
condition is True.

Since the user entered the age as 20, which is greater than 18, the condition age >=
18 became True. Therefore, the statements in the body of if got executed.

If the user enters the age less than 18, then the condition age >= 18 will become
False and the statements in the body of if will not get executed.

That was easy, right?

Let’s see one more example.

a = 45
b = 45
if a == b:
print("a is equal to b")

Output

a is equal to b
Here the condition a == b is True and thus the statement in the body of the if
statement got executed and "a is equal to b" got printed.

Python Indentation

Statements written inside the body of if or else (we will read about else in the next
section) must be indented equally from the left margin. It means that all the
statements written inside the body of if must be equally spaced from the left. We
have used an indentation of 4 spaces in this chapter. This means that before every
statement inside the body of if, there is a left space of 4 spaces.

The best way to add indentation is to press the TAB key where you want to add the
indentation. You can set a different width of Tab space in text editors. We have this
width of 4 in this chapter. That's why we are getting a width of 4 spaces every time.
In Python, this equal indentation represents the body. Here, it is the body of if. It
represents what things are inside if. We will see giving indentation in many more
places like loops, functions to represent their body.

Python if...else Statement

Now, assume that a chocolate costs 10 rupees and a candy costs 5 rupees. So, if
you have at least 10 rupees, you can buy a chocolate, otherwise you have to buy a
candy.

In the world of programming, this is done by using if...else statement. Let's see how.

Python if..else Syntax

if condition:
statement
statement
...
else:
statement
statement
...

The body of if consists of all the indented statements following if, unless some
unindented statement is written.

The body of else consists of all the indented statements following else, unless some
unindented statement is written.

If the condition is True, then the body of if is executed, otherwise the body of else is
executed.

Python if..else Examples


age = int(input("Enter age"))
if age >= 18:
print("Your age is 18+")
print("You are eligible to vote")
else:
print("You are not yet 18")
print("You are not eligible to vote")

Output

Enter age15
You are not yet 18
You are not eligible to vote
Since the user entered the age as 15, the condition age >= 18 became False.
Therefore, the statements in the body of else got executed.

Look at another example.

a = 45
b = 56
if a == b:
print("a is equal to b")
else:
print("a is not equal to b")

Output

a is not equal to b
In the above example, the value of a is not equal to that of b. Since the expression a
== b is False, therefore the statement in the body of else i.e., print("a is not
equal to b") got executed.

Let’s write a program to find out if a number is even or odd.

a = 14
if a%2 == 0: #checking divisibility by 2
print("Your number is even")
else:
print("Your number is odd")
Output

Your number is even


The above code is self-explanatory. a%2 gives us the remainder of the division of a by
2. If the remainder is 0 (perfectly divisible by 2), then the body of if gets executed
otherwise that of else gets executed. In our case where the value of a is 14, the
condition of if is True and therefore the body of if got executed.

Python if...elif...else Statement

elif is a short form of else if. It is a combination of multiple if...else statements.

Python if...elif...else Syntax

if condition:
statement
statement
...
elif condition:
statement
statement
...
elif condition:
statement
statement
...
else:
statement
statement
...

Firstly, as usual the condition of if is checked. If it is True, then the body of if is


executed.
If the condition of if is False, then the condition of the first elif is checked. If the
condition of the first elif is True then the statements inside its body are executed,
otherwise the next elif condition is checked.

If the conditions of if and all elif are False, then the body of else is executed.

Python if...elif...else Examples

age = int(input("Enter your age"))


if age < 13: # condition - age < 13
print("Hey! kid")
elif age > 13 and age < 20: # condition - age > 13 and age < 20
print("So, you are enjoying your teenage.")
else:
print("You are grown up.")

Output

Enter your age15


So, you are enjoying your teenage.
if age < 13: → We are first checking the condition of if - is the age entered by the
user less than 13. If this condition is True, then the body of if is executed. If the
condition is False, then the condition of the next elif is checked.

elif age > 13 and age < 20: → We are checking the condition of elif - is the age
between 13 (included) and 20. If this condition is True, then the body of elif is
executed. If the condition is False, then the body of else is executed.

Let's see one more example.

a = int(input("Enter a number"))
if a%2 == 0 and a > 10: # condition - a%2 == 0 and a > 10
print("Your number is even and greater than 10")
elif a%2 == 0 and a < 10: # condition - a%2 == 0 and a < 10
print("Your number is even and smaller than 10")
else:
print("Your number is odd")
Output

Enter a number6
Your number is even and smaller than 10
a%2 is used to check the divisibility by 2. If a%2 is 0, then a is an even number,
otherwise an odd number.

if a%2 == 0 and a > 10: → If the number entered by the user is divisible by even
and greater than 10, then the body of if is executed.

elif a%2 == 0 and a <= 10: → If the number is even and less than or equal to 10,
then the body of elif is executed.

else → If the conditions of both if and elif are False, then the body of else is
executed.

Python Nested if Statements

We can use if, if...else or if...elif...else statements in the body of if, elif or else. This is
also called nesting.

The last example is rewritten below using nesting.

a = int(input("Enter a number")) #taking input from user


if a%2 == 0: #checking divisibility by 2
if a > 10:
print("Your number is even and greater than 10")
else:
print("Your number is even and smaller than or equal to 10")
else:
print("Your number is odd")

Output

Enter a number14
Your number is even and greater than 10
Here,
if a > 10:
print("Your number is even and greater than 10")

else:
print("Your number is even and smaller than or equal to 10")

is inside if a%2 == 0.

Notice the indentation from the left margin in the above example - print("Your
number is even and greater than 10") is inside both if a > 10 and if a%2 == 0. If
you are facing any difficulty, feel free to ask your question.

Here, we are checking if the number entered by the user is even or odd. If the
number is even, then we are further checking if it is greater than 10 or not.
Accordingly, we are displaying the message. For example, if the user enters 14, then
the number is even (a%2 == 0 is True) and greater than 10 (a > 10 is True) and
therefore "Your number is even and greater than 10" will get displayed.
Python pass Statement

The pass statement is used to skip from if, elif or else. If we don’t want to give
anything in the body of if, elif or else, then we give the pass statement in the body.
On executing pass, nothing happens.

age = int(input("Enter your age."))


if age < 13:
print("Hey! kid")
elif age>13 and age < 20:
pass
else:
print("You are grown up.")

Output

Enter your age.15


The body of elif contains pass. This means that if the age entered by the user is
between 13 (included) and 20, then nothing will get executed. Therefore, on giving
the age as 15, using pass just skipped and printed nothing.

You might be wondering why someone would use pass. There can be different
reasons for using it. One can be that you want to do nothing for some specific
conditions, like in the last example where you don’t want to display anything if age is
between 13 and 20. Another reason can be that you want to write some code in
future and you can’t leave the body empty as that would throw an error, therefore
you just give the pass statement there.

Python While Loop

Playing with loops makes programming fun. Before we try to understand loop, you
should be thorough with all the previous topics of Python. If not, practice a
considerable amount of problems on all the previous topics.
Suppose, we have to print the first 10 natural numbers.

One way to do this is to print the first 10 natural numbers individually using print().
But what if you are asked to print the first 100 natural numbers? You can easily do
this with the help of loops.

Loops are used to repeat a block of code again and again.

Basically, there are two loops in Python:

1. while loop
2. for loop

In this chapter, we will read about the while loop. We will go through the for loop in
the next chapter.

Python while Loop

Let's first look at the syntax of while loop.

Python while loop Syntax

while condition:
statement
statement
...
The body of the while loop consists of all the indented statements below while
condition:.

while loop checks whether the condition is True or not. If the condition is True, the
statements written in the body of the while loop are executed. Then again the
condition is checked, and if found True again, the statements in the body of
the while loop are executed again. This process continues until the condition
becomes False.

Therefore, the while loop repeats the statements inside its body till its condition
becomes False.

Python while loop Examples

Let’s print the first 10 natural numbers using a while loop.


n = 1
while n <= 10:
print(n)
n = n + 1

Output

1
2
3
4
5
6
7
8
9
10
The condition of the while loop is n <= 10.

The body of the while loop consists of print(n) and n = n + 1. These two
statements will get executed only if the condition is True.

First we assigned 1 to a variable n.

while n <= 10: → The condition n <= 10 is checked. Since the value of n is 1 which
is less than 10, the condition becomes True and the statements in the body are
executed.

The value of n i.e. 1 is printed and n = n + 1 increases the value of n by 1. So, now
the value of n becomes 2.

Now, again the condition is checked. This time also n <= 10 is True because the
value of n is 2. So, again the value of n i.e., 2 gets printed and the value of n is
increased to 3.

In this way, when the value of n becomes 10, again the condition n <= 10 is True for
the tenth time and 10 gets printed. Now, n = n + 1 increases the value to n to 11.

This time, the condition n <= 10 becomes False and the loop gets terminated.
Checking the condition and executing the body consists of one iteration. Therefore,
ten iterations took place in the above example.

Quite interesting. Isn't it!

Notice that the body of while is also represented by equal indentation (margin) from
left.

The following animation will also help you to understand the implementation of
the while loop.

Let's print the multiplication table of 14 using a while loop.

i = 1
while i<=10:
print(i*14)
i=i+1

Output

14
28
42
56
70
84
98
112
126
140
In this example, the condition of the while loop is i<=10. Initially, i is 1.
In the first iteration, the condition is satisfied (1 is less than 10). Therefore, the
statements in the body of while are executed - 14*i ( 14*1 = 14 ) gets printed and
then i = i+1 increases the value of i by 1 making it 2.

In the second iteration, again the condition of the loop is satisfied (2 is less than
10). Therefore, again the statements in the body are executed - 14*i ( 14*2 = 28 )
gets printed and then i = i+1 increases the value of i by 1 making it 3.

In the third iteration, again the condition of the loop is satisfied and 42 gets printed
on the screen.

In the tenth iteration, when i becomes 10, 140 gets printed and i = i+1 makes the
value of i equal to 11.

Since the value of i is now 11, the condition of the while loop (i <= 10) becomes
False and the loop gets stopped and the rest of the statements after the while loop
gets executed.

So now you know that in the above example, the while loop will stop
when i becomes greater than 10.
One more example (including if/else):

#initially more is 'True' to run the while loop for at least once
more = True
while more == True:
'''Taking marks from user'''
name = input("Enter your name >>>")
maths_marks = int(input("Maths marks >>>"))
science_marks = int(input("Science marks >>>"))
english_marks = int(input("English marks >>>"))
comupter_marks = int(input("Computer marks >>>"))
total = maths_marks + science_marks + english_marks + comupter_marks

percentage = (total/400)*100
print(name, ", your total marks is", total, "and your percentage is", percenta
ge)

#User has to enter y if he want to run it again


a = input("Want to enter more? y/n >>>")
if a!="y":
#if user enters anything other than 'y', then 'more' is set to 'False' to
stop the loop.
more = False

Output

Enter your name >>>Sam


Maths marks >>>90
Science marks >>>92
English marks >>>85
Computer marks >>>94
Sam , your total marks is 361 and your percentage is 90.25
Want to enter more y/n >>>y
Enter your name >>>John
Maths marks >>>98
Science marks >>>82
English marks >>>89
Computer marks >>>90
John , your total marks is 359 and your percentage is 89.75
Want to enter more y/n >>>n
The code inside the body of while is simple. It is taking marks as input and
calculating the percentage and printing it on the screen.

Again it is asking the user to press 'y' or 'n' to know if the user wants to calculate
more or not.

Now, if the user enters 'n', then the value of more will become False and then the
condition of the loop (more == True) will not be satisfied and thus the loop will stop.
But if the user enters 'y', then there will be no change in the value of the
variable more, which will satisfy the condition of the loop and the loop will be
executed again.

The above while loop will run till more is True and it can change if we don't give 'y'
to a. (if a!= "y" → more = False). And if we enter 'y', then the whole loop will run
again because the value of more is not changed and is still True.

Python Infinite Loop


A loop is called an infinite loop if its condition is always True. It is called so because
it will keep on executing its body forever. Below is an infinite loop created using
a while loop.

while True:
print(1)

Output

1
1
1
1
1
1
1
1
1
1

Press ctrl+c (cmd+c on Mac) to stop infinite loops

Python Nesting of Loop

Nesting means having one loop inside another loop, i.e., to have a loop inside the
body of another loop. You have already studied about having one if statement under
another. This is also similar. Let's see an example first.

a = 5
b = 1
while a>0:
while b<=5:
print("*"*b)
b = b+1
a = a-1

Output

*
**
***
****
*****
Here, a is 5 and b is 1.

In the first iteration of the outer while loop, a is 1 and the inner while loop is
inside the body of the outer while loop. So, the inner while loop is executed
and "*"*1 (b is 1) i.e, "*" gets printed and b becomes 2 and a becomes 4.

Now, the inner while loop gets executed again (as b is 2 and b <= 5). So "*"*2 i.e.
"**" gets printed and both b and a become 3.

Again, the inner loop gets executed and "*"*3 i.e., "***" gets printed. In the last
iteration of the inner while loop with b equals 5, "*"*5 i.e., "*****" gets printed
and b becomes 6 and a becomes 0.

Again the condition of the inner while loop is checked but it is found False (as b is 6).

Now, the second iteration of the outer while loop occurs but since a is 0, so its
condition is also False. Therefore, it will also stop.

In short, there is nothing new in nesting of loops. Inner loop is like all the other
statements in the body of a loop, after the execution of which, the rest of the
statements in the body of the outer loop are executed.

Let's have a look at one more example on this:

a = 5
while a>0:
b = 1
while b<=5:
print("*"*b)
b = b+1
a = a-1
Output

*
**
***
****
*****
*
**
***
****
*****
*
**
***
****
*****
*
**
***
****
*****
*
**
***
****
*****
Try to understand this example yourself. Just go step by step with every while loop
and you will understand this.

Let's create your own digital dice.


Though this is not graphical, we will construct the working structure. You can learn to
link graphics to this or any game after completing this course. For now, let's do this
first.

#Digital dice
#importing random function to genterate random number
from random import randint
print("Give lower limit of dice")
a = int(input())
print("Give upper limit of dice")
b = int(input())
print("type q to Quit or any other key/enter to continue")
while True:
print(">>> "+str(randint(a,b))) #randint is generating random number between a
and b
if input() == 'q': #if 'q' is entered then come out of loop
break

Output

Give lower limit of dice


1
Give upper limit of dice
6
type q to Quit or any other key/enter to continue
>>> 5
>>> 4
>>> 2
>>> 1
>>> 6
>>> 3
>>> 2
>>> 3
q
We are importing the randint() function from the random library of Python. This
function generates a random number between two integers given to it. You can find
more about it in Python documentation. The rest of the parts must be clear. We are
setting the limits of the random numbers generated by taking the lower limit as 'a'
and the upper limit as 'b'.

Make sure to read articles in Further Reading at the end of this chapter

Python For Loop

If you are here, it means you are going to be a coder soon. You will solve real-world
computer problems, make your own programs, and a lot more. Yeah, sounds good!

Now let's talk about for. It is also a loop like while. Let's see how it works.

Python for Loop

Python for loop Syntax

for iter in sequence:


statement
statement
...

sequence is a sequence like list, tuple, string, etc. iter is a variable that takes the
value of each item in the sequence.

The body of the for loop consists of all the indented statements below for iter in
sequence:.
Python for loop Examples

colors = ["Blue", "Green", "Yellow", "Orange", "Red", "Brown"]


for color in colors:
print(color)

Output

Blue
Green
Yellow
Orange
Red
Brown
By looking at the output, you might have got some idea of what the for loop does.
Let’s understand the code.

colors is a list here containing six items.

for color in colors → color is a variable which goes to each element in the
list colors and takes its value.

So, in the first iteration, color is equal to the 1st element of the list. In the second
iteration, color is equal to the 2nd element of the list, and so on.

Therefore, in the first iteration, the value of color is “Blue” and


so print(color) printed Blue. Similarly, in the second iteration, Green got printed,
and so on.

Notice that the body of for loop is also represented by equal indentation (margin)
from the left.
Python Example of Sum of Marks using Loop

marks = [78, 98, 50, 37, 45]


sum = 0
for m in marks:
sum = sum + m
print(sum)

Output

308

You must have understood the code. If not, then let us explain a bit.

sum = 0 → We are taking a variable sum and assigning it an initial value of 0.

In the 1st iteration, m is 78. So, sum + m (0 + 78 = 78) makes the value of sum as 78.
In the 2nd iteration, m is 98. So, sum + m (78 + 98 = 176) makes the value of sum as
176.

After all the iterations, sum becomes 308.

We will look at the use of for loop with lists, strings, tuples and other sequences in
detail in later chapters.

Python range() function

The range() function is used to generate a sequence of numbers.

Python range Syntax

range(start, stop, step_size)

start - Integer from which the sequence starts. It is optional. Its default value is 0.

stop - Integer before which the sequence stops. It is mandatory.

step_size - Increment between each integer in the sequence. It is optional. Its


default value is 1.

range(10) generates ten integers starting from 0 to 9. Here, start = 0, stop = 10,
step_size = 1.

range(1, 10) generates nine integers starting from 1 to 9. Here, start = 1, stop = 10,
step_size = 1.

range(1, 10, 2) generates the integers 1, 3, 5, 7, 9. Here, start = 1, stop = 10,


step_size = 2.
Python range Examples

In the last chapter, we printed the first 10 natural numbers using a while loop. Let’s
do that using a for loop.

for n in range(1, 11):


print(n)

Output

1
2
3
4
5
6
7
8
9
10

The next example prints the multiplication table of 14 using a for loop.

for i in range(1, 11):


print(i*14)

Output

14
28
42
56
70
84
98
112
126
140

Now let’s print all the even numbers from 10 (included) to 20 (included).

for num in range(10, 21, 2):


print(num)

Output

10
12
14
16
18
20
That was cool, right? We could print the desired results using just 2 lines of code.
We suggest you practice more questions on loops from our practice section.

Python Break and Continue

In the last two topics, you learned about loops which are used to repeat a certain
process some number of times.

What if we can control the way our loop operates?

In Python, we can jump out of a loop or jump to the starting condition of a loop
whenever we want. We do this with the help of break and continue statements
respectively.

Python break

break is used to break or terminate a loop whenever we want.

Just type break after the statement after which you want to break the loop. As simple
as that!

Python break Syntax

break

Python break Examples

for n in range(1, 10):


print("*")
if n == 2:
break

Output

*
*

In the first iteration of the loop, '*' gets printed and the condition n == 2 is
checked. Since the value of n is 1, therefore the condition becomes False

In the second iteration of the loop, again '*' gets printed and the condition is
checked. Since this time the condition of if is satisfied (because n is 2),
the break statement terminates the loop.

Let’s write the same program using a while loop.

n = 1
while n < 10:
print("*")
if n == 2:
break
n = n + 1

Output

*
*

Let's have a look at one more example.

while True:
x = int(input("Enter 0 to stop"))
if x == 0:
break

Output

Enter 0 to stop
3
Enter 0 to stop
32
Enter 0 to stop
23
Enter 0 to stop
0

This is an infinite loop. To terminate this, we are using break. If the user enters 0,
then the condition of if will get satisfied and the break statement will terminate the
loop.

Python continue

continue statement works similar to the break statement. The only difference is
that break statement terminates the loop whereas continue statement skips the
rest of the statements in the loop and starts the next iteration.

Python continue Syntax

continue

Python continue Examples

for n in range(1, 10):


if n == 5:
continue
print(n)

Output

1
2
3
4
6
7
8
9

Notice that 5 is not printed in the output. This is because in the fifth iteration when
the value of n became 5, the if condition became True and the continue statement in
the body of the if statement got executed. Thus the next statement print(n) didn’t
get executed and the sixth iteration started.

The same program using while loop is shown below.

n = 1
while n < 10:
if n == 5:
n = n + 1
continue
print(n)
n = n + 1

Output

1
2
3
4
6
7
8
9

Python Lists

From printing something, to decision making, to iterating using loops, we have


learned many concepts till now. This and the next few chapters are going to be about
storing data. So, let’s start with a list which is used to store a list of data.
We often need a list in our programs. Imagine you are writing a program to store the
marks of every student in a class of 50 students. Taking 50 different variables is not
a good option. Instead of that, we can store all those 50 values (marks) as a list in a
single variable. Cool, right?

Creating a List in Python

[] is a list.

[1,2,3] is a list having elements 1, 2 and 3 in it.

a = [1, 2, 3, 4, 5] → Here, a is a list having elements 1, 2, 3, 4 and 5 in it.

The items/elements in a list are enclosed within brackets [ ] and separated by


commas.

a=[]

Output

a is an empty list having no item.

mylist = [2, "Hello", True, 100, 2]


print(mylist)

Output

[2, 'Hello', True, 100, 2]

mylist is a list having five items of different data types.

print(type([]))

Output

<class 'list'>

As you can see, type([]) is showing the type as list. This means that [ ] is a list as
already mentioned.

Python Accessing Elements from List


A particular element from a list can be accessed using its index.

Every element in a list has a unique index based on its position. Indices start from 0.
Therefore, the first element of a list has index 0, the second element has index 1,
and so on.

Take the following list.

mylist = [“a”, “b”, “c”, “d”, “e”]

In this list, the index of “a” is 0, “b” is 1, “c” is 2, “d” is 3 and “e” is 4. Thus, the index
started from 0 and went up to 4.

element "a" "b" "c" "d" "e"

index 0 1 2 3 4

A particular element from a list can be accessed using its index.

To access any element of a list, we write name_of_list[index].

Therefore, the 2nd element of the list mylist can be accessed by


writing mylist[1] (because the index of the 2nd element is 1).

mylist = [4,9,6,2]
print(mylist[0])
print(mylist[1])
print(mylist[2])
print(mylist[3])

Output

4
9
6
2
So, we can use a list to store a collection of data, for example, marks of 50 students,
and to access each element, we can simply use list_name[index].. This is quite
convenient instead of storing marks in 50 different variables.

Negative Index in Python List

We can also access an element of a list by using the negative value of the index.
Negative indices behave differently.

The last element of a list has index -1, second last element has index -2, and so on.

element "a" "b" "c" "d" "e"

Negative index -5 -4 -3 -2 -1

The following example will make you understand better.

mylist = [4,9,6,2]
# accessing using negative indices
print(mylist[-1])
print(mylist[-2])
print(mylist[-3])
print(mylist[-4])

Output

2
6
9
4

Here, mylist[-1] returns the last element 2 of the list mylist, mylist[-2] returns the
second last element 6, and so on.

Python Slicing
Suppose we have a list of the marks of 50 students. Now, if from this list, we want
the marks of only the first 10 students, then we can do so by slicing the list to get
only the first 10 elements.

We can slice a list using the slicing operator.


To access elements from the ith index to the jth index of a list, we
write name_of_list[i:j+1].

mylist = [1,2,3,4,5]
print(mylist[1:4])

Output

[2, 3, 4]

mylist[1:4] returned the elements from the 1st index to the 3rd index i.e., [2, 3, 4].

To access the elements from index 4 till the end of a list, use name_of_list[4:].

To access the elements from the start till index 4 of a list, use name_of_list[:5].

To access all the elements from the start till the end of a list, use name_of_list[:].

mylist = [1, 2, 3, 4, 5, 6]

# from 2nd element till 5th element


print(mylist[1:5])

# from start till 5th element


print(mylist[:5])

# from 5th element till end


print(mylist[4:])

# from start till 3rd last element


print(mylist[:-2])

# from 4th last element till 3rd last element


print(mylist[-4:-2])

# from 3rd element till 3rd last element


print(mylist[2:-2])

# all elements
print(mylist[:])
Output

[2, 3, 4, 5]
[1, 2, 3, 4, 5]
[5, 6]
[1, 2, 3, 4]
[3, 4]
[3, 4]
[1, 2, 3, 4, 5, 6]

We can also give a step to the slice to skip some number of elements while slicing.

Look at the following example.

mylist = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# from 2nd element till 9th element


print(mylist[1:9])

# from 2nd element till 9th element in step 2


print(mylist[1:9:2])

# from 2nd element till 9th element in step 3


print(mylist[1:9:3])

Output

[2, 3, 4, 5, 6, 7, 8, 9]
[2, 4, 6, 8]
[2, 5, 8]

mylist[1:9] returned the elements from the 1st index till the 8th index.

mylist[1:9:2] also returned the elements from the 1st index till the 8th index, but it
returned every 2nd element after the element having the start index. Therefore, 3, 5
and 7 got skipped.

mylist[1:9:3] also returned the elements from the 1st index till the 8th index, but it
returned every 3rd element after the element having the start index.

mylist = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


# from 3rd element till 9th element
print(mylist[2:9])

# from 3rd element till 9th element in step 2


print(mylist[2:9:2])

# from 3rd element till 9th element in step 3


print(mylist[2:9:3])

# from start till 9th element in step 2


print(mylist[:9:2])

# from 3rd element till end in step 3


print(mylist[2::3])

# elements from start till end in step 2


print(mylist[::2])

Output

[3, 4, 5, 6, 7, 8, 9]
[3, 5, 7, 9]
[3, 6, 9]
[1, 3, 5, 7, 9]
[3, 6, 9]
[1, 3, 5, 7, 9]

Giving a negative step will return the elements of a list in the reverse order. A step of
-1 returns the reversed list.

mylist = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# reversed list
print(mylist[::-1])

# reversed list with step -2


print(mylist[::-2])

Output

[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
[10, 8, 6, 4, 2]

Changing Elements of List in Python

Lists are mutable, which means that their elements can be changed.

Changing any element of a list is simple. We know that using list_name[index], we


can access any element of a list. We can simply change that element
using list_name[index] = some_value. This will change the value of the element at
index in the list my_list to some_value.

Changing a Single Element of List in Python

mylist = [1, 2, 3, 4, 5, 6]
mylist[2] = 0 # changing 3rd element, at index 2
print(mylist) # printing changed list

Output

[1, 2, 0, 4, 5, 6]

The third element of the list is changed by simply assigning the new value
to mylist[2].

Changing range of elements in Python List

mylist = [1, 2, 3, 4, 5, 6]
mylist[2:5] = [10, 11, 12] # changing 3rd to 5th elements
print(mylist) # printing changed list

Output

[1, 2, 10, 11, 12, 6]

A range of elements from the 2nd index till the 4th index is changed by assigning a
list of values to mylist[2:5].

Adding Elements to Python List

Adding a Single Element to Python List

To add one element to the end of a list, we can use the append() function.
The append() function appends the element (adds the element at the last) to a list.

mylist = [1, 2, 3, 4]
mylist.append(5) # 5 is added to the list
print(mylist) # printing changed list

Output

[1, 2, 3, 4, 5]

You can see that 5 got appended (added at the last) to the list mylist.

To add one element at a specific position of a list, we use the insert() function.
The insert() function takes two arguments - the first argument is the index where
we want to insert and the second argument is the element which we want to insert.

mylist = [1, 2, 3, 4]
mylist.insert(2, 5) # adding 5 at index 2 of the list
print(mylist) # printing changed list

Output

[1, 2, 5, 3, 4]
In this example, 5 is added at the 2nd index of the list mylist.

Adding Multiple Elements to Python List

To add multiple elements to the end of a list, we use the extend() function.
The extend() function takes a list and inserts all of its elements at the last of the list
on which it is called.

mylist = [1, 2, 3, 4]
mylist.extend([5, 6, 7]) # 5, 6 and 7 are added to the list
print(mylist) # printing changed list

Output

[1, 2, 3, 4, 5, 6, 7]

Here, the extend() function took a list [5, 6, 7] and added the elements 5, 6 and 7 at
the last of the list mylist.

Instead of using the extend() function, we can also concat a list using
the + operator. However instead of changing the current list, it will give us a new list.
Let’s look at an example.

mylist1 = [1, 2, 3]
mylist2 = [4, 5]
mylist3 = mylist1+mylist2
print(mylist1)
print(mylist3)

Output

[1, 2, 3]
[1, 2, 3, 4, 5]

Here, you can see that “+” joined two lists and gave us a new list.
However, mylist1 and mylist2 are unchanged.

As stated above, we can also change the range of a list and that will be helpful if we
want to insert multiple elements at any specific position in a list.
mylist = [1, 2, 3, 4]
mylist[2:2] = [5, 6, 7] # adding 5, 6 and 7 starting from index 2 of the list
print(mylist) # printing changed list

Output

[1, 2, 5, 6, 7, 3, 4]

In this example, we assigned the list [5, 6, 7] to mylist[2:2]. This means that [5, 6,
7] is inserted from the 2nd index onwards of mylist. Thus, 5, 6 and 7 are inserted to
the list at indices 2, 3 and 4 respectively.

Python Deleting Elements from List

Python del

The del keyword can be used to delete a single element, multiple elements or the
entire list.

colors = ["Blue", "Green", "Red", "Orange", "Yellow", "Brown"]


del colors[2] # deleting 3rd element
print(colors)

Output

['Blue', 'Green', 'Orange', 'Yellow', 'Brown']

In the above code, the third element is deleted from the list.

colors = ["Blue", "Green", "Red", "Orange", "Yellow", "Brown"]


del colors[2:4] # deleting from 3rd element to fourth element
print(colors)

Output

['Blue', 'Green', 'Yellow', 'Brown']

In the above code, the elements at the 2nd and 3rd indices are deleted from the list.
colors = ["Blue", "Green", "Red", "Orange", "Yellow", "Brown"]
del colors # deleting the list
print(colors)

Output

Traceback (most recent call last):


File "<stdin7gt;", line 3, in
NameError: name 'colors' is not defined

In the above code, the entire list colors is deleted. Thus, we got an error on printing
the list after deleting it.

Python remove()

The remove() function is used to remove a particular element from a list.

colors = ["Blue", "Green", "Red", "Orange", "Yellow", "Brown"]


colors.remove("Green") # removing "Green" from list
print(colors)

Output

['Blue', 'Red', 'Orange', 'Yellow', 'Brown']

colors.remove("Green") removed "Green" from the list colors.

If the element passed to the remove() function is present more than once in the list,
then the element with the first occurrence is deleted.

colors = ["Blue", "Green", "Red", "Orange", "Green", "Brown"]


colors.remove("Green") # removing "Green" occurring first from list
print(colors)

Output

['Blue', 'Red', 'Orange', 'Green', 'Brown']

"Green" is present at indices 1 and 4. However, only "Green" present at index 1 got
deleted.
Python pop()

The pop() function removes an element from the specified index and returns the
removed element.

colors = ["Blue", "Green", "Red", "Orange", "Yellow", "Brown"]


print(colors.pop(3)) # removing the 4th element from list and printing the remove
d element
print(colors) # printing updated list

Output

Orange
['Blue', 'Green', 'Red', 'Yellow', 'Brown']

colors.pop(3) removed the element at the 3rd index from the list and returned that
element. Therefore, on printing the popped value, Orange got printed.

If we don’t pass any index in the pop() function, then it removes and returns the last
element of the list.

colors = ["Blue", "Green", "Red", "Orange", "Yellow", "Brown"]


print(colors.pop()) # removing the last element from list and printing the remove
d element
print(colors) # printing updated list

Output

Brown
['Blue', 'Green', 'Red', 'Orange', 'Yellow']

Python clear()

The clear() function removes all the elements from a list.

colors = ["Blue", "Green", "Red", "Orange", "Yellow", "Brown"]


colors.clear() # removing all element from list
print(colors) # printing updated list

Output

[]

Python List Operations

Python Concatenation (+) Operation

Two or more lists can be concatenated or combined using the + operator.

list1 = [1, 2, 3]
list2 = ['a', 'b', 'c', 'd']
list3 = list1 + list2
print(list3)

Output

[1, 2, 3, 'a', 'b', 'c', 'd']

list1 and list2 are concatenated to form list3.

Python Repetition (*) Operation

A list can be repeated a specified number of times using the * operator.

list1 = [1, 2]
list2 = list1 * 3
print(list2)

Output

[1, 2, 1, 2, 1, 2]

list1 is repeated 3 times to form list2.


Python List Membership Test

We can test if an element is present in a list using the membership


operators in and not in.

mylist = [1, 2, "Blue", "Green"]


print(2 in mylist)
print(4 in mylist)
print("Green" not in mylist)

Output

True
False
False

Python Iteration Through a List

We can iterate through a list using a for loop. We have already seen examples of
iteration of lists while studying for loops.

colors = ["Blue", "Green", "Red"]


for color in colors:
print(color)

Output

Blue
Green
Red

A variable color goes to each element in the list colors and takes its value, and that
value is printed on the screen.

We can also iterate through the above list using another way.

colors = ["Blue", "Green", "Red"]


for i in range(len(colors)):
print(colors[i])

Output

Blue
Green
Red

len() function is used to return the number of elements in a list.


Therefore range(len(colors)) is equal to range(3). This means that there will be
three iterations in the loop. You must have understood the rest of the code.

In the first iteration, the value of i is 0, thus printing colors[0], i.e., “Blue”.

In the second iteration, the value of i is 1, thus printing colors[1], i.e., “Green”.

In the third iteration, the value of i is 2, thus printing colors[2], i.e., “Red”.

Now that we know how to iterate through a list, let’s write a program to create a list
having 5 values entered by the user.

mylist = [] # creating an empty list

# appending values entered by user to list


for i in range(5):
print("Enter value of n[", i, "]")
mylist.append(input())

# printing final list


print(mylist)

Output

Enter value of n[ 0 ]Python


Enter value of n[ 1 ]C
Enter value of n[ 2 ]Java
Enter value of n[ 3 ]C++
Enter value of n[ 4 ]Perl
['Python', 'C', 'Java', 'C++', 'Perl']
In this program, we are first creating an empty list mylist.

We already know that range() is a sequence of numbers. Therefore, range(5) is a


sequence of five numbers (0 to 4), which means that there will be five iterations in
the loop. In each iteration, we are reading the value entered by the user
using input() and then appending that value to the list using the append() function.
Therefore, finally our list has five elements.

Python 2D List (List inside List)

We can also have a list inside another list. In other words, a list can have an element
which is a list.

mylist = [1, 2, [3, 4, 5], 6]


print(mylist[0])
print(mylist[1])
print(mylist[2])
print(mylist[3])

Output

1
2
[3, 4, 5]
6

Here, the third element of the list mylist is another list [3, 4, 5].

We can access the elements of the inner lists as shown in the following example.

mylist = [1, 2, [3, 4, 5], 6]


print(mylist[2][0])
print(mylist[2][1])
print(mylist[2][2])

Output
3
4
5

mylist[2] is [3, 4, 5]. So, the first element of mylist[2] can be accessed
by mylist[2][0], the second element by mylist[2][1] and the third element
by mylist[2][2].

Different Ways to Create a List in Python

We already know how to create a list.

An empty list is created as follows.

mylist = []

A list having elements is created as follows.

mylist = [1, 2, 3, 4]

Lists can also be created using the list() function. This function converts a
sequence (range, list, string, tuple) or a collection(dictionary, set) into a list.

An empty list can be created using list() as shown below.

mylist = list()
print(mylist)

Output

[]

Now let’s convert a tuple into a list.

mytuple = (1, 2, 3)
mylist = list(mytuple)
print(mylist)

Output

[1, 2, 3]
mytuple is a tuple, which when passed to the list() function returned a list mylist.

mystring = "Codesdope"
mylist = list(mystring)
print(mylist)

Output

['C', 'o', 'd', 'e', 's', 'd', 'o', 'p', 'e']

In the above example, a string is converted into a list. Note that all the characters of
the string became the different elements of the list.

Now let’s write a program to create a list having the first ten natural numbers.

mylist = list(range(1, 11))


print(mylist)

Output

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Here we converted a range of numbers from 1 to 10 into a list. Similarly, we can


create a list of even numbers or odd numbers from 1 to 100. Try it yourself.

Python Copying List

Let’s look at the different ways we can copy a list.

Simple Assignment

A list can be copied to a variable by simply assigning it to the variable.

list1 = [1, 2, 3, 4]
list2 = list1
print(list2)

Output
[1, 2, 3, 4]
Here, the list stored in the variable list1 is assigned to the variable list2. Thus,
both the variables store the same list. As a result, if some change is made in list2, it
will also get reflected in list1, which is often undesirable.

Let’s see how a change in list2 is getting reflected in list1.

list1 = [1, 2, 3, 4]
list2 = list1
list2[0] = 10
print("Original list:", list1)
print("New list:", list2)

Output

Original list: [10, 2, 3, 4]


New list: [10, 2, 3, 4]
After assigning list1 to list2, the first element in list2 is changed. On changing
this, the first element in list1 also got changed. This is because both the variables
are pointing to the memory location of the same list.

To prevent this, we can use the copy() function.

Python copy()

copy() is a function in Python which creates a shallow copy of a list.

list1 = [1, 2, 3, 4]
list1 = [1, 2, 3, 4]
list2 = list1.copy()
list2[0] = 10
print("Original list:", list1)
print("New list:", list2)

Output

Original list: [1, 2, 3, 4]


New list: [10, 2, 3, 4]

In this example, a shallow copy of list1 is created using copy() and that copy is
assigned to list2. As a result, modifying list2 didn’t modify list1 because now the
variables are pointing to the memory addresses of different lists.

Python Slicing Operator ( : )

A list can also be copied using the slicing operator. This is the fastest way to copy a
list.

list1 = [1, 2, 3, 4]
list2 = list1[:]
list2[0] = 10
print("Original list:", list1)
print("New list:", list2)
Output

Original list: [1, 2, 3, 4]


New list: [10, 2, 3, 4]

Python list()

A list can be copied by passing it to the list() function.

list1 = [1, 2, 3, 4]
list2 = list(list1)
list2[0] = 10
print("Original list:", list1)
print("New list:", list2)

Output

Original list: [1, 2, 3, 4]


New list: [10, 2, 3, 4]

We created a new list having all the elements of list1 using list(list1) and
assigned this newly created list to list2.

Python extend()

Another way to copy a list is to add it to an empty list using the extend() function.

list1 = [1, 2, 3, 4]
list2 = []
list2.extend(list1)
list2[0] = 10
print("Original list:", list1)
print("New list:", list2)

Output
Original list: [1, 2, 3, 4]
New list: [10, 2, 3, 4]

We created an empty list list2 and added list1 to it by


writing list2.extend(list1). So, now list2 consists of all the elements of list1.

Built-in List Functions in Python

The functions available in Python which can be used with lists are shown below.

Python len()

It returns the number of elements in a list.

mylist = [1, 2, 3, 4, 5, 6]
print(len(mylist))

Output

Python max()

It returns the element from a list that has the maximum value.

mylist = [3, 2, 1, 5, 6, 4]
print(max(mylist))

Output

Python min()
It returns the element from a list that has the minimum value.

mylist = [3, 2, 1, 5, 4]
print(min(mylist))

Output

Python sum()

It returns the sum of all the elements in a list.

mylist = [3, 2, 1, 5, 6, 4]
print(sum(mylist))

Output

21

Python sorted()

It returns a sorted list of a sequence (list, string, tuple, range) or a collection


(dictionary, set).

mylist = [3, 2, 1, 5, 6, 4]
print(sorted(mylist))

Output

[1, 2, 3, 4, 5, 6]

Here the elements of the list mylist got sorted.

In the next example, a tuple mytuple got converted into a list with its elements
sorted.

mytuple = (3, 2, 1)
print(sorted(mytuple))

Output

[1, 2, 3]

To sort the elements in descending order, pass another argument reverse=True to


the sorted function as shown below.

mylist = [3, 2, 1, 5, 6, 4]
print(sorted(mylist, reverse=True))

Output

[6, 5, 4, 3, 2, 1]

Python any()

It returns True if any of the elements in a list are True. Otherwise, it returns False.

mylist = [3, 2, 1, True]


print(any(mylist))

Output

True

any(mylist) returned True because all the elements of the list mylist are non-zero
or True.

Look at another example where one element of the list is False.

mylist = [3, 2, 1, False]


print(any(mylist))

Output

True

We have one element of the list False, but still the any() function returned True. This
is because if atleast one element is non-zero or True, then the function returns True.
Now consider the following example.

mylist = ["", 0, False]


print(any(mylist))

Output

False

The function returned False because the first element is a null string, the second
element is 0 and the third element is False.

Python all()

It returns True if all the elements in a list are True. Otherwise, it returns False.

mylist = [3, 2, 1, True]


print(all(mylist))

Output

True

mylist = [3, 2, 1, False]


print(all(mylist))

Output

False

In this example, one element is False and so the all() function returned False.

Other Python List Functions

We have already looked at some of the list functions


like append(), insert(), extend(), remove(), pop(), clear() and copy(). Other
useful functions are shown below.
Python index()

It returns the index of the specified element in a list.

languages = ['Python', 'C', 'Java', 'C++']


print(languages.index('Java'))

Output

languages.index(‘Java’) returned the index of 'Java' in the list languages.

If the specified element occurs more than once in a list, then the index of its first
occurrence is returned as shown below.

languages = ['Python', 'C', 'Java', 'C++', 'Java']


print(languages.index('Java'))

Output

In the above example, 'Java' is present at the indices 2 and 4 in the list languages.
However, with the index() function, only the index 2 of its first occurrence got
returned.

We can also pass the start index from which we want the search to begin and the
end index upto which we want the search to take place.

languages = ['Python', 'C', 'Java', 'C++', 'Java', 'Perl']

print(languages.index('Java', 3)) # 'Java' is searched after index 3


print(languages.index('Java', 1, 3)) # 'Java' is searched between index 1 and ind
ex 3

Output

4
2
languages.index(‘Java’, 3) returned the index of 'Java' after index 3 (3 is the start
index).

languages.index(‘Java’, 1, 3) returned the index of 'Java' between indices 1 and


3 (1 is the start index and 3 is the end index).

Python count()

It returns the number of times the specified element occurs in a list.

languages = ['Python', 'C', 'Java', 'C++', 'Java']


print(languages.count('Java'))

Output

Python sort()

It sorts the elements of a list in ascending or descending order.

numbers = [10, 20, 15, 30, 25]


numbers.sort() # sorting the elements
print(numbers) # printing sorted list

Output

[10, 15, 20, 25, 30]

numbers.sort() sorted the elements of the list number is ascending order.

If nothing is passed to the sort() function, then the list is sorted in ascending order.
In order to sort the list in descending order, we need to pass reverse=True to
the sort() function.

languages = ['Python', 'C', 'Java', 'C++', 'Perl']


languages.sort(reverse=True) # sorting the elements in descending order
print(languages) # printing sorted list
Output

['Python', 'Perl', 'Java', 'C++', 'C']

In this example, the words in the list are sorted in descending order based on the
dictionary.

Difference between sort() and sorted()

You might be wondering why we have two separate


functions sort() and sorted() when both are used to sort lists. It is true that both
are used to sort lists, however, there is a small difference.

sort() changes the original list and doesn’t return anything. Let’s again look at the
following example.

numbers = [10, 20, 15, 30, 25]


numbers.sort() # sorting the elements
print(numbers) # printing original list

Output

[10, 15, 20, 25, 30]

The original list got updated. numbers.sort() sorted the elements of the original
list numbers, thus updating it.

On the other hand, sorted() doesn’t change the original list, but returns the sorted
list. Look at the following example to understand this.

numbers = [10, 20, 15, 30, 25]


new_list = sorted(numbers) # sorting the elements of original list and returning t
he updated list
print("Original list:", numbers) # printing original list
print("Updated list:", new_list) # printing updated list

Output

Original list: [10, 20, 15, 30, 25]


Updated list: [10, 15, 20, 25, 30]
From the output, we can see that the original list didn’t get
updated. sorted(numbers) sorted the elements of the original list but didn’t update it.
Instead, it returned the sorted list which we assigned to new_list.
Therefore, new_list contains the sorted list.

Python reverse()

It reverses a list.

languages = ['Python', 'C', 'Java', 'C++']


languages.reverse() # reversing the list
print(languages)

Output

['C++', 'Java', 'C', 'Python']

You can learn more about lists by rea

Python List Comprehension

List comprehension is an easy way of creating lists within a single line of code.
Suppose you have a list of integers and you want to create a new list in which the
elements are square of the corresponding elements of the first list. You can do this in
a single line of code using list comprehension.

We will look at how to create lists using list comprehension with examples and will
also look at the cases where it should be avoided.

Using List Comprehension in Python


Let’s take an example in which we will create a list of the cubes of numbers from 1 to
10. From what we have learned so far, we can easily use a for loop to iterate
range() and calculate cubes and store them in a list.

The following example prints the cube of all numbers from 1 to 10 (included) using
a for loop.

cubes = []

for i in range(1, 11):


cubes.append(i**3)

print(cubes)

Output

[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]


We created an empty list cubes to store the cubes. Then we iterate over range(1,
11) using a for loop. In each iteration, the cube of the number is calculated and the
result is appended to the list cubes.

We can do the same in a single line of code using list comprehension as shown
below.

cubes = [i**3 for i in range(1, 11)]


print(cubes)

Output

[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]


If you closely look at [i**3 for i in range(1, 11)], you will find a for loop
definition - for i in range(1, 11).

Let’s start with the for loop - for i in range(1, 11). It is a simple for loop through
which we are iterating over range(1, 11).

Now in each iteration, i**3 is evaluated.

So in the first iteration, i is 1 and hence i**3 is also 1.

In the second iteration, i is 2 and hence i**3 is 8. This goes for all the ten
iterations.
The interesting part is that the values we get after evaluation of i**3 in each iteration
are added in a list and that list is returned at the end of the loop. The results of the
evaluation of the expression in different iterations constitute the different elements of
the list.

Thus, this method of creating the list cubes is called list comprehension.

Summing up, the expression of a list comprehension is enclosed within brackets [ ].

Inside [ ], we use a for loop which has a variable (i in the above example) for
iteration. Before the for loop we also have an expression (i**3 in the above
example) which is evaluated in each iteration and the result of this evaluation is
added to a list which we are going to get at the end of the loop.
We have created a list using list comprehension. Now let’s look at the general syntax
of list comprehension.

Python List Comprehension Syntax

new_list = [expression for_loop]

The list returned by list comprehension method is enclosed within brackets [ ]. On


each iteration of the for_loop, the expression is evaluated and defines the
elements of the list.
Comparing this syntax to the last example, i**3 is expression and for i in
range(1, 11) is for_loop.

Python List Comprehension Examples

Let’s look at a few more examples.

The following example creates a list mylist of numbers from 1 to 5 (included) using
a for loop.

mylist = []

for i in range(1, 6):


mylist.append(i)

print(mylist)

Output

[1, 2, 3, 4, 5]
We can create the same list using list comprehension as shown below.

mylist = [i for i in range(1, 6)]


print(mylist)

Output

[1, 2, 3, 4, 5]
In for i in range(1, 6), the variable i iterates over range(1, 6).

In each iteration, the expression i is evaluated. In the first iteration, i is 1. In


the second iteration, i is 2, and this goes on till the fifth iteration.

The values of i in the five iterations constitutes the five elements of the list mylist.

Comparing this code to the syntax, i is expression and for i in range(1,


6) is for_loop.

Now let’s create a list having five items, each equal to 2, using list comprehension.

mylist = [2 for i in range(1, 6)]


print(mylist)

Output

[2, 2, 2, 2, 2]
In for i in range(1, 6), the variable i iterates over range(1, 6). Therefore there
are five iterations. In each iteration, the value 2 is appended to the list. Therefore, the
list mylist has five elements, each equal to 2.

Comparing this code to the syntax, 2 is expression and for i in range(1,


6) is for_loop.

Using List Comprehension with Conditional


Statements in Python

We can also use some condition with list comprehension. For example, if we want to
create a list of all even numbers from 1 to 100, we can add a condition i%2==0 and
only the elements passing this condition can be included in our list.

Let’s see how to implement list comprehension when an if statement is used in the
body of a for loop.

The following example creates a list containing all even numbers from 1 to 10
(included).

even = []

for num in range(1, 11):


if num % 2 == 0:
even.append(num)

print(even)

Output

[2, 4, 6, 8, 10]
We iterate over range(1, 11). In each iteration, it is checked if the number is
divisible by 2. If it is, then it is appended to the list even.

Let’s create this list using list comprehension.

even = [num for num in range(1, 11) if num % 2 == 0]


print(even)

Output

[2, 4, 6, 8, 10]
Here in the for loop for num in range(1, 11), the variable num iterates
over range(1, 11).

In each iteration, the if condition num % 2 == 0 checks if num is even. If the condition
is True (which means if num is even), then num is appended to the list, otherwise it is
not appended.

On comparing this example with the example using for loop, you will understand the
match.

Now let’s look at the syntax of list comprehension when some if condition is used.

Syntax

new_list = [expression for_loop if condition]

On each iteration of the for_loop, the if condition is checked. If the condition is


True, then only the expression is evaluated and appended to the list.

Comparing this syntax to the last example, num is expression, for i in range(1,
11) is for_loop and and if num % 2 == 0 is if condition.

Python If...else with List Comprehension

Using if...else while creating lists using list comprehension has a slightly different
syntax than using just if. Let’s look at the two syntaxes.

Syntax of List Comprehension including if statement


new_list = [expression for_loop if condition]

We have already seen two examples where if statements were used.

Syntax of List Comprehension including if..else statement

new_list = [expression1 if condition else expression2 for_loop]

Here, if the condition of if is True, then expression1 is evaluated,


else expression2 is evaluated. Rest everything is the same.

Let’s see how if..else is implemented in list comprehension.

The following example stores the square of all even numbers and the cube of all odd
numbers from 1 to 10 (included) in a list using a for loop.

result = []

for num in range(1, 11):


if num % 2 == 0:
result.append(num ** 2)
else:
result.append(num ** 3)

print(result)

Output

[1, 4, 27, 16, 125, 36, 343, 64, 729, 100]


In each iteration, we are checking if the number is even. If it is even, we are
appending the square of the number, otherwise we are appending the cube of the
number to the list.

This can be transformed using list comprehension as follows.

result = [num ** 2 if num % 2 == 0 else num ** 3 for num in range(1, 11)]


print(result)

Output

[1, 4, 27, 16, 125, 36, 343, 64, 729, 100]


In the for loop for num in range(1, 11), the variable num iterates over range(1,
11).

In each iteration, the if condition num % 2 == 0 checks if num is divisible by 2. If this


condition is True, then the expression num ** 2 is evaluated and appended to the
list. Otherwise, the expression num ** 3 is evaluated and appended to the list.

Comparing this code to the syntax, num ** 2 is expression1, num **


3 is expression2, num % 2 is condition and for num in range(1, 11) is for_loop.
In each iteration of for_loop, if condition is True, then expression1 is evaluated
and added to the list, else expression2 is evaluated and added to the list.

Using List Comprehension with Nested Loops in


Python

Let’s see how to use List Comprehension in case of nested loops.

In the following example, we are creating a list result which contains the elements
common in two lists list1 and list2.

list1 = [1, 2, 3, 4]
list2 = [1, 20, 30, 4]
result = []

for x in list1:
for y in list2:
if x == y:
result.append(y)

print(result)

Output

[1, 4]
The outer for loop is iterating over the elements of list1 and the inner for loop is
iterating over the elements of list2. For each iteration of the outer loop, there is a
complete iteration of the inner loop, and for each iteration of the inner loop we are
checking if the values of x and y are equal. If the values are equal, then it is
appended to the list result.

This code can be rewritten using list comprehension as follows.

list1 = [1, 2, 3, 4]
list2 = [1, 20, 30, 4]

result = [y for x in list1 for y in list2 if x == y]

print(result)

Output

[1, 4]
In this example, we have two for loops. The loop for y in list2 is inside the
loop for x in list1. The if statement if x == y is inside the inner for loop.

In the outer loop for x in list1, the variable x iterates over list1, and in the inner
loop for y in list2, the variable y iterates over list2. In each iteration of the outer
loop, the inner loop iterates over list2. In each iteration of the inner loop, if the
condition x == y is True, then the variable y is added to the list.

Comparing this code to the syntax, y is expression, x == y is condition and for x


in list1 and for y in list2 are two for_loops.

When to use List Comprehension

Creating lists using list comprehension is faster as compared to loops mainly


because we don’t need to call the append() function in each iteration. Moreover, it
takes just one line of code thus making the code more readable.

However, that doesn’t mean list comprehension should be used to replace for loop
every time a list is created. If the logic is long or complex, then using list
comprehension is a bad choice. This is because it can become less readable and
you won’t be able add comments, thus making it difficult to debug as well.
Therefore, use list comprehension if the logic is simple and small and use for loop
otherwise

Python Strings

You have already been introduced with strings. Like a list, a string is also a
sequence. It is a sequence of characters.

Creating a String in Python

A string is created by enclosing the sequence of characters within single quotes '
' or double quotes " ". Therefore, 'Hello World' and "Hello World" both are strings.

mystring = 'Hello World'


print(mystring)
print(type(mystring))

Output

Hello World <class 'str'>

mystring = "Hello World"


print(mystring)
print(type(mystring))

Output

Hello World <class 'str'>

As we mentioned that a string is a sequence of characters, the string “Hello World” is


a sequence of 11 characters - ‘H’, ‘e’, ‘l’, ‘l’, ‘o’, ‘ ‘, ‘W’, ‘o’, ‘r’, ‘l’, ‘d’. Yes, space is
also a character. In fact, all the alphabets, digits, punctuation marks, space, or any
other special character enclosed within ' ' or " " is a character.

Python Multiline Strings


Multiline strings are strings which extend upto multiple lines. These are enclosed
within triple single quotes ''' ''' or triple double quotes """ """.

mystring = '''This is a multiline string


which is extended in two lines'''
print(mystring)

Output

This is a multiline string


which is extended in two lines

mystring = """This is a multiline string


which is extended in two lines"""
print(mystring)

Output

This is a multiline string


which is extended in two lines

Multiline strings can also be used as multiline comments or docstrings if not


assigned to a variable. This is because if not assigned to a variable or not involved in
any operation, these are not really doing anything in the code and hence get ignored.
We already read about multiline comments in the chapter Basics of Python. We will
be looking at docstrings in a separate chapter.

Python Accessing Characters from String

A particular character from a string can be accessed using its index.

Just like lists, every character in a string has a unique index based on its position.
Indices start from 0. Therefore, the first character of a string has index 0, the second
character has index 1, and so on.

For example, consider the following string.


mystring = "Hey!"

In this string, the index of 'H' is 0, 'e' is 1, 'y' is 2 and '!' is 3. Thus, the index started
from 0 and went up to 3.

charcter 'H' 'e' 'y' '!'

index 0 1 2 3

A character of a string can be accessed by writing name_of_string[index], where


index is the index of the character in the string.

Therefore, the 2nd character of the string mystring can be accessed by writing
mystring[1] (because the index of the 2nd character is 1).

mystring = "Hey!"
print(mystring[0])
print(mystring[1])
print(mystring[2])
print(mystring[3])

Output

H
e
y
!

Python Negative Index

Just like lists, in strings also we can access a character by using a negative index.

The last character of a string has index -1, second last character has index -2, and
so on.

mystring = "Hey!"
#accessing using indices
print(mystring[0])
print(mystring[1])
print(mystring[2])
print(mystring[3])

# accessing using negative indices


print(mystring[-1])
print(mystring[-2])
print(mystring[-3])
print(mystring[-4])

Output

H
e
y
!
!
y
e
H

Here, mystring[-1] returns the last character ‘!’ of the string mystring, mystring[-
2] returns the second last character ‘y’, and so on.

Python String Slicing

A range of characters from a string can be accessed using the slicing operator :.

To access characters from the ith index to the jth index of a string, we
write name_of_string[i:j+1].

mystring = "Python@Codesdope"
print(mystring[2:6])
Output

thon

mystring[2:6] returned the characters from the 2nd index to the 5th index i.e., 'thon'.

To access the characters from index 4 till the end of a string,


use name_of_string[4:].

To access the characters from the start till index 4 of a string,


use name_of_string[:5].

To access all the characters from the start till the end of a string,
use name_of_string[:].

mystring = "Python@Codesdope"

# from 2nd character till 5th character


print(mystring[1:5])

# from start till 5th character


print(mystring[:5])

# from 5th character till end


print(mystring[4:])

# from start till 3rd last character


print(mystring[:-2])

# from 4th last character till 3rd last character


print(mystring[-4:-2])

# from 3rd character till 3rd last character


print(mystring[2:-2])

# all characters
print(mystring[:])

Output
ytho
Pytho
on@Codesdope
Python@Codesdo
do
thon@Codesdo
Python@Codesdope

We can also give a step to the slice to skip some number of characters while slicing.

Look at the following example.

mystring = "Python@Codesdope"

# from 2nd character till 9th character


print(mystring[1:9])

# from 2nd character till 9th character in step 2


print(mystring[1:9:2])

# from 2nd character till 9th character in step 3


print(mystring[1:9:3])

Output

ython@Co
yhnC
yoC

mystring[1:9] returned the characters from the 1st index till the 8th index.

mystring[1:9:2] also returned the characters from the 1st index till the 8th index, but
it returned every 2nd character after the character having the start index.

mystring[1:9:3] also returned the characters from the 1st index till the 8th index, but
it returned every 3rd character after the character having the start index.

mystring = "Python@Codesdope"

# from 3rd character till 9th character


print(mystring[2:9])
# from 3rd character till 9th character in step 2
print(mystring[2:9:2])

# from 3rd character till 9th character in step 3


print(mystring[2:9:3])

# from start till 9th character in step 2


print(mystring[:9:2])

# from 3rd character till end in step 3


print(mystring[2::3])

# characters from start till end in step 2


print(mystring[::2])

Output

thon@Co
to@o
tno
Pto@o
tnosp
Pto@oedp

Giving a negative step will return the characters of a string in the reverse order. A
step of -1 returns the reversed string.

mystring = "Python@Codesdope"

# reversed string
print(mystring[::-1])

# reversed string with step -2


print(mystring[::-2])

Output
epodsedoC@nohtyP
eosdCnhy

Python Changing Characters of String

Strings are immutable, which means that the characters of a string can’t be
changed. However, a new string value can be assigned to the same variable.

Let’s try to change a single element of a string.

mystring = "Python@Codesdope"
mystring[6] = "a"

Output

Traceback (most recent call last):


File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment

We tried to change the 7th character of a string and got an error.

Now, let’s assign a new string to the variable mystring.

mystring = "Python@Codesdope"
mystring = "Java@Codesdope"
print(mystring)

Output

Java@Codesdope

Python Deleting String

We cannot delete characters from a string, Instead, we can delete an entire string
using the del keyword.
mystring = "Python@Codesdope"
del mystring[6]

Output

Traceback (most recent call last):


File "<stdin>", line 1, in <module>
TypeError: 'str' object doesn't support item deletion

We tried to delete the 7th character of a string and got an error.

mystring = "Python@Codesdope"
del mystring
print(mystring) # printing the string after deleting it

Output

Traceback (most recent call last):


File "<stdin>", line 1, in <module>
NameError: name 'mystring' is not defined

We deleted the entire string mystring. Thus, we got an error on printing the string
after deleting it.

Python String Operations

Python String Concatenation (+)

Two or more strings can be concatenated or combined using the + operator.

string1 = "Codes"
string2 = "Dope"
string3 = string1 + string2
print(string3)
Output

CodesDope

string1 and string2 are concatenated to form string3.

Python String Repetition (*)

A string can be repeated a specified number of times using the * operator.

string1 = "Python"
string2 = string1 * 3
print(string2)

Output

PythonPythonPython

string1 is repeated 3 times to form string2.

Python String Membership Test

We can test if a character is present in a string using the membership


operators in and not in.

mystring = "Ice and candy"


print("e an" in mystring)
print("ice" in mystring)
print("candy" not in mystring)

Output

True
False
False

Python String Comparison


Two strings can be compared using Relational Operators. Strings are compared
character by character, and these characters are compared lexicographically i.e.
based on their ASCII values. The character having the lower Unicode value is
considered smaller. You can find the ASCII values for different characters here.

string1 = "Candy"
string2 = "Candy"

print(string1 == string2)
print(string1 != string2)
print(string1 > string2)
print(string1 < string2)
print(string1 >= string2)
print(string1 <= string2)

Output

True
False
False
False
True
True

The values of both the strings are same i.e., "Candy".

string1 = "Hat"
string2 = "Heat"

print(string1 == string2)
print(string1 != string2)
print(string1 > string2)
print(string1 < string2)
print(string1 >= string2)
print(string1 <= string2)

Output
False
True
False
True
False
True

Here the strings "Hat" and "Heat" are compared. To start with, the ASCII values of
the first characters of both the strings are compared. Since that is equal, the ASCII
values of the second characters of the strings are then compared. The ASCII value
of ‘a’ (97) is smaller than that of ‘e’ (101), and therefore the string "Hat" is smaller
than "Heat" lexicographically.

Looking at the ASCII chart, we can see that the ASCII values of uppercase
alphabets (65 - 90) are smaller than those of lowercase alphabets (97 - 122).
Therefore, the comparisons are case-sensitive.

string1 = "Hat"
string2 = "hat"

print(string1 == string2)
print(string1 != string2)
print(string1 > string2)
print(string1 < string2)

Output

False
True
False
True

"Hat" is smaller than "hat" because the ASCII value of ‘H’ is smaller than that of ‘h’.

Python does not handle uppercase and lowercase letters the same way that people
do. All the uppercase letters come before all the lowercase letters.

Python Iteration Through a String


Iteration through a string is done in the same way we iterate through a list because
string is also a list in which each element is a character.

mystring = "CodesDope"
for ch in mystring:
print(ch)

Output

C
o
d
e
s
D
o
p
e

A variable ch goes to each element in the string mystring and takes its value, and
that value is printed on the screen.

We can also iterate through a string using another way.

mystring = "Hey"
for i in range(len(mystring)):
print(mystring[i])

Output

H
e
y

len() function is used to return the number of characters in a string.


Therefore range(len(mystring)) is equal to range(3). This means that there will be
three iterations in the loop. You must have understood the rest of the code.
Using Quotes in Python String

Suppose we have a string "This is "Python" course". This string contains another set
of double quotes inside it. Let’s try to print it.

mystring = "This is "Python" course"


print(mystring)

Output

File "", line 1


mystring = "This is "Python" course"
^
SyntaxError: invalid syntax

We got a syntax error. This is because we cannot nest the same type of quotes with
which we enclose the string.

To prevent this, there are two solutions.

In the first solution, follow the rule that if a string contains double quotes, then
enclose it within single quotes, and if it contains single quotes, then enclose it within
double quotes. Thus, the strings 'This is "Python" course' and "This is 'Python'
course" will give no syntax errors.

mystring = 'This is "Python" course'


print(mystring)

Output

This is "Python" course

mystring = "This is 'Python' course"


print(mystring)

Output

This is 'Python' course


The other solution is to use \' and \" in place of ' and " respectively in the
string. \' and \" are escape sequences which we will look at in the next section.
mystring = "This is \"Python\" course"
print(mystring)

Output

This is "Python" course

Python Escape Sequence

An escape sequence starts with an escape character (backslash \) and is followed


by a special character (characters having special meaning in Python like ', '', etc).
The escape sequences supported by Python get interpreted when placed inside a
string.

For example, \' and \" are two escape sequences which tell Python that ' and " are
not the special string opening and closing quotes and just simple ‘ and “ characters.
We have seen an example of these two escape sequences in the previous section.

The list of escape sequences supported by Python are given below.

Escape Sequence Description

\\ Backslash (\)

\' Single Quote (')

\" Double Quote (")

\a ASCII Bell
\b ASCII Backspace

\f ASCII Form Feed

\n ASCII Line Feed

\r ASCII Carriage Return

\t ASCII Horizontal Tab

\v ASCII Vertical Tab

\uxxxx Character with 16-bit hex value xxxx (Unicode only)

\Uxxxxxxxx Character with 32-bit hex value xxxxxxxx (Unicode only)

\ooo Character with octal value ooo

print("C:\\Documents\\Folder")
print("This is \'Python\' course")
print("This is \"Python\" course")
print("First line\nSecond line")
print("Before horizontal tab\tAfter horizontal tab")
print("Before horizontal tab\t\tAfter two horizontal tabs")

Output
C:\Documents\Folder
This is 'Python' course
This is "Python" course
First line
Second line
Before horizontal tab After horizontal tab
Before horizontal tab After two horizontal tabs
\\ gets replaced by \, \' gets replaced by ', \" gets replaced by ", \n inserts a new
line and \t inserts a horizontal tab.

\n

\n or new line character is a special character which we frequently use. As stated


above it is used to print a new line. Let’s look at some examples of it.

print("ab\ncde")
print("Hello\n\n\n\nCodesDope")
print("Hello Codes\nDope")
print("Hello World")

Output

ab
cde
Hello

CodesDope
Hello Codes
Dope
Hello World

Python Ignoring Escape Sequence


The meaning of escape sequences inside a string can be ignored and these can be
displayed as part of the string by adding r or R before the string.

print(r"C:\\Documents\\Folder")
print(R"This is \'Python\' course")

Output

C:\\Documents\\Folder
This is \'Python\' course

In this example, we can see that \\ didn’t get replaced by \ and \' didn’t get
replaced by '.

Creating List from String in Python

A string can be converted into a list using the following functions.

list()

With the list() function, each character of a given string becomes an element of the
list.

mystring = "Codesdope"
mylist = list(mystring)
print(mylist)

Output

['C', 'o', 'd', 'e', 's', 'd', 'o', 'p', 'e']

split()
The split() function breaks a given string into multiple substrings at the specified
separator and returns a list having these substrings as its elements.

Let’s look at an example.

mystring = "Have a good day"


print(mystring.split(" "))

Output

['Have', 'a', 'good', 'day']

In mystring.split(" "), the separator is space " ". Therefore, the string got
splitted at the spaces " " giving four substrings 'Have', 'a', 'good' and 'day', and a list
is returned containing the substrings as elements.

The syntax of split is:

str.split(separator, maxsplit)

separator - The string gets split at the specified separator. This is also called
a delimiter. Its default value is whitespace (space, newline, etc.).

maxsplit - This specifies the number of times the string is split at the separator. Its
default value is -1 (which means that the string is split at every occurrence of the
separator).

Let’s take another example where comma is used as a separator.

mystring = "Apples, Oranges, Grapes, Watermelons"


print(mystring.split(", "))

Output

['Apples', 'Oranges', 'Grapes', 'Watermelons']

Here, ", " is used as the separator.

If no separator is specified, then by default any whitespace (space, newline, etc.)


becomes the separator.

mystring = "Have a good day"


print(mystring.split())
Output

['Have', 'a', 'good', 'day']

In the above example, no separator was passed to the split() function, and so by
default the string got splitted into substrings at the spaces.

A second value (maxsplit) can be passed to the split() function to specify the
number of times a given string is split at the specified separator. If this value is 1,
then the string gets splitted into two substrings at the leftmost separator. If the value
is 2, then the string gets splitted into three substrings at the leftmost and the second
left separators.

mystring = "Python.Java.C.Perl.C#.Ruby"

print(mystring.split(".", 1)) # maxsplit = 1


print(mystring.split(".", 2)) # maxsplit = 2
print(mystring.split(".", 3)) # maxsplit = 3
print(mystring.split(".", 0)) # maxsplit = 0
print(mystring.split(".")) # no maxsplit

Output

['Python', 'Java.C.Perl.C#.Ruby']
['Python', 'Java', 'C.Perl.C#.Ruby']
['Python', 'Java', 'C', 'Perl.C#.Ruby']
['Python.Java.C.Perl.C#.Ruby']
['Python', 'Java', 'C', 'Perl', 'C#', 'Ruby']

In this example, when the maxsplit value is 1, then the string gets split at the leftmost
separator . into two substrings i.e., ‘Python’ and ‘Java.C.Perl.C#.Ruby’. When
the maxsplit value is 2, it gets split at the two leftmost separators . into three
substrings i.e., 'Python', 'Java', and 'C.Perl.C#.Ruby'. When the maxsplit value is 0,
then the string doesn’t get split. When no maxsplit is passed, then it gets split at
every separator.

rsplit()
The rsplit() function breaks a given string into multiple substrings at the specified
separator from the right and returns a list having these substrings as its elements.
The only difference from split() is that split() splits from the left
while rsplit() splits from the right.
Syntax

str.rsplit(separator, maxsplit)

separator - The string gets split at the specified separator from the right. This is also
called a delimiter. Its default value is whitespace (space, newline, etc.).

maxsplit - This specifies the number of times the string is split at the separator. Its
default value is -1 (which means that the string is split at every occurrence of the
separator).
Example

If maxsplit is not passed, rsplit() gives the same result as split().

mystring = "Apples, Oranges, Grapes, Watermelons"


print(mystring.rsplit(", "))
print(mystring.rsplit())

Output

['Apples', 'Oranges', 'Grapes', 'Watermelons']


['Apples,', 'Oranges,', 'Grapes,', 'Watermelons']

If maxsplit is passed, it specifies the number of times a given string is split at the
specified separator from the right.

mystring = "Python.Java.C.Perl.C#.Ruby"

print(mystring.rsplit(".", 1)) # maxsplit = 1


print(mystring.rsplit(".", 2)) # maxsplit = 2
print(mystring.rsplit(".", 3)) # maxsplit = 3
print(mystring.rsplit(".", 0)) # maxsplit = 0
print(mystring.rsplit(".")) # no maxsplit

Output
['Python.Java.C.Perl.C#', 'Ruby']
['Python.Java.C.Perl', 'C#', 'Ruby']
['Python.Java.C', 'Perl', 'C#', 'Ruby']
['Python.Java.C.Perl.C#.Ruby']
['Python', 'Java', 'C', 'Perl', 'C#', 'Ruby']

splitlines()

The splitlines() function splits a given string into multiple substrings at line breaks
and returns a list having these substrings as its elements.

string1 = "Hello learners, Welcome to the Python course."


print(string1.splitlines())

string2 = '''Hello learners,


Welcome to the Python course.'''
print(string2.splitlines())

Output

['Hello learners, Welcome to the Python course.']


['Hello learners,', 'Welcome to the Python course.']

string1 has no line break, therefore the list returned by the splitlines() function
has only one element which is the string itself.

string2 is a multiline string which has a line break. As a result, it gets split into two
substrings at the line break and the returned list contains these two substrings as its
elements.

Line breaks in a string are also induced by various escape characters


like \n and \r. \n is called the newline character which breaks the string when printed.

mystring = "Hello learners,\nWelcome to the Python course."


print(mystring)
print(mystring.splitlines())

Output
Hello learners,
Welcome to the Python course.
['Hello learners,', 'Welcome to the Python course.']

Here, we have first printed the string mystring to show you how printing \n breaks
the line. Therefore, splitlines() splits the string into two substrings at the line
break \n.

Built-in Functions in Python

Python len()

It returns the number of characters in a string.

mystring = "Hello learners"


print(len(mystring))

Output

14

Common Python String Functions

We have already looked at some of the string functions


like split(), rsplit(), splitlines(), remove(), pop(), clear() and copy(). Other
useful functions are shown below.

Python capitalize()

It returns a string with the first character in uppercase and the other characters in
lowercase. It doesn’t change the original string.
mystring = "hEy tHEre WOrld!"
new_string = mystring.capitalize()
print("Original string:", mystring) # printing original string
print("Updated string:", new_string) # printing updated string

Output

Original string: hEy tHEre WOrld!


Updated string: Hey there world!

Python lower()

It returns a string with all the characters in lowercase. It doesn’t change the original
string.

mystring = "hEy tHEre WOrld!"


new_string = mystring.lower()
print("Original string:", mystring) # printing original string
print("Updated string:", new_string) # printing updated string

Output

Original string: hEy tHEre WOrld!


Updated string: hey there world!

Python upper()

It returns a string with all the characters in lowercase. It doesn’t change the original
string.

mystring = "hEy tHEre WOrld!"


new_string = mystring.upper()
print("Original string:", mystring) # printing original string
print("Updated string:", new_string) # printing updated string

Output
Original string: hEy tHEre WOrld!
Updated string: HEY THERE WORLD!

Python swapcase()

It returns a string with the case of all the characters inverted, which means that all
lowercase characters get converted to uppercase and vice versa. It doesn’t change
the original string.

mystring = "hEy tHEre WOrld!"


new_string = mystring.swapcase()
print("Original string:", mystring) # printing original string
print("Updated string:", new_string) # printing updated string

Output

Original string: hEy tHEre WOrld!


Updated string: HeY TheRE woRLD!

Python title()

It returns a string with the first character of each in uppercase and the rest of the
characters in lowercase. It doesn’t change the original string.

mystring = "hEy tHEre WOrld!"


new_string = mystring.title()
print("Original string:", mystring) # printing original string
print("Updated string:", new_string) # printing updated string

Output

Original string: hEy tHEre WOrld!


Updated string: Hey There World!

Python join()
It joins the element of a list to form a string.

mystring = ["hEy", "tHEre", "WOrld!"]


print(" ".join(mystring))
print(";".join(mystring))

Output

hEy tHEre WOrld!


hEy;tHEre;WOrld!

Python index()

It returns the index of the specified substring in a string.

mystring = "I am a programmer"


print(mystring.index("prog"))

Output

mystring.index("prog") returned the index of "prog" in the string mystring.

If the specified substring occurs more than once in a string, then the index of its first
occurrence is returned as shown below.

mystring = "I am a programmer"


print(mystring.index("am"))

Output

In this example, the substring "am" occurs twice at indices 2 and 12, but only the
index of its first occurrence is returned.

We can also pass the start index from which we want the search to begin and the
end index upto which we want the search to take place.
mystring = "I am a programmer"
print(mystring.index("am", 6)) # "am" is searched after index 6
print(mystring.index("am", 1, 6)) # "am" is searched between index 1 and index 6

Output

12
2

<pmystring.index("am", 6) returned the index of "am" after index 6 (6 is


the start index).
print(mystring.index("am", 1, 6)) returned the index of "am" between
indices 1 and 6 (1 is the start index and 6 is the end index).

Python rindex()

It returns the highest index of the specified substring in a string. We can


also pass the start index from which we want the search to begin and the
end index upto which we want the search to take place.

mystring = "I am a programmer"


print(mystring.rindex("prog"))
print(mystring.rindex("am", 6)) # "am" is searched after index 6
print(mystring.rindex("am", 1, 6)) # "am" is searched between index 1 and index 6

Output

7
12
2

In the above example, using index() will give the same result.

In case of multiple occurrences of the specified substring in a string,


then the index of its last occurrence is returned as shown below.

mystring = "I am a programmer"


print(mystring.rindex("am"))
Output

12

In this example, the substring "am" occurs twice at indices 2 and 12, but
only the index of its last occurrence is returned.

Python replace()

It replaces a substring with another substring in a string.

mystring = "I am a programmer"


print(mystring.replace("am", "was"))

Output

I was a progrwasmer

In this example, both the occurrences of the substring "am" is replaced by


another substring "was".

We can also pass a second value to the replace() function to specify the
number of occurrences of the old substring to be replaced.

mystring = "I scream, you scream, we all scream for ice cream"
print(mystring.replace("scream", "cry")) # all occurrences of scream get replaced
print(mystring.replace("scream", "cry", 2)) # first two occurrences of scream get
replaced

Output

I cry, you cry, we all cry for ice cream


I cry, you cry, we all scream for ice cream

Python isalnum()

It returns True if all the characters of a given string are alphanumeric


(either alphabets or numbers). Otherwise, it returns False.
string1 = "Abcd123dE"
string2 = "Abcd,123dE" # contains a comma
string3 = "564782"

print("Is string1 alphanumeric?", string1.isalnum())


print("Is string2 alphanumeric?", string2.isalnum())
print("Is string3 alphanumeric?", string3.isalnum())

Output

Is string1 alphanumeric? True


Is string2 alphanumeric? False
Is string3 alphanumeric? True

Python isalpha()

It returns True if all the characters of a given string are alphabets.


Otherwise, it returns False.

string1 = "AbcdEf"
string2 = "Abcd 123dE" # contains a space
string3 = "Abcd123dE" # contains numbers

print("Does string1 contain only alphabets?", string1.isalpha())


print("Does string2 contain only alphabets?", string2.isalpha())
print("Does string3 contain only alphabets?", string3.isalpha())

Output

Does string1 contain only alphabets? True


Does string2 contain only alphabets? False
Does string3 contain only alphabets? False

Python isdigit()
It returns True if all the characters of a given string are digits.
Otherwise, it returns False.

string1 = "2274645"
string2 = "Abcd123dE" # contains alphabets

print("Does string1 contain only digits?", string1.isdigit())


print("Does string2 contain only digits?", string2.isdigit())

Output

Does string1 contain only digits? True


Does string2 contain only digits? False

Python islower()

It returns True if all the alphabets of a given string are in lowercase.


Otherwise, it returns False.

string1 = "codesdope"
string2 = "CodesDope"

print("Is string1 in lowercase?", string1.islower())


print("Is string2 in lowercase?", string2.islower())

Output

Is string1 in lowercase? True


Is string2 in lowercase? False

Python isupper()

It returns True if all the alphabets of a given string are in uppercase.


Otherwise, it returns False.

string1 = "CODESDOPE"
string2 = "CodesDope"

print("Is string1 in uppercase?", string1.isupper())


print("Is string2 in uppercase?", string2.isupper())

Output

Is string1 in uppercase? True


Is string2 in uppercase? False

Python istitle()

It returns True if the string is in titlecase. Otherwise, it returns False.

string1 = "Hey There World!"


string2 = "Hey there world!"

print("Is string1 in titlecase?", string1.istitle())


print("Is string2 in titlecase?", string2.istitle())

Output

Is string1 in titlecase? True


Is string2 in titlecase? False

Python Tuples

A tuple is similar to a list. The difference is that we cannot change the elements of a
tuple once assigned whereas we can change the elements of a list.

Creating a Tuple in Python


The items/elements in a tuple are enclosed within parentheses ( ) and separated by
commas.

a = ()

Output

a is an empty tuple having no item.

mytuple = (10, 30, 20, 40)


print(mytuple)

Output

(10, 30, 20, 40)

In the above example, mytuple is a tuple having three integers.

mytuple = (2, "Hello", [1, 2, 3], (100, 200), 2)


print(mytuple)

Output

(2, 'Hello', [1, 2, 3], (100, 200), 2)

mytuple is a tuple having five items of different data types. Notice that the third item
is a list having three items and the fourth item is another tuple having two items.

print(type(()))

Output

<class 'tuple'>

Here type(()) is showing the type as tuple. Hence ( ) is a tuple as we already know.

A tuple can also be created without parentheses.

mytuple = 10, "Hello", 20


print(mytuple)
print(type(mytuple))

Output
(10, 'Hello', 20)
<class 'tuple'>

In the above example, mytuple is a tuple having three elements.

Creating a Tuple With One Item

To create a tuple with a single item, we need to add a comma after the item. If a
comma is not added, then it is not considered a tuple.

var1 = ("Hello World",)


print(var1)
print(type(var1))

var2 = ("Hello World")


print(var2)
print(type(var2))

Output

('Hello World',)
<class 'tuple'>
Hello World
<class 'str'>

We can see that var1 is a tuple whereas var2 is not a tuple (because comma is not
added after the item). Look at another example.

var1 = (10,)
print(var1)
print(type(var1))

var2 = (10)
print(var2)
print(type(var2))

Output
(10,)
<class 'tuple'>
10
<class 'int'>

Here var1 is a tuple whereas var2 is not a tuple.

Accessing Elements from Tuple in Python

An element from a tuple can be accessed using its index.

Same as in lists and strings, every element in a tuple has a unique index based on
its position. Indices start from 0. Therefore, the first element of a tuple has index 0,
the second element has index 1, and so on.

To access any element of a tuple, we write name_of_tuple[index].

Therefore, the 2nd element of the tuple mytuple can be accessed by


writing mytuple[1] (because the index of the 2nd element is 1).

mytuple = (4, 9, 6, 2)
print(mytuple[0])
print(mytuple[1])
print(mytuple[2])
print(mytuple[3])

Output

4
9
6
2

Negative Index in Python Tuple


We can also access an element of a tuple by using a negative index just like in lists
and strings.

The last element of a tuple has index -1, second last element has index -2, and so
on.

mytuple = (4, 9, 6, 2)

#accessing using indices


print(mytuple[0])
print(mytuple[1])
print(mytuple[2])
print(mytuple[3])

# accessing using negative indices


print(mytuple[-1])
print(mytuple[-2])
print(mytuple[-3])
print(mytuple[-4])

Output

4
9
6
2
2
6
9
4

Here, mytuple[-1] returns the last element 2 of the list mytuple, mytuple[-2] returns
the second last element 6, and so on.

Extracting elements from nested tuples and strings can be done in the same way as
nested lists as shown in the following example.

mytuple = (4, [10, 20], (100, 200, 300), "Hello")


#accessing elements of nested list
print(mytuple[1][0]) # 10

#accessing elements of nested tuple


print(mytuple[2][1]) # 200

#accessing elements of nested string


print(mytuple[3][4]) # o

Output

10
200
o

In this example, mytuple is a tuple which consists of four elements - an integer, a list,
a tuple and a string. mytuple[1][0] prints the 1st element of the 2nd element of the
tuple. Similarly, the other values are printed.

Python Tuple Slicing

A range of elements from a tuple can be accessed using the slicing operator :.

To access elements from the ith index to the jth index of a tuple, we
write name_of_tuple[i:j+1].

mytuple = (1,2,3,4,5)
print(mytuple[1:4])

Output

[2, 3, 4]

mytuple = (1,2,3,4,5)

print(mytuple[1:4])

Output

[2, 3, 4]
mytuple = (1, 2, 3, 4, 5, 6)

# from 2nd element till 5th element


print(mytuple[1:5])

# from start till 5th element


print(mytuple[:5])

# from 5th element till end


print(mytuple[4:])

# from start till 3rd last element


print(mytuple[:-2])

# from 4th last element till 3rd last element


print(mytuple[-4:-2])

# from 3rd element till 3rd last element


print(mytuple[2:-2])

# all elements
print(mytuple[:])

Output

(2, 3, 4, 5)
(1, 2, 3, 4, 5)
(5, 6)
(1, 2, 3, 4)
(3, 4)
(3, 4)
(1, 2, 3, 4, 5, 6)

We can also give a step to the slice to skip some number of elements while slicing.

Look at the following example.

mytuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)


# from 2nd element till 9th element
print(mytuple[1:9])

# from 2nd element till 9th element in step 2


print(mytuple[1:9:2])

# from 2nd element till 9th element in step 3


print(mytuple[1:9:3])

Output

(2, 3, 4, 5, 6, 7, 8, 9)
(2, 4, 6, 8)
(2, 5, 8)

mytuple[1:9] returned the elements from the 1st index till the 8th index.

mytuple[1:9:2] also returned the elements from the 1st index till the 8th index, but it
returned every 2nd element after the element having the start index. Therefore, 3, 5
and 7 got skipped.

mytuple[1:9:3] also returned the elements from the 1st index till the 8th index, but it
returned every 3rd element after the element having the start index.

Giving a negative step returns the elements of a tuple in the reverse order. A step of
-1 returns the reversed tuple.

mytuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

# reversed tuple
print(mytuple[::-1])
# reversed tuple with step -2
print(mytuple[::-2])

Output

(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
(10, 8, 6, 4, 2)
Changing Elements of Python Tuple

Tuples are immutable, which means that their elements can’t be changed. Let’s try
this:

mytuple = (1,2,"Hello World!")


mytuple[1] = 5

Output

mytuple[1] = 5
TypeError: 'tuple' object does not support item assignment

This is the same error that we got while trying to change the elements of a string.
Since tuple is also not mutable like a string, so we can't change the elements of a
tuple.

Deleting Python Tuple

We cannot delete any item from a tuple because tuples are immutable. Instead, we
can delete an entire tuple using the del keyword.

mytuple = (1,2,"Hello World!")


del mytuple[1]

Output

Traceback (most recent call last):


File "<stdin>", line 1, in <module>
TypeError: 'tuple' object doesn't support item deletion

We tried to delete the 2nd element of a tuple and got an error.

mytuple = (1,2,"Hello World!")


del mytuple
print(mytuple)
Output

Traceback (most recent call last):


File "<stdin>", line 1, in <module>
NameError: name 'mytuple' is not defined

We deleted the entire tuple mytuple. Thus, we got an error on printing the tuple after
deleting it.

Python Tuple Operations

Tuple Concatenation (+)

Two or more tuples can be concatenated or combined using the + operator.

tuple1 = (1, 2, 3)
tuple2 = ('a', 'b', 'c', 'd')
tuple3 = tuple1 + tuple2
print(tuple3)

Output

(1, 2, 3, 'a', 'b', 'c', 'd')

tuple1 and tuple2 are concatenated to form tuple3.

Tuple Repetition (*)

A tuple can be repeated a specified number of times using the * operator.

tuple1 = (1, 2)
tuple2 = tuple1 * 3
print(tuple2)

Output
(1, 2, 1, 2, 1, 2)

tuple1 is repeated 3 times to form tuple2.

Python Tuple Membership Test

We can test if an element is present in a tuple using the membership


operators in and not in.

mytuple = (1, 2, "Blue", "Green")


print(2 in mytuple)
print(4 in mytuple)
print("Green" not in mytuple)

Output

True
False
False

Python Iteration Through a Tuple

Like lists, we can iterate through a tuple also using a for loop.

colors = ("Blue", "Green", "Red")


for color in colors:
print(color)

Output

Blue
Green
Red

A variable color goes to each element in the tuple colors and takes its value, and
that value is printed on the screen.
Different Ways to Create a Tuple in Pyhton

We already know how to create a tuple.

An empty tuple is created as follows.

mytuple = ()

A tuple having elements is created as follows.

mytuple = (1, 2, 3, 4)

It can also be created as follows.

mytuple = 1, 2, 3, 4

Tuples can also be created using the tuple() function. This function converts a
sequence (range, list, string, tuple) or a collection(dictionary, set) into a tuple.

An empty tuple can be created using tuple() as shown below.

mytuple = tuple()
print(mytuple)

Output

()

In the next example, a list and a string are converted to tuples.

tuple1 = tuple([1, 2, 3]) # list is converted to tuple


tuple2 = tuple("Codesdope") # string is converted to tuple
print(tuple1)
print(tuple2)

Output

(1, 2, 3)
('C', 'o', 'd', 'e', 's', 'd', 'o', 'p', 'e')

Note that all the characters of the string became the different elements of the tuple.
Built-in Functions in Python for Tuples

The functions available in Python which can be used with tuples are shown below.

len()

It returns the number of elements in a tuple.

mytuple = (1, 2, 3, 4, 5, 6)
print(len(mytuple))

Output

max()

It returns the element from a tuple that has the maximum value.

mytuple = (3, 2, 1, 5, 6, 4)
print(max(mytuple))

Output

min()

It returns the element from a tuple that has the minimum value

mytuple = (3, 2, 1, 5, 4)
print(min(mytuple))
Output

sum()

It returns the sum of all the elements in a tuple.

mytuple = (3, 2, 1, 5, 6, 4)
print(sum(mytuple))

Output

21

sorted()

It returns a sorted list from the tuple.

mytuple = (3, 2, 1, 5, 6, 4)
print(sorted(mytuple))

Output

[1, 2, 3, 4, 5, 6]

To sort the elements in descending order, pass another argument reverse=True to


the sorted() function.

mytuple = (3, 2, 1, 5, 6, 4)
print(sorted(mytuple, reverse=True))

Output

[6, 5, 4, 3, 2, 1]

any()
It returns True if any of the elements in a tuple are True. Otherwise, it returns False.

mytuple = (3, 2, 1, True)


print(any(mytuple))

Output

True

any(mytuple) returned True because all the elements of the tuple are non-zero or
True.

Look at another example where one element of the tuple is False.

mytuple = (3, 2, 1, False)


print(any(mytuple))

Output

True

In the above example, one element of the tuple is False, but still the any() function
returned True. This is because if at least one element is non-zero or True, then the
function returns True.

all()

It returns True if all the elements in a tuple are True. Otherwise, it returns False.

mytuple = (3, 2, 1, True)


print(all(mytuple))

Output

True

mytuple = (3, 2, 1, False)


print(all(mytuple))
Output

False

In this example, one element is False and so the all() function returned False.

Other Tuple Functions

We can use the index() and count() functions which we use with lists with tuples as
well.

languages = ('Python', 'C', 'Java', 'C++', 'Java')

print(languages.index('Java')) # printing index of first occurrence of 'Java'

print(languages.count('Java')) # printing count of 'Java'

Output

2
2

In the above example, 'Java' is present at the indices 2 and 4 in the


tuple languages. However, languages.index('Java') returned only the index 2 of its
first occurrence.

print(languages.count('Java')) returned the number of times the


element 'Java' is present in the tuple.

Python Dictionary

Dictionary is the most important data structure of Python. Like lists and tuples, a
dictionary is a collection of items, where each item is in the form of a key-value pair.

Let's take an example to explain it.


Suppose, the cost of a mango is 40
The cost of a banana is 10
The cost of cherry is 20

To store them, we can use a list or a tuple, but using a dictionary is another good
way to store all these in a better way.

Like, list is [],

tuple is (),

dictionary is {}.

Let's code using a dictionary.

fruit = {'mango':40, 'banana':10, 'cherry':20}

The above code shows that 'mango' is related to 40, 'banana' to 10 and 'cherry' to
20.

The dictionary fruit has three items - 'mango':40, 'banana':10, 'cherry':20.

In the first item, the key is mango and the value is 40. Similarly, there is a key-value
pair in the other two items as well.
Creating a Dictionary in Python

As we just saw, the items/elements in a dictionary are enclosed within braces {


} and separated by commas.

a = {}
Output

a is an empty dictionary having no item.

fruit = {'mango': 40, 'banana': 10, 'cherry': 20}


print(fruit)

Output

{'mango': 40, 'banana': 10, 'cherry': 20}

fruit is the same dictionary containing three items we saw in the beginning of this
chapter. Here, mango, banana and cherry are the keys, and 40, 10 and 20 are values.
Therefore, each of the three items contain a key-value pair.

Let's see one more example of a dictionary containing items of different types.

mydict = {'x':[1,2,3,4], 'y':"Hello World", 'z':4.0}


print(mydict)

Output

{'x': [1, 2, 3, 4], 'y': 'Hello World', 'z': 4.0}

There is another way to create dictionaries.

# declaring an empty dictionary


fruit = {}

# adding key-value pairs to dictionary


fruit['mango'] = 40
fruit['banana'] = 10
fruit['cherry'] = 20

print(fruit)

Output

{'mango': 40, 'banana': 10, 'cherry': 20}

In this example, we first declared an empty dictionary fruit. After that, we added all
the items one by one. For example, we added an item having 'mango' as key
and 40 as value by writing fruit['mango'] = 40.
We can also get all keys and values at one go by using the keys() and values()
functions respectively. keys() and values() give us all keys and values of a dictionary
respectively. Let's see an example of these functions:

fruit = {'mango':40, 'banana':10, 'cherry':20}


print(list(fruit.keys()))
print(list(fruit.values()))

Output

['mango', 'banana', 'cherry']


[40, 10, 20]

list(fruit.keys()) returned a list of all the keys


and list(fruit.values()) returned a list of all the values of the dictionary fruit.

Rules for Creating Python Dictionary

There are some rules which must be followed while creating dictionaries.

Keys must be unique.


A key can’t be present more than once in a dictionary. However, if we give the same
key more than once, it will take the value assigned to it in the last element containing
the key.

fruit = {'mango':40,'banana':10,'cherry':20, 'mango':60}


print(fruit)

Output

{'mango': 60, 'banana': 10, 'cherry': 20}

In the above example, we gave the key 'mango' twice. So, the key 'mango' took the
last assigned value (60).

However, there is no such boundation on values. The same value can be assigned
to any number of keys.

fruit = {'mango':40,'banana':20,'cherry':20}
print(fruit)

Output

{'mango': 40, 'banana': 20, 'cherry': 20}

Here, the value 20 is assigned to two keys.

Keys should be some immutable data type.


As already mentioned, data types whose elements can’t be modified, added or
deleted are called immutable. int, float, bool, string, tuple are immutable data types.
list, dictionary and set are mutable data types.

A key can be an immutable data type only. However, values can be both immutable
and mutable data types.

fruit = {'mango':40, 'banana':20, [1, 2]:20}


print(fruit)

Output

Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError:
unhashable type: 'list'

Here, we gave a list as the key of the third element which resulted in an error.

Accessing Elements from Python Dictionary

We use index to extract value from other data types like lists. In a dictionary, we
access values by using keys.

There are two ways to extract values using keys in a dictionary - using brackets [
] or get() function.

In the first method, to access any value of a dictionary, we


write name_of_dictionary[key].

fruit = {'mango':40,'banana':10}
print(fruit['mango'])
print(fruit['banana'])

Output

40
10

fruit['mango'] returned the value of the key 'mango' of the dictionary fruit.

In the second method, a value of a dictionary is accessed by


writing name_of_dictionary.get(key).

fruit = {'mango':40,'banana':10}
print(fruit.get('mango'))
print(fruit.get('banana'))

Output

40
10

fruit.get('mango') returned the value of the key 'mango' of the dictionary fruit.

In the second method, a value of a dictionary is accessed by writing


name_of_dictionary.get(key).

If you try to access a value using a key which doesn’t exist in the dictionary, then
accessing with brackets [ ] returns an error whereas accessing with get() simply
returns None.

fruit = {'mango':40,'banana':10}
print(fruit.get('cherry')) # returns None
print(fruit['cherry']) # throws an error

Output

Traceback (most recent call last): File "<stdin>", line 3, in <module> KeyError: 'cherry'

Here the key ‘cherry’ is not present in the dictionary fruit.


Thus fruit.get(‘cherry’’) returned None and fruit[‘cherry’] gave us an error as
stated above.
We can also pass a value in get function. In that case, if the key is not present in the
dictionary, that value will be returned instead of None. Let’s look at an example.

fruit = {"mango": 40, "banana": 10}


print(fruit.get("cherry", 50))

Output

50

Since the key 'cherry' is not present in the dictionary fruit, fruit.get("cherry",
50) returned default value of 50.

Changing Elements of Python Dictionary

Dictionaries are mutable, which means that their elements can be changed. The
value of a key present in a dictionary can be changed by assigning the new value.

mydict = {'name': 'John', 'age': 45}


mydict['age'] = 50 # changing value
print(mydict) # printing changed dictionary

Output

{'name': 'John', 'age': 50}

The value of the key 'age' is changed by simply assigning the new value
to mydict['age'].

Another way to do this is by using the update() function.

mydict = {'name': 'John', 'age': 45}


mydict.update(age = 50) # changing value
print(mydict) # printing changed dictionary

Output

{'name': 'John', 'age': 50}

mydict.update(age = 50) changed the value of the key 'age' to 50.


Adding Elements to Python Dictionary

New elements can be added to a dictionary in the same way they are modified.

The first way to add a new element is to assign the value to the key as shown in the
following example.

mydict = {'name': 'John', 'age': 45}


mydict['gender'] = 'male' # adding a new key-value pair
print(mydict) # printing changed dictionary

Output

{'name': 'John', 'age': 45, 'gender': 'male'}

mydict['gender'] = 'male' appended a new element with 'gender' as key


and 'male' as value to the dictionary.

The second way is to append using the update() function.

mydict = {'name': 'John', 'age': 45}


mydict.update(gender = 'male') # adding a new key-value pair
print(mydict) # printing changed dictionary

Output

{'name': 'John', 'age': 45, 'gender': 'male'}

Deleting Elements from Python Dictionary

Python del

The del keyword can be used to delete a single element or the entire list.

mydict = {'name': 'John', 'age': 45, 'gender': 'male'}


del mydict['age'] # deleting element with key 'age'
print(mydict) # printing changed dictionary

Output

{'name': 'John', 'gender': 'male'}

In the above code, the element having the key 'age' is deleted from the dictionary.

del can also be used to delete an entire dictionary.

mydict = {'name': 'John', 'age': 45, 'gender': 'male'}


del mydict # deleting dictionary
print(mydict) # printing dictionary after deleting it

Output

Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name
'mydict' is not defined

In the above code, the entire dictionary colors is deleted. Thus, we got an error on
printing the dictionary after deleting it.

Python pop()

The pop() function removes an element having the specified key and returns the
value of the removed element.

mydict = {'name': 'John', 'age': 45, 'gender': 'male'}


print(mydict.pop('age')) # removing element with key 'age' and printing its value
print(mydict) # printing updated dictionary

Output

45
{'name': 'John', 'gender': 'male'}

mydict.pop('age') removed the element having the key 'age' from the dictionary
and returned the value of that element. Therefore, on printing the popped value, 45
got printed.

If we don’t pass any key to the pop() function, we will get an error.
mydict = {'name': 'John', 'age': 45, 'gender': 'male'}
print(mydict.pop())

Output

Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: pop
expected at least 1 argument, got 0

Python popitem()

The popitem() function removes the last element from a dictionary and returns the
(key, value) pair of the removed element.

mydict = {'name': 'John', 'age': 45, 'gender': 'male'}


print(mydict.popitem()) # removing the last element and printing its key-value pa
ir
print(mydict) # printing updated dictionary

Output

('gender', 'male')
{'name': 'John', 'age': 45}

mydict.popitem() removed the last element from the dictionary and returned its key-
value pair ('gender', 'male').

Before Python 3.7, popitem() removed an arbitrary element from a dictionary, instead
of removing the last element

Python clear()

The clear() function removes all the elements from a dictionary.

mydict = {'name': 'John', 'age': 45, 'gender': 'male'}


mydict.clear() # removing all element from dictionary
print(mydict) # printing updated dictionary
Output

{}

Python Dictionary Operations

Dictionary Membership Test

We can test if a key is present in a list using the membership operators in and not
in.

In a dictionary, the membership test is applicable only for keys, not values.

mydict = {1: 1, 2: 8, 3: 27, 4: 64}


print(2 in mydict)
print(5 in mydict)
print(4 not in mydict)

Output

True
False
False

In the above example, 2 and 4 are the keys whereas 5 is not a key in the dictionary.

As already mentioned, the membership test is not applicable for values. Therefore, in
the following example, the membership test returned False even when the value is
present in the dictionary.

mydict = {1: 1, 2: 8, 3: 27, 4: 64}


print(8 in mydict) # 8 is not a key

Output

False
Iteration Through a Python Dictionary

The different ways to iterate through a dictionary are discussed below.

Looping Through Keys

We can iterate through the keys of a dictionary using a for loop. Since we can
iterate over keys, we can use those keys to get values also. Let’s look at an
example.

mydict = {'name': 'John', 'age': 45, 'gender': 'male'}


for k in mydict:
print("key =", k)
print("value =", mydict[k])

Output

key = name
value = John
key = age
value = 45
key = gender
value = male

A variable k goes to each element in the dictionary mydict and takes its key.
Therefore, in the first iteration, k is 'name', in the second iteration it is 'age' and so
on.

Looping Through Values

We can also iterate through the values of a dictionary using the values() function.
We can use this function if we want to access just the values.
mydict = {'name': 'John', 'age': 45, 'gender': 'male'}
for v in mydict.values():
print("value =", v)

Output

value = John
value = 45
value = male

Note that we are iterating through mydict.values() instead of mydict. The


variable v goes to each element in the dictionary mydict and takes its value.

Looping Through Keys and Values

We can iterate through both keys and values of a dictionary using


the items() function. We can use this function if we want to access just the values.

mydict = {'name': 'John', 'age': 45, 'gender': 'male'}


for k, v in mydict.items():
print("key =", k)
print("value =", v)

Output

key = name
value = John
key = age
value = 45
key = gender
value = male

In each iteration, k takes the key and v takes the value.

Different Ways to Create a Python Dictionary


We already know how to create a dictionary.

An empty dictionary is created as follows.

mydict = {}

A dictionary having elements is created as follows.

mydict = {'name': 'John', 'age': 45, 'gender': 'male'}

It can also be created as follows.

mydict = {}

mydict['name'] = 'John'

mydict['age'] = 45

mydict['gender'] = 'male'

Dictionaries can also be created using the dict() function.

An empty dictionary can be created using dict() as shown below.

mydict = {}
print(mydict)

Output

{}

A dictionary having elements can also be created by passing the key-value pairs to
dict() as shown in the following example.

mydict = dict(name = 'John', age = 45, gender = 'male')


print(mydict)

Output

{'name': 'John', 'age': 45, 'gender': 'male'}

Python Copying Dictionary

Let’s look at the different ways we can copy a dictionary.


Simple Assignment

A dictionary can be copied to a variable by simply assigning it to the variable.

dict1 = {'a': 20, 'b': 10, 'c': [1, 2, 3]}


dict2 = dict1
print(dict2)

Output

{'a': 20, 'b': 10, 'c': [1, 2, 3]}

Here, the dictionary stored in the variable dict1 is assigned to the variable dict2.
This results in both the variables storing the same dictionary. As a result, if some
change is made in dict2, it will also get reflected in dict1, which is often
undesirable.

Let’s see how a change in dict2 is getting reflected in dict1.

dict1 = {'a': 20, 'b': 10, 'c': [1, 2, 3]}


dict2 = dict1
dict2['b'] = 30
dict2['c'] = [1, 2, 3, 4]
print("Original dictionary:", dict1)
print("New dictionary:", dict2)

Output

Original dictionary: {'a': 20, 'b': 30, 'c': [1, 2, 3, 4]}


New dictionary: {'a': 20, 'b': 30, 'c': [1, 2, 3, 4]}

After assigning dict1 to dict2, the values assigned to the keys 'b' and 'c' are
changed. The same changes got reflected in dict1 also. This is because both the
variables are pointing to the memory location of the same dictionary.

To prevent this, we can use the copy() function.

Python copy()
copy() is a function in Python which creates a shallow copy of a dictionary.

dict1 = {'a': 20, 'b': 10, 'c': [1, 2, 3]}


dict2 = dict1.copy()
dict2['b'] = 30
dict2['c'] = [1, 2, 3, 4]
print("Original dictionary:", dict1)
print("New dictionary:", dict2)

Output

Original dictionary: {'a': 20, 'b': 10, 'c': [1, 2, 3]}


New dictionary: {'a': 20, 'b': 30, 'c': [1, 2, 3, 4]}

In this example, a shallow copy of dict1 is created using copy() and that copy is
assigned to dict2. As a result, modifying dict2 didn’t modify dict1 because now the
variables are pointing to the memory addresses of different dictionaries.

Python dict()

A dictionary can also be copied by passing it to the dict() function.

dict1 = {'a': 20, 'b': 10, 'c': [1, 2, 3]}


dict2 = dict(dict1)
dict2['b'] = 30
dict2['c'] = [1, 2, 3, 4]
print("Original dictionary:", dict1)
print("New dictionary:", dict2)

Output

Original dictionary: {'a': 20, 'b': 10, 'c': [1, 2, 3]}


New dictionary: {'a': 20, 'b': 30, 'c': [1, 2, 3, 4]}

We created a new dictionary having all the elements of dict1 using dict(dict1) and
assigned this newly created dictionary to dict2.
Built-in Functions in Python for Dictionary

The functions available in Python which can be used with dictionaries are shown
below.

len()

It returns the number of elements in a dictionary.

mydict = {'name': 'John', 'age': 45, 'gender': 'male'}


print(len(mydict))

Output

sorted()

It returns a list of the keys of a dictionary in sorted order.

mydict = {'name': 'John', 'age': 45, 'gender': 'male'}


print(sorted(mydict))

Output

['age', 'gender', 'name']

Here a sorted list of the keys of the dictionary is returned..

Look at another example in which the keys are numeric.

mydict = {2: 'John', 1: 45, 3: 'male'}

Output

[1, 2, 3]
To sort the keys in descending order, pass another argument reverse=True to
the sorted function.

mydict = {2: 'John', 1: 45, 3: 'male'}


print(sorted(mydict, reverse=True))

Output

[3, 2, 1]

any()

It returns True if any of the keys in a dictionary are True. Otherwise, it returns False.

mydict = {1: 1, 'a': 2, True: 3}


print(any(mydict))

Output

True

any(mydict) returned True because all the keys of the dictionary mydict are non-
zero or True.

Look at another example where one key of the dictionary is False.

mydict = {1: 1, 'a': 2, False: 3}


print(any(mydict))

Output

True

We have one key of the dictionary False, but still the any() function returned True.
This is because if at least one key is non-zero or True, then the function returns
True.

Now consider the following example.

mydict = {'': 1, 0: 2, False: 3}


print(any(mydict))
Output

False

The function returned False because the first key is a null string, the second key is 0
and the third key is False.

all()

It returns True if all the keys in a dictionary are True. Otherwise, it returns False.

mydict = {1: 1, 'a': 2, True: 3}


print(all(mydict))

Output

True

mydict = {1: 1, 'a': 2, False: 3}


print(all(mydict))

Output

False

In this example, one key is False and so the all() function returned False.

Other Dictionary Functions in Python

We have already looked at some of the dictionary functions


like get(), update(), pop(), popitem(), clear() and copy(). Other useful functions
are shown below.

Python keys()

It returns an object which contains the keys of a dictionary in the form of a list.
mydict = {'name': 'John', 'age': 45, 'gender': 'male'}
print(mydict.keys())

Output

dict_keys(['name', 'age', 'gender'])

Python values()

It returns an object which contains the values of a dictionary in the form of a list.

mydict = {'name': 'John', 'age': 45, 'gender': 'male'}


print(mydict.values())

Output

dict_values(['John', 45, 'male'])

items()

It returns an object which contains the key-value pairs of a dictionary in the form of a
list.

mydict = {'name': 'John', 'age': 45, 'gender': 'male'}


print(mydict.items())

Output

dict_items([('name', 'John'), ('age', 45), ('gender', 'male')])

fromkeys()

It creates and returns a dictionary with the specified keys and the specified value.
Using this function, all the keys are assigned the same value.
mykeys = {'a', 'b', 'c', 'd'} # defining keys
myvalue = 10 # defining value
mydict = dict.fromkeys(mykeys, myvalue) # creating dictionary
print(mydict)

Output

{'c': 10, 'd': 10, 'a': 10, 'b': 10}

In this example, a sequence of keys is assigned to the variable mykeys and the value
is assigned to the variable myvalue. A dictionary is created with the keys stored
in mykeys and the value stored in myvalue assigned to each by
writing dict.fromkeys(mykeys, myvalue).

Python Dictionary
Comprehension

Like list comprehension, we can use dictionary comprehension for creating


dictionaries which are otherwise created using a for loop. Dictionary comprehension
makes creating dictionaries easier and more readable.

So, let’s see how to create dictionaries using dictionary comprehension.

Using Dictionary Comprehension

Let’s take an example which stores the cube of all numbers from 1 to 10 in a
dictionary using a for loop.

cubes = {}

for num in range(1, 11):


cubes[num] = num**3
print(cubes)

Output

{1: 1, 2: 8, 3: 27, 4: 64, 5: 125, 6: 216, 7: 343, 8: 512, 9: 729, 10: 1000}

In the dictionary cubes, the keys are the numbers from 1 to 10 and the values are the
cubes of the corresponding keys.

We created an empty dictionary cubes. Then we iterate over range(1, 11) using
a for loop. In each iteration, the key is assigned the number and the cube of the
number is calculated and the result is assigned to the value.

The dictionary cubes can be created using dictionary comprehension as follows.

cubes = {num: num**3 for num in range(1, 11)}


print(cubes)

Output

{1: 1, 2: 8, 3: 27, 4: 64, 5: 125, 6: 216, 7: 343, 8: 512, 9: 729, 10: 1000}

On looking at [num: num**3 for num in range(1, 11)] , you will find a for loop
definition - for num in range(1, 11).

In for num in range(1, 11), the variable num iterates over range(1, 11).

In each iteration, an element with the key equal to num and the value equal to
the num**3 is added to a dictionary and that dictionary is returned at the end of the
loop.

In the first iteration, num is 1. Thus, the key is 1 and the value is also 1 (1**3 = 1).

In the second iteration, num is 2. Thus, the key is 2 and the value is also 8 (2**3 =
8).

This goes on till all the ten iterations. Finally, the dictionary created is assigned to the
variable cubes.

So we have just created a dictionary using dictionary comprehension. Now let’s look
at the general syntax of dictionary comprehension.

Python Dictionary Comprehension Syntax


new_dictionary = {key: value for_loop}

The dictionary returned by dictionary comprehension method is enclosed within


braces { }. On each iteration of the for_loop, an element is added to the dictionary
with key as the key and value as the value.

Comparing this syntax to the last example, num is key, num**3 is value and for num
in range(1, 11) is for_loop.

Python Dictionary Comprehension Examples


Let’s look at an example in which we will create a dictionary from the elements of
another dictionary.

fruits = {'mango': 40, 'banana': 10, 'cherry': 20}


new_fruits = {}

for fruit, price in fruits.items():


new_fruits[fruit] = price * 2

print(new_fruits)

Output

{'mango': 80, 'banana': 20, 'cherry': 40}

We have a dictionary fruits storing three fruits and their prices. We are creating
another dictionary new_fruits storing the same fruits with double their prices. For
that, we iterate through the items of the fruits dictionary using items() and append a
new item to the new_fruits dictionary in each iteration.

The dictionary new_fruits can be created using dictionary comprehension as


follows.

fruits = {'mango': 40, 'banana': 10, 'cherry': 20}


new_fruits = {fruit: price*2 for (fruit, price) in fruits.items()}
print(new_fruits)

Output

{'mango': 80, 'banana': 20, 'cherry': 40}

In the above program, in each iteration of the loop for (fruit, price) in
fruits.items(), an element with key as fruit and value as price*2 is added to a
dictionary and that dictionary is retired at the end of the loop.

Comparing this code to the syntax, fruit is key, price*2 is value and for (fruit,
price) in fruits.items() is for_loop.

Using Dictionary Comprehension with


Conditional Statements
Let’s see how to implement dictionary comprehension when an if statement is used
in the body of for loop.
Syntax

new_dictionary = {key: value for_loop if condition}

On each iteration of the for_loop, the if condition is checked. If the condition is


True, then only the element is added to the dictionary.
Examples

Suppose in the last example, we want to create a new dictionary new_fruits only for
those fruits whose price is greater than 15. This is done using a for loop in the
following example.

fruits = {'mango': 40, 'banana': 10, 'cherry': 20}


new_fruits = {}

for fruit, price in fruits.items():


if price > 15:
new_fruits[fruit] = price * 2

Output

{'mango': 80, 'cherry': 40}

Let’s create this dictionary using dictionary comprehension.

fruits = {'mango': 40, 'banana': 10, 'cherry': 20}


new_fruits = {fruit: price*2 for (fruit, price) in fruits.items() if price > 15}
print(new_fruits)

Output

{'mango': 80, 'cherry': 40}

Comparing this code to the syntax, fruit is key, price*2 is value, for (fruit,
price) in fruits.items() is for_loop and if price > 15 is if condition.

Python if...else With Dictionary Comprehension


Like in list comprehension, using if...else in dictionary comprehension has a slightly
different syntax than using just if. Let’s look at the two syntaxes.
Syntax of Dictionary Comprehension including if statement

new_list = {key: value for_loop if condition}

We have already seen examples where if statements were used.


Syntax of Dictionary Comprehension including if..else statement

new_list = {key: (value1 if condition else value2) for_loop}

Here, if the condition of if is True, then value1 is evaluated and assigned to key,
else value2 is evaluated and assigned to key. Rest everything is the same.

Let’s see how if..else is implemented in dictionary comprehension.

Take an example in which we have a dictionary containing the names of students


along with their marks. The names are the keys and the marks are the values. We
have to create another dictionary with the names as the keys and ‘Pass’ or ‘Fail’ as
the values depending on whether the student passed or failed, assuming the passing
marks are 40.

result = {"John": 45, "Mark": 70, "Jack": 32, "Devin": 65}


new_result = {}

for name, marks in result.items():


if marks >= 40:
new_result[name] = "Pass"
&nbspelse:
new_result[name] = "Fail"

print(new_result)

Output

{'John': 'Pass', 'Mark': 'Pass', 'Jack': 'Fail', 'Devin': 'Pass'}


We iterate through the items of the result dictionary using items(). In each
iteration, we check if the value (marks) is greater than or equal to 40. If it is, then we
append a new element to the new dictionary new_result with the same key and the
value "Pass", otherwise we append the element with the value "Fail".

This can be created using dictionary comprehension as follows.

result = {"John": 45, "Mark": 70, "Jack": 32, "Devin": 65}


new_result = {name: ("Pass" if marks >= 40 else "Fail") for (name, marks) in resul
t.items()}
print(new_result)

Output

{'John': 'Pass', 'Mark': 'Pass', 'Jack': 'Fail', 'Devin': 'Pass'}

name is key, "Pass" is value1, "Fail" is value2, marks >= 40 is condition and for
(name, marks) in result.items() is for_loop. In each iteration of for_loop,
if condition is True, then value1 is assigned to the key, else value2 is assigned to
the key.

When to use Dictionary Comprehension

Dictionary comprehension makes creating dictionaries faster and more readable.


However, it should not be used every time to create a dictionary. If the logic is long
or complex, then using dictionary comprehension is a bad choice. This is
because it can become less readable and you won’t be able add comments, thus
making it difficult to debug as well.

Therefore, use dictionary comprehension if the logic is simple and small and
use for loop otherwise

Python Sets
Set is a collection of items. Apart from sets, three other data types used as collection
of data are List, Tuple and Dictionary. We have already learnt about the other three
and will go through sets in this chapter.

Why Sets?

Just like lists, tuples and dictionaries, sets are also used to store data. So, why a
new data type? What makes sets different from lists and tuples is that a set can’t
store duplicate data. For example, a list can have an element more than once,
whereas a set can have an element only once.

A set in Python is similar to the set about which we talk in our day-to-day life. For
example, a set of books, a set of movies, a set of students. We know that a set of
students can’t have a student more than once. Thus, a set of students consists of the
names of unique students. Similarly, a set of movies consists of the names of unique
movies.

Therefore, sets are used when we want to store unique data.

Creating a Python Set

The items/elements in a set are enclosed within braces { } and separated by


commas. The elements can be of the same or different data types.

myset = {1, 2, 3}
print(myset)

Output

{1, 2, 3}

In the above example, myset is a set having three elements of type integer.

myset = {1, 2, 3, "Hello"}


print(myset)
Output

{1, 2, 3, 'Hello'}

In the above example, myset is a set having elements of different data types.

An empty set is defined using the set() function as shown below.

myset = set()
print(myset)

Output

set()

We can’t use { } to create an empty set because it creates an empty dictionary.

Properties of Python Set

Each element in a set is unique


There can’t be duplicates of any element present in a set.

myset = {1, 2, 3, 2}
print(myset)

Output

{1, 2, 3}

We assigned the element 2 twice in the set but it got discarded.

A set is unordered
Elements in a set don’t have a specific order. Their order can get changed every
time you use them.

Consider the following code.

myset = {1, 2, 3, "1", "Hello"}


print(myset)
On running it, we got a different result each time. For example, we got the following
result on running it the first time.

{1, 2, 3, 'Hello', '1'}

On running the second time, we got the following result.

{'Hello', 1, 2, 3, '1'}

This is also the reason why we can’t access elements of a set through index or
position.

The elements of a set can’t be accessed through index or position.

Elements of a set are immutable


If a data type is immutable, it means that its elements can’t be added or deleted.
Examples of immutable data types are int, float, bool, string and tuple. Examples of
mutable data types are list, dictionary and set.

Therefore, lists, dictionaries or sets can’t be elements of a set.

The following set is valid.

myset = {1, 2, 3.2, "1", "Hello", (10, 20)}


print(myset)

Output

{1, 2, 3.2, 'Hello', '1', (10, 20)}

The following set is invalid because it contains a list which is mutable.

myset = {1, 2, 3.2, "1", "Hello", [10, 20]}


print(myset)

Output

Traceback (most recent call last):


File "<stdin>", line 2, in <module>
TypeError: unhashable type: 'list'

The elements of a set can’t be changed, but new elements can be added.

Sets are mutable


The elements of a set can be added or deleted.

Adding Elements to Python Set

We can add new elements to a set using the following functions.

Python add()

The add() function adds a single element to a set.

myset = {1, 2, 3}
myset.add(5)
print(myset)

Output

{1, 2, 3, 5}

myset.add(5) added 5 to the set myset.

Python update()

The update() function takes lists, strings, tuples and other sets as an argument and
adds the elements in the passed arguments to the set.

myset = {1, 2, 3}
myset.update([5, 10])
print(myset)

Output

{1, 2, 3, 5, 10}

In the above example, a list [5, 10] is passed to the update() function. Thus, the
elements of the list got appended to the set.
myset = {1, 2, 3}
myset.update([5, 10], [15, 20], {25, 30})
print(myset)

Output

{1, 2, 3, 5, 10, 15, 20, 25, 30}

Here, since two lists and a set are passed to the update() function, their elements
got added to the set myset.

Deleting Elements from Python Set

Python discard()

The discard() function is used to remove a particular element from a set.

colors = {"Blue", "Green", "Red", "Orange", "Yellow", "Brown"}


colors.discard("Green") # removing "Green" from set
print(colors)

Output

{'Yellow', 'Orange', 'Blue', 'Brown', 'Red'}

colors.discard("Green") removed "Green" from the set colors.

If we try to remove an element which is not present in the set, discard() doesn’t
throw an error.

colors = {"Blue", "Green", "Red", "Orange", "Yellow", "Brown"}


colors.discard("Violet")
print(colors)

Output

{'Green', 'Yellow', 'Red', 'Brown', 'Orange', 'Blue'}


"Violet" is not present in the set, still trying to remove it didn’t throw any error.

Python remove()

The remove() function is used to remove a particular element from a set. It differs
from discard() as it throws the “KeyError” if we try to remove an element which is
not present in the set.

colors = {"Blue", "Green", "Red", "Orange", "Yellow", "Brown"}


colors.remove("Green") # removing "Green" from set
print(colors)

Output

{'Yellow', 'Orange', 'Blue', 'Brown', 'Red'}

colors.remove("Green") removed "Green" from the set.

If we try to remove an element which is not present in the set, remove() throws the
“KeyError” as shown below.

colors = {"Blue", "Green", "Red", "Orange", "Yellow", "Brown"}


colors.remove("Violet")

Output

Traceback (most recent call last): File "<stdin>", line 2, in <module> KeyError: 'Violet'

Python pop()

For sets, the pop() function behaves differently because we can’t remove an
element by index. This function removes an element randomly and returns the
removed element.

colors = {"Blue", "Green", "Red", "Orange", "Yellow", "Brown"}


colors.pop()
print(colors)
Output

{'Orange', 'Green', 'Red', 'Brown', 'Blue'}

In the above example, pop() selected a random element and removed it from the
set.

Python clear()

The clear() function removes all the elements from a set.

colors = {"Blue", "Green", "Red", "Orange", "Yellow", "Brown"}


colors.clear() # removing all element from set
print(colors)

Output

set()

Python Set Operations

Some mathematical operations like union, intersection, difference and symmetric


difference on sets can be implemented on Python sets as well. For that, consider the
two sets s1 and s2 on which we will be performing the operations.

s1 = {1, 2, 3, 4, 5, 6}
s2 = {4, 5, 6, 7, 8}

Python Set Union

Union of sets s1 and s2 is a new set having all the elements of both the sets.
Union can be performed using the | operator.

s1 = {1, 2, 3, 4, 5, 6}
s2 = {4, 5, 6, 7, 8}
print(s1 | s2)

Output

{1, 2, 3, 4, 5, 6, 7, 8}

Union can also be performed using the union() function.

s1 = {1, 2, 3, 4, 5, 6}
s2 = {4, 5, 6, 7, 8}

print(s1.union(s2))
print(s2.union(s1))

Output

{1, 2, 3, 4, 5, 6, 7, 8}
{1, 2, 3, 4, 5, 6, 7, 8}

Python Set Intersection

Intersection of sets s1 and s2 is a new set having the common elements of both the
sets.
Intersection can be performed using the & operator.

s1 = {1, 2, 3, 4, 5, 6}
s2 = {4, 5, 6, 7, 8}

print(s1 & s2)

Output

{4, 5, 6}

4, 5 and 6 are present in both the sets and hence constitutes the intersection set s1
& s2.

Intersection can also be performed using the intersection() function.

s1 = {1, 2, 3, 4, 5, 6}
s2 = {4, 5, 6, 7, 8}

print(s1.intersection(s2))
print(s2.intersection(s1))

Output

{4, 5, 6}
{4, 5, 6}

Python Set Difference

Difference of set s1 from set s2 is a new set having the elements which are present
in s1 and not in s2.
Difference can be performed using the - operator.

s1 = {1, 2, 3, 4, 5, 6}
s2 = {4, 5, 6, 7, 8}

print(s1 - s2)
print(s2 - s1)

Output

{1, 2, 3}
{8, 7}

Set s1 - s2 consists of the elements present in s1 but not in s2, and set s2 -
s1 consists of the elements present in s2 but not in s1.

Difference can also be performed using the difference() function.

s1 = {1, 2, 3, 4, 5, 6}
s2 = {4, 5, 6, 7, 8}

print(s1.difference(s2))
print(s2.difference(s1))

Output

{1, 2, 3}
{8, 7}

s1.difference(s2) is equivalent to s1 - s2 and s2.difference(s1) is equivalent


to s2 - s1.

Python Set Symmetric Difference

Symmetric Difference of sets s1 and s2 is a new set having the elements in s1 and
s2 but not the common elements in both. It can be seen as (union - intersection).
Difference can be performed using the ^ operator.
s1 = {1, 2, 3, 4, 5, 6}
s2 = {4, 5, 6, 7, 8}

print(s1 ^ s2)

Output

{1, 2, 3, 7, 8}

Symmetric difference can also be performed using


the symmetric_difference() function.

s1 = {1, 2, 3, 4, 5, 6}
s2 = {4, 5, 6, 7, 8}

print(s1.symmetric_difference(s2))
print(s2.symmetric_difference(s1))

Output

{1, 2, 3, 7, 8}
{1, 2, 3, 7, 8}

Python Set Membership Test

We can test if an element is present in a set using the membership


operators in and not in.

myset = {1, 2, "Blue", "Green"}


print(2 in myset)
print(4 in myset)
print("Green" not in myset)

Output

True
False
False
Iteration Through a Python Set

We can iterate through a set using a for loop.

colors = {"Blue", "Green", "Red"}


for color in colors:
print(color)

Output

Blue
Green
Red

A variable color goes to each element in the set colors and takes its value, and that
value is printed on the screen.

Different Ways to Create a Python Set

We already know how to create a set.

An empty set is created as follows.

myset = set()

A set having elements is created as follows.

myset = {1, 2, 3, 4}

Sets can also be created using the set() function. This function converts a
sequence (list, string, tuple) or a collection(dictionary, set) into a set.

In the following example, a list, a string and a set are converted to sets.

set1 = set([1, 2, 3]) # list is converted to set


set2 = set("Codesdope") # string is converted to set
set3 = set((1, 2, 3, 4)) # set is converted to set
print(set1)
print(set2)
print(set3)

Output

{1, 2, 3}
{'o', 'e', 'C', 'p', 's', 'd'}
{1, 2, 3, 4}

Python Copying Set

Let’s look at the different ways we can copy a set.

Simple Assignment

A set can be copied to a variable by simply assigning it to the variable.

set1 = {1, 2, 3, 4}
set2 = set1
print(set2)

Output

{1, 2, 3, 4}

Here, the set stored in the variable set1 is assigned to the variable set2. Thus, both
the variables store the same set. As a result, if some change is made in set2, it will
also get reflected in set1, which is often undesirable.

Let’s see how a change in set2 is getting reflected in set1.

set1 = {1, 2, 3, 4}
set2 = set1
set2.add(10)
print("Original set:", set1)
print("New set:", set2)
Output

Original set: {1, 2, 3, 4, 10}


New set: {1, 2, 3, 4, 10}

On changing set2, set1 also got changed. This is because both the variables are
pointing to the memory location of the same set.

To prevent this, we can use the copy() function.

Python copy()

copy() is used to create a shallow copy of a set.

set1 = {1, 2, 3, 4}
set2 = set1.copy()
set2.add(10)
print("Original set:", set1)
print("New set:", set2)

Output

Original set: {1, 2, 3, 4}


New set: {1, 2, 3, 4, 10}

In this example, a shallow copy of set1 is created using copy() and that copy is
assigned to set2. As a result, modifying set2 didn’t modify set1 because now the
variables are pointing to the memory addresses of different sets.

Python set()

A set can be copied by passing it to the set() function.

set1 = {1, 2, 3, 4}
set2 = set(set1)
set2.add(10)
print("Original set:", set1)
print("New set:", set2)

Output

Original set: {1, 2, 3, 4}


New set: {1, 2, 3, 4, 10}

We created a new set having all the elements of set1 using set(set1) and assigned
this newly created set to set2.

Built-in Functions in Python for Set

The functions available in Python which can be used with sets are shown below.

len()

It returns the number of elements in a set.

myset = {1, 2, 3, 4, 5, 6}
print(len(myset))

Output

max()

It returns the element from a set that has the maximum value.

myset = {3, 2, 1, 5, 6, 4}
print(max(myset))

Output

6
min()

It returns the element from a set that has the minimum value.

myset = {3, 2, 1, 5, 4}
print(min(myset))

Output

sum()

It returns the sum of all the elements in a set.

myset = {3, 2, 1, 5, 6, 4}
print(sum(myset))

Output

21

sorted()

It returns a sorted list from the set.

myset = {3, 2, 1, 5, 6, 4}
print(sorted(myset))

Output

[1, 2, 3, 4, 5, 6]

To sort the elements in descending order, pass another argument reverse=True to


the sorted() function.
myset = {3, 2, 1, 5, 6, 4}
print(sorted(myset, reverse=True))

Output

[6, 5, 4, 3, 2, 1]

any()

It returns True if any of the elements in a set are True. Otherwise, it returns False.

myset = {3, 2, 1, True}


print(any(myset))

Output

True

any(mytuple) returned True because all the elements of the set are non-zero or
True.

Look at another example where one element of the set is False.

myset = {3, 2, 1, False}


print(any(myset))

Output

True

In the above example, one element of the set is False, but still the any() function
returned True. This is because if atleast one element is non-zero or True, then the
function returns True.

all()

It returns True if all the elements in a set are True. Otherwise, it returns False.

myset = {3, 2, 1, True}


print(all(myset))

Output

True

myset = {3, 2, 1, False}


print(all(myset))

Output

False

In this example, one element is False and so the all() function returned False.

Other Set Functions

We have already looked at some of the set functions like add(), discard(), remove(),
pop(), clear(), union(), intersection(), difference(), symmetric_difference() and copy().
Other useful functions are shown below.

Python isdisjoint()

It returns True if two sets are disjoint. Two sets are disjoint if they have no common
element.

set1 = {1, 2, 3, 4, 5}
set2 = {6, 7, 8}

print(set1.isdisjoint(set2))
print(set2.isdisjoint(set1))

Output

True
True
isdisjoint() function returned True in this example because both the sets don’t
have any common element.

Python intersection_update()

It calculates the intersection of two or more sets and assigns the result to the set
which calls the function.

Look at the following example to understand the concept better.

set1 = {1, 2, 3, 4, 5, 6}
set2 = {4, 5, 6, 7, 8}
set1.intersection_update(set2)

print('set1:', set1)
print('set2:', set2)

Output

set1: {4, 5, 6}
set2: {4, 5, 6, 7, 8}

set1.intersection_update(set2) - the intersection_update() function assigned


the intersection set {4, 5, 6} of sets set1 and set2 to set1 because set1 called the
function.

We can also pass more than one set to this function as shown below.

set1 = {1, 2, 3, 4, 5, 6}
set2 = {4, 5, 6, 7, 8}
set3 = {2, 5, 6, 8}
result = set1.intersection_update(set2, set3)

print('intersection_update result:', result)


print('set1:', set1)
print('set2:', set2)
print('set3:', set3)
Output

intersection_update result: None


set1: {5, 6}
set2: {4, 5, 6, 7, 8}
set3: {8, 2, 5, 6}

Here, the intersection set of sets set1, set2 and set3 is assigned to set3.

Python difference_update()

It calculates the difference of one set from another and assigns the result to the set
which calls the function.

Look at the following example.

set1 = {1, 2, 3, 4, 5, 6}
set2 = {4, 5, 6, 7, 8}

set1.difference_update(set2)

print('set1:', set1)
print('set2:', set2)

Output

set1: {1, 2, 3}
set2: {4, 5, 6, 7, 8}

The difference_update() function assigned the difference set {1, 2, 3}


to set1 because it called the function.

Python symmetric_difference_update()
It calculates the symmetric difference of one set from another and assigns the result
to the set which calls the function.

set1 = {1, 2, 3, 4, 5, 6}
set2 = {4, 5, 6, 7, 8}

set1.symmetric_difference_update(set2)

print('set1:', set1)
print('set2:', set2)

Output

set1: {1, 2, 3, 7, 8}
set2: {4, 5, 6, 7, 8}

The symmetric_difference_update() function assigned the symmetric difference set


{1, 2, 3, 7, 8} to set1 because it called the function.

Python issubset()

It returns True if a set is a subset of another set, otherwise it returns False. A


set set1 is a subset of set set2 if all the elements of a set1 are present in set2.

set1 = {4, 5, 6}
set2 = {4, 5, 6, 7, 8}

print(set1.issubset(set2)) # is set1 subset of set2?


print(set2.issubset(set1)) # is set2 subset of set1?

Output

True
False

All the elements of set1 are present in set2 and thus set1 is a subset of set2.
Whereas, set2 doesn’t have all its elements in set1 and so set2 is not a subset
of set1.
Python issuperset()

It returns True if a set is a superset of another set, otherwise it returns False. A


set set1 is a superset of set set2 if set1 has all the elements of set2.

set1 = {4, 5, 6}
set2 = {4, 5, 6, 7, 8}

print(set1.issuperset(set2)) # is set1 superset of set2?


print(set2.issuperset(set1)) # is set1 superset of set2?

Output

False
True

set1 doesn’t have all the elements of set2 and thus set1 is not a superset
of set2, set2 is a superset of set1.

Python Frozenset

Frozenset is an immutable variant of set. Sets are mutable and so elements can be
added or deleted to sets. Whereas, frozensets are immutable and so their elements
can’t be modified once assigned.

A frozenset can be created using the frozenset() function.


Like set(), frozenset() also converts a sequence (list, string, tuple) or a
collection(dictionary, set) into a frozenset.

The following example creates three frozensets from a list, a string and a set.

frozenset1 = frozenset([1, 2, 3]) # list is converted to frozenset


frozenset2 = frozenset("Codesdope") # string is converted to frozenset
frozenset3 = frozenset((1, 2, 3, 4)) # set is converted to frozenset
print(frozenset1)
print(frozenset2)
print(frozenset3)

Output

frozenset({1, 2, 3})
frozenset({'o', 'd', 's', 'e', 'C', 'p'})
frozenset({1, 2, 3, 4})

All the functions which can be used with sets can also be used with frozensets,
except for those functions which modify the elements. For example, functions
like add(), update(), remove(), etc, can’t be used with frozensets.

In the next example, adding an element to a frozenset with add() results in an error.

frozenset1 = frozenset([1, 2, 3])


frozenset1.add(4)
print(frozenset1)

Output

Traceback (most recent call last):


File "<stdin>", line 2, in <module>
AttributeError: 'frozenset' object has no attribute 'add'

Let’s look at some more examples.

frozenset1 = frozenset({1, 2, 3})


frozenset2 = frozenset({2, 3, 4, 5, 6})

print(len(frozenset1)) # prints no. of elements of frozenset1


print(sum(frozenset1)) # prints sum of elements of frozenset1
print(frozenset2.intersection(frozenset1)) # prints intersection of frozenset1 an
d frozenset2
print(frozenset2.issuperset(frozenset1)) # prints if frozenset1 is superset of fr
ozenset2

Output

3
6
frozenset({2, 3})
False
When Should We Use a Frozenset?

We know that the elements of a frozenset can’t be changed and no more elements
can be added or deleted from it. Therefore, it can be used to store the data we don’t
want the user to change. For example, suppose we have finalized some students in
a group. In that case, we can use a frozenset to store the names of those students,
so that no one can add, delete or modify their names.

If in this group of students, we don’t want to change the names of the students but
want to have the flexibility to add new students or delete existing students from the
group, then we should use a set instead.

So now we have learned about all the built-in data types in Python. We have gone
through some examples in the course but it is important that you practice questions
from the practice section, because the more you practice the better you will
understand the concept. In the next section, you are going to make your own
functions.

Python Functions

We have already used functions provided by Python like len(), split(), etc. Do you
know we can also create our own functions? Yes, we can create functions for
performing different tasks. In this chapter, we will learn to make our own functions.

Why do we need functions?

Function is a set of statements written together and given a name. We can call that
set of statements at any place in our program by just calling its name and without
writing the whole set of statements again. Quite convenient, right?
Suppose we need to check where a number is even or not multiple times in our
program. Instead of writing the same login again and again, we can write a function
and call it whenever we need to check whether a number is even or not.

Let's take an example.

# function definition
def display(x):
print("Hello")
print("You passed:", x)
# calling function
display(10)

Output

Hello
You passed: 10

def is a keyword used for making functions. To make a function, we first write def
and then the name of the function.

In the above example, display is the name of the function.

If we want to pass some value to a function, we give a parameter in the function


definition. In our example, 10 is the value passed to the function and x is the name of
the parameter. So, whenever a value is passed to the function, the parameter takes
that value. Therefore, x takes the value 10 in our example.
The body of the function consists of the
statements print("Hello") and print("You passed:", x).
So this was the description of the function. Now let’s call this function. A function is
called by writing the name of the function and passing a value (if required) to it. In
the above example the function is called by writing display(10).

Once a function is called, the statements in the body of the function are executed.
So, when display(10) was called, then the statements inside the
function display got executed with x = 10.

Notice that the body of the function is also represented by equal indentation (margin)
from the left.

Let’s take another example.

# function
def is_even(x):
if x%2 == 0:
print("even")
else:
print("odd")

is_even(2) # calling function


is_even(3) # calling function

Output

even
odd

In this example, is_even is the name of a function. From the body, you must have
understood what the function does. It prints "even" if the value passed to it is
divisible by 2, otherwise it prints "odd".

When the function is called for the first time, 2 is passed in the function call. So, the
statements in the body of the function are executed with x = 2.

Similarly, 3 is passed in the second function call. In that case, the statements in the
body of the function are executed with x = 3.

It is not compulsory for a function to have a parameter. We can also define functions
without any parameter. Let's see this example:

# function
def print_poem():
print("I am playing with computer")
print("Soon I will be master")
print("You will play my written game")
print("I will get a huge fame")

print_poem() # calling function

Output

I am playing with computer


Soon I will be master
You will play my written game
I will get a huge fame

As we saw here, we have not passed any parameter. Whenever we want to print the
whole poem, we just have to call the function print_poem().

Let's write a program to calculate and print the sum of two numbers.

def sum():
a = int(input("Enter first number >>>"))
b = int(input("Enter second number >>>"))
print(a+b)

sum()

Output

Enter first number >>>2


Enter second number >>>5
7

Python Parameters and Arguments


Parameters and arguments are just the different terms used for the variables/values
you are already aware of. Parameters are the variables we use in the function
definition whereas arguments are the values we pass in the function call.

Let’s again look at the first example of this chapter.


# function definition
def display(x):
print("Hello")
print("You passed:", x)

# calling function
display(10)

Output

Hello
You passed: 10

In this example, the variable x is called the parameter and the value 10 is called the
argument.

Look at another example in which the function has more than one parameter.

def checkdiv(x,y):
if x>=y:
if x%y == 0:
print(x,"is divisible by",y)
else:
print(x,"is not divisible by",y)
else:
print("Enter first number greater than or equal to second number")

checkdiv(4,2)
checkdiv(4,3)
checkdiv(2,4)

Output

4 is divisible by 2
4 is not divisible by 3
Enter first number greater than or equal to second number

While calling the function checkdiv(), the first argument is matched with the
parameter x and the second argument is matched with the parameter y. The rest of
the code is simple. The function checkdiv() checks if the first number x is greater
than or equal to the second number y. If the first number is greater or equal, it simply
checks whether the first number x is divisible by the second number y or not.
Whereas, if the first number is smaller than the second number, then the
interpreter prints("Enter first number greater than or equal to second
number").

Our own reverse function

We already have the reverse() function provided by Python to reverse the elements
of a list. Let's create our own reverse function.

def rev(a):
c = []
i = len(a) - 1 #len(a)-1 as index will go to 1 less than length as it starts f
rom 0.
while i >= 0:
c.append(a[i])
i = i - 1
print(c)

rev([2, 4, 6, 3, 5, 2, 6, 43])

Output

[43, 6, 2, 5, 3, 6, 4, 2]

That was fun!

Python Return

Functions can do one more thing, they can return us something. It means that a
function can give us something back. Whenever a return statement is executed in a
function, it terminates the function and returns some value to the place from where
the function was called

You will understand this from some examples.

def is_even(x):
if x%2 == 0:
return True
else:
return False

print(is_even(1))
print(is_even(2))

Output

False
True

Here, our function is returning us a Boolean (True or False). Inside the


function is_even(), if x%2 == 0 is True, then the function returns True, otherwise it
returns False.

Look at another example.

def rev(a):
c = []
i = len(a) - 1 #len(a)-1 as index will go to 1 less than length as it starts f
rom 0.
while i >= 0:
c.append(a[i])
i = i - 1
return c

new_list = rev([2, 4, 6, 3, 5, 2, 6, 43])


print(new_list)

Output

[43, 6, 2, 5, 3, 6, 4, 2]

Returning Multiple Values from a Python Function

We can also return multiple values from a function.

def calculation(num1, num2):


result1 = num1 + num2
result2 = num1 * num2
return result1, result2

sum, product = calculation(10, 20)


print("Sum:", sum, "Product:", product)

Output

Sum: 30 Product: 200

The function calculation() returns two values. The first returned value result1 is
assigned to sum and the second returned value result2 is assigned to product.

The function in the above program can be rewritten in a lesser number of lines as
shown below.

def calculation(num1, num2):


return (num1 + num2), (num1 * num2)

sum, product = calculation(10, 20)


print("Sum:", sum, "Product:", product)

Output

Sum: 30 Product: 200

Calling a function inside another function

Yes, we can call a function inside another function.

Let's take an example of checking a number's divisibility by 6. For a number to be


divisible by 6, it must be divisible by both 2 and 3. In the following example, we have
a function is_even() to check if it is divisibility by 2. The div6() function
calls is_even() inside itself to check the number's divisibility by both 2 and 3. Let's
see how:

def is_even(x):
if x%2 == 0:
return True
else:
return False
# div6 function to check divisibility by 6
def div6(y):
if is_even(y) and y%3 == 0:
return True
else:
return False

print(div6(12)) # calling div6 function

Output

True

We have called the is_even() function inside the div6() function.

Here, is_even() will return True if the given number is even or divisible by 2. The
condition is_even(y) and y%3 == 0 will be True only if is_even() and y%3==0 both
are True, which means that the condition will be True if the number is divisible by
both 2 and 3 or divisible by 6.

If a variable is defined inside a function, then it is available only to that function and
not outside the function.

Python Recursion

Recursion is calling a function inside the same function. Before going into this, let's
learn some mathematics.

We will calculate the factorial of a number. Factorial of any number n is (n)*(n-1)*(n-


2)*....*1 and written as (n!) and read as 'n factorial'. For example, 4! = 4*3*2*1 = 24

3! = 3*2*1 = 6
2! = 2*1 = 2
1! = 1
Also, 0! = 1

Let's code to calculate the factorial of a number:

def factorial(x):
if x==0 or x==1:
return 1
else:
return x*factorial(x-1)

print(factorial(0))
print(factorial(1))
print(factorial(4))
print(factorial(5))
print(factorial(10))

Output

1
1
24
120
3628800

Let's go through this code.

If we give 0 or 1, our function will return 1 because the values of both 0! and 1! are 1.
It is very simple up till here. Now see what happens if we pass 2 to our
function factorial().

else:
return x*factorial(x-1)

factorial(x-1) → When x is 2, factorial(x-1) is factorial(1) and it will be equal to 1.


(This is called recursion, the factorial() function is called inside itself). So, the result is
2*factorial(1) = 2*1 i.e. 2.

Therefore, the function will finally return 2.


Now let's see for 3:

x*factorial(x-1)
3*factorial(2) Now factorial(2) will be called and then it will be 2*factorial(1):
3*2*factorial(1) As factorial(2) will be 2*factorial(2-1)
3*2*1
So, it will return 6.

For 4

4*factorial(3)
4*3*factorial(2)
4*3*2*factorial(1)
4*3*2*1

If you have understood this, you have just done a thing which most of the
programmers find difficult in their beginning days.

Do you know about the Fibonacci series?

It is a series having its 0th and 1st terms 0 and 1 respectively.

Examples of series:
2, 4, 6, 8, 10, ... is a series. Its nth term is n*2. For example, 1st term is 2*1 = 2, 2nd
term is 2*2 = 4.

One more example can be 11,15,19,23,... . Its 1st term is 11 and any other nth term is
(n-1)th term + 4. Eg- 2nd term is 1st term + 4 = 11+4 = 15. 3rd term is 2nd term + 4 =
15+4 = 19.

The 0th term of the fibonacci series is 0, the 1st term of the fibonacci series is 1, and
its nth term f(n) = f(n-1)+f(n-2).

This means that

f(2) = f(1)+f(0)
= 1+0 = 1

f(3) = f(2)+f(1)
=1+1=2

Now, let's program it.

prev = {0:0,1:1}
def fib(n):
if n in prev.keys():
return prev[n]
else:
fi = fib(n-1) + fib (n-2)
prev[n] = fi
return fi

print(fib(0))
print(fib(1))
print(fib(2))
print(fib(3))
print(fib(4))
print(fib(5))

Output

0
1
1
2
3
5

prev = {0:0,1:1} → We are using a dictionary to store the fibonacci series


corresponding to the nth term. Initially there are only two numbers f(0) = 0 and f(1) =
1.

Coming to the next line:

if prev.has_key(n) → If the required term is in the dictionary then it will simply


return that, else, it will calculate and also store the corresponding calculated value in
the dictionary.

For f(2):

fi = fib(n-1) + fib(n-2) => fi = f(1)+f(0) = 1.

prev[n] = fi It will update our dictionary. And in the last line 1 is returned.

For f(3)

fi = f(1)+f(2)

For f(1), simply 1 will be returned and f(2) will be calculated as above and 1+0 i.e., 1
will be returned.

Similarly f(4) will be calculated

Python Fixed Variable


Argument

We already learned about functions in the previous chapter. In this section, we will
look at the different types of arguments we can pass to a function.

In the previous chapter, we passed some arguments to functions. However, a


function can also take a variable number of arguments.
Let’s look at a fixed number of arguments first.

Python Fixed Arguments

The number of arguments passed in a function call are equal to the number of
parameters in the function definition.

Python Positional Arguments

In all the previous examples, we passed a fixed number of arguments in the correct
order. These arguments which are passed in the same order as that of the
parameters to which they have to be assigned are called positional arguments.

def display(name, age):


print("My name is", name, "and my age is", age)

display("John", 45)

Output

My name is John and my age is 45


The function display() has two parameters - name and age. Therefore, we have
passed two arguments in the function call so that the first argument “John” is
assigned to the first parameter name and the second argument 45 is assigned to the
second parameter age.

In the above example, if only one argument is passed to the function, we will get an
error of missing positional argument.

def display(name, age):


print("My name is", name, "and my age is", age)

display("John")

Output
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
TypeError: display() missing 1 required positional argument: 'age'

Python Keyword Arguments

We can also pass a value assigned to a parameter as named argument. Let’s see
how.

def display(name, age):


print("My name is", name, "and my age is", age)

display(name = "John", age = 45)

Output

My name is John and my age is 45


In this example, name = "John" and age = 45 are the two keyword arguments. The
first keyword argument name = "John" assigns the value "John" to the
parameter name and the second keyword argument age = 45 assigns the value 45 to
the parameter age.

The order (position) in which keyword arguments are passed can be changed. Look
at the following example.

def display(name, age):


print("My name is", name, "and my age is", age)

display(age = 45, name = "John")

Output

My name is John and my age is 45


We can also pass a mix of positional and keyword arguments in a function call, but
keyword arguments must follow positional arguments i.e., all keyword arguments
must come after positional arguments.

def display(name, age, country):


print("My name is", name, "my age is", age, "and I live in", country)

display("John", 45, country = "Canada")

Output

My name is John my age is 45 and I live in Canada


In the above example, "John" and 45 are positional arguments and country =
"Canada" is a keyword argument.

If we give positional argument after keyword argument, then we get an error.

def display(name, age, country):


print("My name is", name, "my age is", age, "and I live in", country)

display("John", country = "Canada", 45)

Output

File "<stdin>", line 4 SyntaxError: positional argument follows keyword argument


Here, we have passed a position argument 45 after keyword argument
country=”Canada” and that’s why we got the error.

Keyword arguments must follow positional arguments when given together.

Python Variable Arguments

For variable arguments, the number of arguments passed in a function call may not
be equal to the number of parameters in the function definition.

Python Default Arguments

We can assign default values to parameters in function definition. These are called
default arguments.

def display(name = "xyz"):


print("My name is", name)

display()
display("John")

Output

My name is xyz
My name is John
The parameter name is assigned a default value "xyz". In the first function call, when
no value is passed for this parameter, then the parameter takes this default value. In
the second function call, "John" is passed in the function call, so the
parameter name takes the value "John".

We can give any number of default arguments in a function. Default arguments must
follow non-default arguments (positional or keyword arguments) when passed
together.

Default arguments must follow non-default arguments when given together.

Let’s see an example in which the function has a positional and a default argument.

def display(name, age = 45):


print("My name is", name, "and my age is", age)

display("John")
display("John", 50)

Output

My name is John and my age is 45


My name is John and my age is 50
Here, passing the first argument (positional argument) is mandatory and the second
argument is optional. If no second argument is passed, then the parameter age takes
the default value 45.

Look at another example in which the function has a mix of positional, keyword and
default arguments.

def display_marks(maths, physics, chem, bio = 80):


print("Maths:", maths, "Physics:", physics, "Chemistry:", chem, "Biology:", bi
o)

display_marks(85, chem = 45, physics = 90)


display_marks(85, chem = 45, physics = 90, bio = 50)

Output

Maths: 85 Physics: 90 Chemistry: 45 Biology: 80


Maths: 85 Physics: 90 Chemistry: 45 Biology: 50
As mentioned, keyword arguments must follow positional arguments and default
arguments must follow non-default arguments (positional and keyword arguments).
Violation of this order results in an error.

The following program throws an error because the default argument is given before
a non-default argument in function definition.

def display_marks(maths, bio = 80, physics):


print("Maths:", maths, "Physics:", physics, "Biology:", bio)

display_marks(85, physics = 90)

Output

File "", line 4


def display_marks(maths, bio = 80, physics):
^
SyntaxError: non-default argument follows default argument

Python Arbitrary Arguments

In some situations, we don’t know in advance how many arguments will be passed in
function call. For those cases, we can use arbitrary arguments.

Arbitrary arguments are variable-length arguments and are used when we are not
sure of the number of arguments to be passed in function call.

There are two types of arbitrary arguments.


1. Arbitrary positional arguments
2. Arbitrary keyword arguments

Python Arbitrary Positional Arguments

An asterisk (*) is used before the parameter name for arbitrary positional arguments.
For example,

def function_name(*parameter_name)

We can pass any number of non-keyword arguments to this parameter.

*parameter_name will take all those non-keyword arguments and parameter_name will
be a tuple containing all those arguments.

Let’s look at an example.

def some_function(*some_parameter):
print(some_parameter)

some_function(1,2,3,4)
some_function(1,2,3,4,5,6,7)

You can see that all passed arguments 1,2,3,4 and 1,2,3,4,5,6,7 are available as
elements of the tuple some_parameter.

Let’s take a function product_summary() which displays the list of products


purchased by different customers.

def product_summary(*products):
print("Products purchased:")

for product in products:


print(product)

print("### Customer 1 ###")


product_summary("TV", "Fridge", "Washing Machine", "AC", "Heater")

print("### Customer 2 ###")


product_summary("AC", "Microwave", "Heater")

Output

### Customer 1 ###


Products purchased:
TV
Fridge
Washing Machine
AC
Heater
### Customer 2 ###
Products purchased:
AC
Microwave
Heater

Customer 1 purchased 5 items and customer 2 purchased 3 items.

In the first function call, we passed five values. These values got assigned to
the parameter products in the form of a tuple, making products = ('TV', 'Fridge',
'Washing Machine', 'AC', 'Heater'). We printed the elements of this tuple using
a for loop.

Similarly, in the second function call, we passed three values, making products =
('AC', 'Microwave', 'Heater').

This example must have given you an idea of the use case of arbitrary arguments.
Python Arbitrary Keyword Arguments

A double asterisk (**) is used before the parameter name for arbitrary keyword
arguments. We can pass any number of keyword arguments to this parameter.

In arbitrary positional arguments, the passed values get wrapped up into a tuple.
Whereas, in arbitrary keyword arguments, the passed values get wrapped up into a
dictionary.
Now, suppose the function product_summary() displays the list of products along
with their quantities purchased by different customers.

def product_summary(**products):
print("Products purchased:")

for product, quantity in products.items():


print(product,":",quantity)

print("*** Customer 1 ***")


product_summary(TV = 3, Fridge = 4)

print("*** Customer 2 ***")


product_summary(AC = 3, Microwave = 2, Heater = 2)

Output

*** Customer 1 ***


Products purchased:
TV : 3
Fridge : 4
*** Customer 2 ***
Products purchased:
AC : 3
Microwave : 2
Heater : 2
In the first function call, we passed two keyword values. These values got assigned
to the parameter products in the form of a dictionary, making products = {'TV': 3,
'Fridge': 4}. Similarly, in the second function call, we passed three keyword values,
making products = {'AC': 3, 'Microwave': 2, 'Heater': 2}.

With this, you just finished the first part of Python programming. So give yourself a
pat and have a cup of coffee

Python OOP
Welcome to the new part of programming : Object Oriented Programming (OOP).

Wait for a while. You came up till here. Have you celebrated this? If not, then go and
take a break, because you have achieved a milestone in programming. Believe us!

Now after your break, let's talk about Object Oriented Programming.

To understand Object Oriented Programming, think of a book having a serial number


and a number specifying the number of pages in the book. Now, suppose you have
two types of books - Science book and Computer book. Suppose the serial number
of the Science book is SC12 and that of the Computer book is CS34 and the number
of pages are 200 and 250 respectively. It is like in the picture below:

Here, ‘Book’ is a class having attributes 'Page' and 'Serial number', and 'Science'
and 'Computer' are objects(instances) of ‘Book’.
So, now you understand that class is something which acts like a base, having
some attributes or features(Page and Serial Number in this case). And objects are
instances of class following those definitions.

One more example of class and objects you can think of is a bank account. Think of
'Account' as a class having attributes 'min_balance' and 'rate_of_interest', and
'Saving' and 'Current' as its objects.

I hope you have got the feel of classes and objects. We will be programming this in
the next chapter.

Python Classes and Objects


We introduced the concept of Object oriented programming in the last chapter. We
also looked at what classes and objects are with examples.

Let’s now see how classes and objects are created and used in Python.

Python Creating Classes and Objects

A class is defined using the class keyword.

class Square():
pass

Here, Square is the name of a class. In other words, Square is a class.

In the body of this class, we have not defined anything yet and so passed the
keyword pass.

Let’s make an object of this class.

class Square():
pass

sq = Square()

Here, sq is an object of the class Square or you can also say sq is a Square.

class Square():
pass

sq1 = Square()
sq2 = Square()

In this example, we have made two objects of the class Square - sq1 and sq2. We
can say that sq1 is a Square and sq2 is another Square.
We can define variables inside a class. These variables defined inside a class are
called attributes. These variables (attributes) can be accessed by all the objects of
the class.

We know that each square has a side. Thus, we can define an attribute ‘side’ inside
the Square class as shown below.

Let’s create a simple class.

# class definition
class Square():
side = 14

# creating object of class Square


sq = Square()
print(sq.side)

Output

14

class Square(): → Square is the name of a class.

side = 14 → The class has an attribute named side having a value of 14.

sq = Square() → sq is an object of the class Square. This also means that sq is a


Square. A class can have any number of objects.

print(sq.side) → The object sq accesses the class attribute side and its value is
printed.
In the above example, we can say that sq is a square whose side is 14.

It’s this easy to create a class and its object.

In Python, the terms object and instance are used interchangeably, and the
creation of an object is also called instantiation.

Now let’s create two objects of the class Square.

# class definition
class Square():
side = 14

# creating objects of class Square


sq1 = Square()
sq2 = Square()
print(sq1.side)
print(sq2.side)

Output

14
14

We created two objects sq1 and sq2 of the class Square. In simple English, we can
say that sq1 and sq2 are two squares having sides equal to 14.

Now suppose in the above example, these two objects have sides of different length.
In that case, we can change the value of the class attribute side for each object as
shown below.

# class definition
class Square():
side = 14

# creating objects of class Square


sq1 = Square()
sq2 = Square()

# changing value of side attribute for each object


sq1.side = 10
sq2.side = 20

print(sq1.side)
print(sq2.side)

Output

10
20

sq1.side = 10 changed the value of side for the object sq1 to 10 and sq2.side =
20 changed the value of side for the object sq2 to 20. In other words, sq1 is a square
having side 10 and sq2 is a square having side 20.

Now that was cool.

We can define attributes for objects without defining those attributes in the class as
shown below.

# class definition
class Rectangle():
pass

# creating objects of class Rectangle


rc1 = Rectangle()
rc2 = Rectangle()

# defining attributes for rc1 object


rc1.length = 10
rc1.breadth = 30

# defining attributes for rc2 object


rc2.breadth = 20

print(sq1.length)
print(sq1.breadth)
print(sq2.breadth)

Output
10
30
20

In the above example, we didn’t define any attribute inside the class. The
attributes length and breadth are defined for the object rc1 and an
attribute breadth is defined for the object rc2.

Note that there is no attribute named length for the object rc2. Therefore, if rc2 tries
to access length through rc2.length in the above program, then we will get an error
as shown below.

# class definition
class Rectangle():
pass

# creating objects of class Rectangle


rc1 = Rectangle()
rc2 = Rectangle()

# defining attributes for rc1 object


rc1.length = 10
rc1.breadth = 30

# defining attributes for rc2 object


rc2.breadth = 20

print(rc2.length)

Output

Traceback (most recent call last):


File "script.py", line 16, in <module>
print(rc2.length)
AttributeError: Rectangle object has no attribute 'length'

Now you know how to create classes and objects. So, let’s move forward and see
what else we can do with classes and objects..
Python Accessing Class Attributes by Class

In the above examples, we accessed the attributes of a class through the objects of
that class (for example, the object ‘sq’ accessed the attribute ‘side’ of the class
‘Square’). However, we can also access the attributes of a class by the class itself
without creating any object.

Look at the following example.

# class definition
class Rectangle():
length = 10

# accessing class attribute length


print("Length:", Rectangle.length)

Output

Length: 10

In this example, the class Rectangle directly accessed its


attribute length through Rectangle.length without creating any object.

A class can also define a new attribute outside the class definition as shown below.

# class definition
class Rectangle():
length = 10

# accessing class attribute length


print("Length:", Rectangle.length)

# defining new class attribute breadth


Rectangle.breadth = 20
print("Breadth:", Rectangle.breadth)

Output
Length: 10
Breadth: 20

The class Rectangle accessed its attribute length and defined a new
attribute breadth outside its class definition.

Python Methods Inside Class

Like variables defined in a class are called attributes, functions defined in a class
are called methods. All the objects of a class can access the variables and can call
the methods defined inside the class.

# class definition
class Square():
side = 14

def description(self):
print("This is a Square")

# creating objects of class Square


sq = Square()

print(sq.side) # accessing variable side of class


sq.description() # calling method description() of class

Output

14
This is a Square

The class Square has two members- a variable side and a method description().
We created an object sq of this class. Therefore, the object was able to access the
class variable (sq.side) and call the class method (sq.description()).

However, you must be wondering why we passed self as the parameter to the
method description().
self is a self-referencing pointer. It is necessary to pass self as a parameter if the
method is inside a class. This is because when an object calls a method, Python
automatically passes the object as the first argument to the method.

So, if we do not write self as a parameter, we will get an error. Try


removing self and see the error.
Look at another example.

# class definition
class Square():
def perimeter(self, side):
return side * 4
# object1
sq1 = Square()
print("Perimeter of Object 1:", sq1.perimeter(10))

# object2
sq2 = Square()
print("Perimeter of Object 2:", sq2.perimeter(20))

Output

Perimeter of Object 1: 40
Perimeter of Object 2: 80

Inside the Square class, a method named perimeter() is defined that takes the side
as input and returns the perimeter. We created two objects sq1 and sq2 of the class.
The first object called the perimeter() method passing 10 as the argument.
Similarly, the second object called the method by passing 20 as the argument.

Note that when an object called the class method perimeter(), Python automatically
passed the object as the first argument. Thus, a total of two arguments were passed
- instance of object and a value, which got assigned to the
parameters self and side respectively.
Python Using self to Access Attributes

The attributes in a class can be accessed inside a method of the same class
using self.

You will understand this concept by looking at some examples.

Look at the following example.

# class definition
class Square():
def perimeter(self):
return side * 4

# object1
sq1 = Square()
sq1.side = 10
print("Perimeter of Object 1:", sq1.perimeter())

# object2
sq2 = Square()
sq2.side = 20
print("Perimeter of Object 2:", sq2.perimeter())

Output

Traceback (most recent call last):


File "script.py", line 9, in <module>
print("Perimeter of Object 1:", sq1.perimeter())
File "script.py", line 4, in perimeter
return side * 4
NameError: name 'side' is not defined

Whoops, this program threw an error. We got this error because in


the perimeter() method, Python is not sure that the side in side * 4 is of which
object. (sq1 has a side of 10 and sq2 has a side of 20)

We can correct this by replacing side by self.side as shown below.

# class definition
class Square():
def perimeter(self):
return self.side * 4

# object1
sq1 = Square()
sq1.side = 10
print("Perimeter of Object 1:", sq1.perimeter())
# object2
sq2 = Square()
sq2.side = 20
print("Perimeter of Object 2:", sq2.perimeter())

Output

Perimeter of Object 1: 40
Perimeter of Object 2: 80

As mentioned before, self refers to the current object. In other words, self refers to
the object which called the method.

In this example, when the object sq1 calls the method perimeter(), then self refers
to sq1. In that case, writing self.side is the same as writing sq1.side.

Similarly, when the method is called by the object sq2, then writing self.side is the
same as writing sq2.side.

If you understood till here, well done! You have understood the major concepts
related to classes and objects. Now let’s see what more is there.

Python Use of ‘self’

So, use of 'self' are:

 It is passed automatically as the first parameter when you call a method on an


instance.
 It is used if we want to access an instance variable from the instance method,
from inside that method.

Python Class Attributes and Instance Attributes

Class attributes are the variables which belong to a class and are accessible by all
the objects of that class. These attributes are defined outside of all the class
methods.
# class definition
class Counter():
count = 0 # count is class attribute

def increase(self):
Counter.count = Counter.count + 1

# creating object of class Counter


c1 = Counter()
c1.increase()
print(c1.count)

# creating object of class Counter


c2 = Counter()
c2.increase()
print(c2.count)

Output

1
2

In the class Counter, the variable count is declared outside the class method and
thus is a class attribute. The first object c1 called the method increase() which
increased the value of count by 1 making it 1. Similarly, another object c2 called the
method increase() to increase the value of count to 2. Thus, you can see that after
the value of count got changed when the object c1 called the method, the updated
value of count was available for the object c2.

Note that inside a class method, an attribute can be accessed either by the class or
its objects. In this example, since the attribute is a class attribute, we accessed it
with the help of the class Counter inside the method as Counter.count.

Thus, the class attribute count is common for all class objects.

Let’s again look at the following example which we saw in the beginning of this
chapter.

# class definition
class Square():
side = 14

# creating objects of class Square


sq1 = Square()
sq2 = Square()
print(sq1.side)
print(sq2.side)

Output

14
14

Here also, side is a class attribute because both the objects of the class are able to
access it.

Instance variables are the variables which belong to a specific object.

Let’s again take the following example.

# class definition
class Square():
def perimeter(self):
return self.side * 4

# object1
sq1 = Square()
sq1.side = 10
print("Perimeter of Object 1:", sq1.perimeter())

# object2
sq2 = Square()
sq2.side = 20
print("Perimeter of Object 2:", sq2.perimeter())

Output

Perimeter of Object 1: 40
Perimeter of Object 2: 80
Here, side in self.side is an instance attribute because its value is different for
each method.

Now let’s move on to the last concept of this chapter.

Python Constructor

A constructor is a special method of a class which gets called automatically when


an object of that class is created. In Python, the __init__() method is called the
constructor.

A constructor is used to initialize variables when an object of that class is created.

class Student():
def __init__(self, name):
self.name = name

s = Student("Sam")
print(s.name)

Output

Sam

__init__(self, name) → This is the constructor which gets called when an object of
class Student is created. It takes name as a parameter, which means that
we must pass a value while making an object of the Student class.

self.name = name → This is the body of the constructor. name in self.name is an


attribute. It implies that the Student class will have some attribute name. Whereas
name on the right side of '=' is the parameter that we will pass to
the __init__ method at the time of object creation.

s = Student("Sam") → We are creating an object s of the Student class and


passing a string "Sam" while creating it. This string "Sam" is passed to the name
parameter of __init__(self, name).
In self.name = name, name on the right side is the parameter in the
method __init__(self, name) and name in self.name is an attribute of
the Student object.

You are now ready with the idea of class, objects and methods. Let's look at an
example and get the concept clearer.

class Rectangle():
def __init__(self, l, b):
self.length = l
self.breadth = b

def getArea(self):
return self.length * self.breadth

def getPerimeter(self):
return 2*(self.length + self.breadth)

rect = Rectangle(2,4)

print(rect.getArea())
print(rect.getPerimeter())

Output

8
12

Most of the program must be clear to you.

In this program, the constructor __init__() takes two arguments whenever an


object of the class Rectangle is created. The first argument is assigned to the
parameter l and the second is assigned to the parameter b.

The statement self.length = l specifies that length is an attribute of


the Rectangle object and is assigned the value of the parameter l. Similarly, the
statement self.breadth = b specifies that breadth is also an attribute of
the Rectangle object and is assigned the value of the parameter b. self in these two
statements refer to the Rectangle class.
In the getArea method, the parameter self refers to the object which calls this
method. Thus, self.length and self.breadth refers to the length and breadth of the
current object (object which calls the method). For example, when the
object rect calls this method, then self.length ('rect' is calling) will refer to
the length of rect i.e. 2.

So this was all in classes and objects. If you have reached till here, that means you
have just understood what many beginners struggle with.

Python Instance Class and


Static Method

We read about classes and objects in the previous chapter. We also read that the
attributes which belong to a class and are accessible by all the objects of the class
are called class attributes and the attributes which belong to objects are
called instance attributes. In this chapter, we will be looking at different types of
methods a class can have.

Till now, the methods we have seen were used to access or change instance
attributes. These are called instance methods and you will be using mostly these
methods only in classes.

However, there are two other types of methods: class methods and static
methods. Class methods are used to access or change class attributes. Static
methods are normal functions which don’t deal with anything related to classes. For
example, static methods can be used to print some message or detect if a number
passed to it is even.

Let’s look at them one by one.

Python Instance Methods


Instance methods are the regular methods we use in classes. These are used most
of the time. Till now, all the methods we saw were instance methods.

We know that instance methods take self as the first parameter which refers to an
object of the class. These are mostly used to access or change the attributes of the
object on which they are called.

Instance methods can be called only by the objects of the class in which they are
defined.

class Student():
def __init__(self, name):
self.name = name

def display(self):
return self.name

st1 = Student("John")
st2 = Student("Johny")

print(st1.display())
print(st2.display())

Output

John
Johny

The display() method is an instance method and so it receives self as a parameter


which refers to the current object (object which calls the method). It accesses the
attribute name in self.name of only the object which calls this method.

When the object st1 calls the method, then self.name becomes the same
as st1.name. When st2 calls the method, then self.name becomes the same
as st2.name.

Python Class Methods


Class methods take cls as the first parameter which refers to the current class.
These are used to access or change the class attributes. You know that class
attributes can be accessed by all the objects of a class.

To define a class method, @classmethod decorator is used before the function


definition. We will read more about decorators later.

Class methods can be called by the objects of the class as well as by the class in
which they are defined. So unlike instance methods, we can call class methods
directly with class (without making any object).

class Student():
name = "John"

@classmethod
def create(cls):
return cls.name

print(Student.create())

Output

John

In this example, create() is a class method because it is defined following


the @classmethod decorator. This method is called by the Student class by
writing Student.create() and so it takes the class Student as its first parameter cls.

It accesses the class attribute name by writing cls.name. Writing cls.name is the same
as writing Student.name.

Let’s look at another example in which a class method is used to call the constructor
when an object of the class is created.

class Student():
def __init__(self, name, age):
self.name = name
self.age = age

@classmethod
def create(cls, name, age):
return cls(name, age + 1)

st1 = Student("John", 20)


st2 = Student.create("Johny", 30)

print(st1.age)
print(st2.age)

Output

20
31

In this example, the first object st1 is created normally using constructor and the
second object st2 is created using the create() class method.

In the create method, return cls(name, age + 1) is similar to return Student(name,


age + 1).

The create() method takes the Student class as its first parameter cls,
and name and age as the other two parameters. Then it increases the age by 1 and
finally returns the constructor by calling cls(name, age + 1) (Writing cls(name, age
+ 1) is the same as writing Student(name, age + 1)) .This returned constructor
creates a new instance with the passed name and age.

Python Static Methods

Static methods, like class methods, are bound to a class and not its objects. A static
method doesn’t know anything about the attributes or other definitions of the class in
which it is defined. These are mostly used as independent utility functions which
don’t access or interfere with the state or attributes of the class.

To define a static method, @staticmethod decorator is used before the function


definition.

Static methods can be called by the objects of the class as well as by the class itself
in which they are defined.
class Number():

@staticmethod
def even(x):
if x % 2 == 0:
return True
else:
return False

print(Number.even(40))

Output

True

In this example, even() is a static method because it is defined following


the @staticmethod decorator. And yes, as we said, it is just a normal function defined
inside a class and doesn’t interfere with the class members. It returns True if its
parameter is even, and returns False otherwise.

Let’s see an example where class method and static method are used together.

class Person():
def __init__(self, name, age, can_vote):
self.name = name
self.age = age
self.can_vote = can_vote

@staticmethod
def is_adult(age):
if age >= 18:
return True
else:
return False

@classmethod
def create(cls, name, age):
if cls.is_adult(age) == True:
return cls(name, age, "Yes")
else:
return cls(name, age, "No")

st1 = Person.create("John", 15)


st2 = Person.create("Johny", 20)

print("Can", st1.name, "vote?", st1.can_vote)


print("Can", st2.name, "vote?", st2.can_vote)

Output

Can John vote? No


Can Johny vote? Yes

In the above example, is_adult() is a static method which returns True if the age
passed to it is greater than or equal to 18, and returns False otherwise.

create() is a class method which calls the constructor with suitable arguments.

st1 = Person.create("John", 15) - While creating an object st1, the Person class
calls the create() method passing the name and age parameters.

The create() method internally calls the is_adult() method which returns True or
False depending on whether the age passed to it is greater than 18 or not. If it
returns True, then the create() method calls the constructor with the third argument
as “Yes”, otherwise calls it with the third argument as “No”. The constructor then
assigns the passed values to the name, age and can_vote attributes of the current
object st1.

Similarly, the other object st2 is created.

We read about the three types of methods we can deal with in OOP, but mostly we
use instance methods

Python Subclass of a class


Till now, you have learned about classes and objects. In this chapter, you will learn a
new concept of object oriented programming- Subclass.

Python Subclass

To understand what a subclass is, let’s look at an example.

Suppose we define a rectangle with some length and breadth. Now, a square is also
a rectangle but having the same length and breadth.

From this, you must have got the feel that a square is a subclass of rectangle.

Let’s think of some more examples of subclasses. A class ‘Book’ can have
‘ScienceBook’ and ‘MathsBook’ as its subclasses. Another class ‘Animals’ can have
‘Dog’, ‘Cat’ and ‘Goat’ as its subclasses.

The class whose subclass has been made is called a superclass. Other names
of superclass are base class or parent class, and other names
of subclass are derived class or child class. In our Rectangle example, Rectangle
is the superclass and Square is its subclass. The process of creating a subclass of a
class is called inheritance.

All the attributes and methods of superclass are inherited by its subclass also. This
means that an object of a subclass can access all the attributes and methods of the
superclass. Moreover, subclass may have its own attributes or methods in addition
to the inherited ones as well.
Let's learn to create a subclass in Python.

# superclass
class Person():
def display1(self):
print("This is superclass")

# subclass
class Employee(Person):
def display2(self):
print("This is subclass")

emp = Employee() # creating object of subclass

emp.display1()
emp.display2()

Output

This is superclass
This is subclass

We created two classes Person and Employee with one method in each class.

class Employee(Person) → It means that the class Employee is inherited from the
class Person. In simple words, Employee is a subclass of Person. (An employee is a
person)

Since Employee is a subclass of Person, it inherits the method display1() of its


superclass.

emp = Employee → We created an object emp of the class Employee.

emp.display1() → An object of a subclass can access any of the members


(attributes or methods) of the superclass, so emp was able to call the
method display1() of the parent class Person.

So this was a simple implementation of a subclass. Let’s try something more.


We know that an object of a child class can access the attributes or methods of the
parent class. But the reverse is not true, i.e., an object of the parent class can’t
access the attributes or methods of the child class.

Let’s see what happens when an object of the class Person tries to access the
method of its subclass Employee.

# superclass
class Person():
def display1(self):
print("This is superclass")

# subclass
class Employee(Person):
def display2(self):
print("This is subclass")

p = Person() # creating object of superclass

p.display1()
p.display2()

Output

This is superclass
Traceback (most recent call last):
File "script.py", line 14, in
p.display2()
AttributeError: 'Person' object has no attribute 'display2'

We created an object p of the parent class Person. This object called the
method display1() successfully which printed a message. But when it tried to call
the method display2() of its subclass, we got an error as expected.

Look at the next example.

# superclass
class Person():
def __init__(self, per_name, per_age):
self.name = per_name
self.age = per_age

def display1(self):
print("In superclass method: name:", self.name, "age:", self.age)

# subclass
class Employee(Person):
def display2(self):
print("In subclass method: name:", self.name, "age:", self.age)

emp = Employee("John", 20) # creating object of superclass

emp.display1()
emp.display2()
print("Outside both methods: name:", emp.name, "age:", emp.age)

Output

In superclass method: name: John age: 20


In subclass method: name: John age: 20
Outside both methods: name: John age: 20

Let’s try to understand what is happening here.

The class Employee is a subclass of the class Person. Thus, Employee inherits the
attributes (name and age), the method (display1()) and the constructor (__init__())
of Person. As a result, these can also be accessed by the objects of the
subclass Employee.

Therefore, in the method display2() of the subclass, we have directly accessed the
attributes name and age of the parent class.

emp = Employee("John", 20) → An object emp of the subclass Employee is created


by passing the values “John” and 20 to the per_name and per_age parameters of the
constructor of the parent class (because the subclass has inherited the constructor
of the parent class). Thus, in the constructor, the values of the
attributes name and age become “John” and 20 respectively for the object emp.
emp.display1() →The object emp calls the display1() method of the parent class.

emp.display2() →The object emp calls the display2() method of its own class.

print("Outside both methods: name:", emp.name, "age:", emp.age) →The


object emp was able to access the attributes name and age of the parent class.

If you have understood this example, that means you have understood the concept
of subclasses. Now, let’s look at some more cases.

If both the subclass and the superclass has a constructor, then the object of the
subclass is created through the constructor of the subclass.

If a subclass has the __init__() method, then it will not inherit


the __init__() method of the superclass. In other words, the __init__() method of
the subclass overrides the __init__() method of the superclass.

Let’s see an example where both the subclass and the superclass has a constructor.

# superclass
class Person():
def __init__(self, per_name, per_age):
self.name = per_name
self.age = per_age

# subclass
class Employee(Person):
def __init__(self, emp_name, emp_age, emp_salary):
self.salary = emp_salary
Person.__init__(self, emp_name, emp_age)

emp = Employee("John", 20, 8000) # creating object of superclass

print("name:", emp.name, "age:", emp.age, "salary:", emp.salary)

Output

name: John age: 20 salary: 8000

We created an object emp of the subclass Employee by passing the values “John”, 20
and 8000 to the emp_name, emp_age and emp_salary parameters of the constructor of
the child class (because the child class has a constructor of its own). In this
constructor, the value of emp_salary is assigned to the attribute salary of the
object emp, and the constructor of the parent class is called by
passing emp_name and emp_age as the arguments. In the constructor of the parent
class, the attributes name and age becomes “John” and 20 respectively for the
object emp.

Note that the constructor of the parent class Person is called from the constructor of
the child class by writing Person.__init__(self, emp_name, emp_age) .

Let’s see one more example.

# superclass
class Person():
def __init__(self, per_name, per_age):
self.name = per_name
self.age = per_age

def display1(self):
print("name:", self.name)
print("age:", self.age)

# subclass
class Employee(Person):
def __init__(self, emp_name, emp_age, emp_salary):
self.salary = emp_salary
Person.__init__(self, emp_name, emp_age)

def display2(self):
print("salary:", self.salary)
Person.display1(self)

emp = Employee("John", 20, 8000) # creating object of superclass

emp.display2()

Output
salary: 8000
name: John
age: 20

You must have understood this program. We are calling the constructor of the parent
class Person inside the constructor of the child class by
writing Person.__init__(self, emp_name, emp_age) and calling
the display1() method of the parent class inside the display2() method of the child
class by writing Person.display1(self).

Python super() Function

In the previous example, instead of Person.__init__, we can use


the super() function for calling the constructor and methods of the parent class
inside the child class.

The super() function returns a parent class object and can be used to access the
attributes or methods of the parent class inside the child class.

Let’s rewrite the last example using super().

# superclass
class Person():
def __init__(self, per_name, per_age):
self.name = per_name
self.age = per_age

def display1(self):
print("name:", self.name)
print("age:", self.age)

# subclass
class Employee(Person):
def __init__(self, emp_name, emp_age, emp_salary):
self.salary = emp_salary
super().__init__(emp_name, emp_age)
def display2(self):
print("salary:", self.salary)
super().display1()

emp = Employee("John", 20, 8000) # creating object of subclass

emp.display2()

Output

salary: 8000
name: John
age: 20

In this example, we replaced Person.__init__(self, emp_name,


emp_age) by super().__init__(emp_name,
emp_age) and Person.display1(self) by super().display1() inside the subclass.

When calling a method using super(), passing self as the first argument is not
required.

So, that was all that you needed to know about subclasses. After this chapter,
practice questions on this topic to have a good hold over it.

There are two in-built functions in Python, namely isinstance() and issubclass(),
which can be used to check the class of an object or the subclass of a class.

Python isinstance()

This function is used to check if an object is an instance of a particular class. In other


words, it checks if an object belongs to a particular class.

# superclass
class Person():
pass
# subclass
class Employee(Person):
pass

per = Person() # creating object of superclass


emp = Employee() # creating object of subclass

print(isinstance(per, Person))
print(isinstance(per, Employee))
print(isinstance(emp, Person))
print(isinstance(emp, Employee))

Output

True
False
True
True

Here, per is an object of the superclass Person and emp is an object of the
subclass Employee. Therefore, per belongs only to Person, whereas emp belongs
to Employee as well as Person.

isinstance(per, Person) checks if the object per belongs to the class Person.

Python issubclass()

This function is used to check whether a class is a subclass of another class.

# superclass
class Person():
pass

# subclass
class Employee(Person):
pass
print(issubclass(Person, Employee))
print(issubclass(Employee, Person))

Output

False
True

issubclass(Person, Employee) checks whether the class Person is a subclass of


the class Employee.

Before wrapping up this chapter, let’s look at one more example of subclass.
Square and rectangle example

class Rectangle():
def __init__(self,leng,br):
self.length = leng
self.breadth = br
'''while calling a method in a class python
automatically passes an instance( object ) of it.
so we have to pass sef in area i.e. area(self)'''
def area(self):
'''length and breadth are not globally defined.
So, we have to access them as self.length'''
return self.length*self.breadth
class Square(Rectangle):
def __init__(self,side):
Rectangle.__init__(self,side,side)
self.side = side
s = Square(4)
print(s.length)
print(s.breadth)
print(s.side)
print(s.area())# It appears as nothing is passed but python will pass an instance
of class.

Output
4
4
4
16

There is nothing new in the above code to explain. If you are facing any difficulty,
then read the comments inside the code.

You can ask questions in the discussion section anytime and feel free to ask

Python Multiple Inheritance

Inheritance can be done in a number of ways. Till now, we have seen examples
where a single class inherits another class.

We can also create a subclass of another subclass.


For example, a class Shape has a subclass Rectangle, and the class Rectangle has
a subclass Square. In this case, Square will inherit the properties of Rectangle as
well as Shape.

class Shape():
# code

class Rectangle(Shape):
# code

class Square(Rectangle):
# code
We also know that a class can have more than one subclass.

For example, a class Shape has two subclasses Rectangle and Circle. Here, both
Rectangle and Circle will inherit the properties of Shape.

class Shape():
# code

class Rectangle(Shape):
# code

class Circle(Shape):
# code

Let’s see an example having the above two scenarios of inheritance.

class Shape():
def m(self):
print("This is Shape")

class Rectangle(Shape):
def m1(self):
print("This is Rectangle")
class Circle(Shape):
def m2(self):
print("This is Circle")

class Square(Rectangle):
def m3(self):
print("This is Square")

r = Rectangle() # creating object r of subclass Rectangle


c = Circle() # creating object c of subclass Circle
s = Square() # creating object s of subclass Square

r.m1()
r.m()

print("****")

c.m2()
c.m()

print("****")

s.m3()
s.m1()
s.m()

Output

This is Rectangle
This is Shape
****
This is Circle
This is Shape
****
This is Square
This is Rectangle
This is Shape

In this example, Shape has two subclasses Rectangle and Circle, and thus the
objects of Rectangle and Circle can access the methods of their respective classes
as well as the method of the parent class Shape. Rectangle further has a
subclass Square and so the object of Square can access the method of Square and
the methods of its ancestor classes Rectangle and Shape.

This was pretty straightforward, right?

In Python, a class can have more than one parent class. So, it inherits the properties
of all its parent classes.

For example, a class Deer is a subclass of two classes - Animal and Herbivorous.
So, it will inherit the properties of both its parent classes.

class Animal():
# code

class Herbivorous():
# code

class Deer(Animal, Herbivorous):


# code
# superclass
class Animal():
def m1(self):
print("This is parent class Animal")

# superclass
class Herbivorous():
def m2(self):
print("This is parent class Herbivorous")

# subclass
class Deer(Animal, Herbivorous):
def m(self):
print("This is child class Deer")

deer = Deer()

deer.m()
deer.m1()
deer.m2()

Output

This is child class Deer


This is parent class Animal
This is parent class Herbivorous

Here, the class Deer has two parent classes Animal and Herbivorous. Thus, its
object deer can access both its parent classes’ methods m1() and m2().

Look at another example.

class Area():
def getArea(self, l, b):
return l * b

class Perimeter():
def getPerimeter(self, l, b):
return 2*(l + b)

class Rectangle(Area, Perimeter):


def __init__(self, l, b):
self.length = l
self.breadth = b

def area(self):
return super().getArea(self.length, self.breadth)

def perimeter(self):
return super().getPerimeter(self.length, self.breadth)

rect = Rectangle(7, 4)

print("Area:", rect.area())
print("Perimeter:", rect.perimeter())

Output

Area: 28
Perimeter: 22

In this example, the class Rectangle inherits two classes Area and Perimeter. The
class Area has a method getArea() which receives length and breadth and returns
the area. The class Perimeter also has a method getPerimeter() which receives
length and breadth and returns the perimeter.

When we created the object rect of the class Rectangle, its constructor got called
and assigned the values 7 and 4 to the attributes length and breadth respectively
for the object rect. So the length and breadth of rect became 7 and 4 respectively.

Then the object called the method area() of the class Rectangle which called the
method getArea(l, b) of its parent class Area assigning the values 7 and 4 to the
parameters l and b respectively. This method then returned the area of the
rectangle of length 7 and breadth 4.

Similarly, we returned the perimeter of the rectangle using the class Perimeter.
Note that in super().getArea(self.length, self.breadth), the getArea() method
of the parent class Area got called, and in super().getPerimeter(self.length,
self.breadth), the getPerimeter() method of the class Perimeter got called.

You were already aware of the concept of inheritance. In this chapter, we saw the
different ways we can implement it.

Python Method Overriding and


MRO

In the previous chapters, we read about inheritance and the different types of
inheritance that can be implemented in Python.

Now, let’s consider a scenario where the parent and the child class have methods
with the same name. In such cases, method overriding takes place.

Python Method Overriding

To understand method overriding, let's first look at an example.

class Animal():
def sound(self, a):
print("This is parent class")

class Dogs(Animal):
def sound(self):
print("Dogs bark")

d = Dogs()
d.sound()

Output

Dogs bark
In the above example, the class Dogs and its parent class Animal have methods with
the same name sound. When the object d of the class Dogs calls this method, then
the method of the child class Dogs gets called, not that of the parent class. Thus, the
method of the child class overrides the method of the parent class when called by an
object of the child class.

This is called method overriding.

We saw the use of super() while learning about subclass. Let’s call a parent class
method inside a child class method using super(), though this is not related to
method overriding.

class Shape():
def display(self):
print("This is Shape")

class Rectangle(Shape):
def display(self):
print("This is Rectangle")
super().display()

class Square(Rectangle):
def display(self):
print("This is Square")
super().display()

sq = Square()
sq.display()

Output

This is Square
This is Rectangle
This is Shape

The class Shape has a subclass Rectangle and Rectangle has a subclass Square. All
the three classes have methods with the same name. The display() method
of Square calls the display() method of its parent Rectangle
using super() as super().display(). The display() method of Rectangle calls
the display() method of its parent Shape by calling super().display().

That’s it. So this was all about method overriding.

Now consider a scenario where a class inherits two parent classes and both the
parent classes have a method with the same name, say func. If an object of the child
class calls this method func(), then according to you the method of which of the two
parent classes must be called?

This seems confusing. For these types of scenarios, there is an order in which
Python calls the variables (attributes) and methods of classes in inheritance. This
order is known as Method Resolution Order (MRO).

Python Method Resolution Order (MRO)

Before looking at the rules of MRO, let’s introduce a new term - object class.

Python object Class

In Python, every class is a subclass of the object class. The object class is an in-
built class in Python and is the superclass of all the classes.

In Python, each class is a subclass of the object class.

Let’s prove it with an example.

class Shape():
pass

s = Shape()

print(issubclass(Shape, object))
print(isinstance(s, object))

Output
True
True
Here we created a class Shape and its object s. So, as we mentioned above, Shape is
a subclass of the in-build object class. Therefore, issubclass(Shape,
object) returned True and isinstance(s, object) also returned True.

In the chapter Datatypes, we saw that the data types (like int, float, list, etc.) are
predefined classes in Python. For example, int is a pre defined class and integers
like 4, 10, etc, are its objects. This also means that all the data types are also
subclasses of the object class. We can verify this using
the issubclass() and isinstance() functions as shown below.

print(issubclass(int, object))
print(issubclass(float, object))
print(isinstance(5, int))
print(isinstance(5, object))

Output

True
True
True
True
As discussed, data types int and float are subclasses of the object class.

In fact it is a good practice to inherit object class while making our own class. For
example instead of class Square(), we can and should write class Square(object).

class Shape(object):
pass

s = Shape()

Python Rules of Method Resolution Order

There are two rules followed by Python to achieve MRO - depth-first path rule
and left-right path rule.
Python Depth-first path Rule

This rule states that a method is first searched in the current class. If the method is
not found in the current class, then it is searched in the parent class.

We have already looked at this case in method overriding. Let’s again see an
example.

class A():
def display(self):
print("This is A")

class B(A):
def display(self):
print("This is B")

b = B()
b.display()

Output

This is B
This is the simplest example of method overriding.

Here B is a subclass of A and both the classes have the display() method. An
object of B calls the display() method.

So, according to the depth-first path rule, the method is first searched in the class
whose object calls it. Therefore, the display() method is searched in class B and
executed.

Now in the above example, assume that this method is not present in class B as
shown below.

class A():
def display(self):
print("This is A")
class B(A):
pass

b = B()
b.display()

Output

This is A
According to the rule, if the method is not present in the class whose object called it,
then the method is searched in the parent class. Therefore the display() method is
searched in the parent class A of the class B and executed.

Look at another example below.

class A():
def display(self):
print("This is A")

class B(A):
pass

class C(B):
pass

c = C()
c.display()

Output

This is A
This is the same as previous examples, with an additional level of inheritance. C is a
subclass of B and B is a subclass of A.

So, if the object of C calls a method, then it is first searched in C, then in B and then
in A. So, the order is C → B → A.

We can also check this order using the __mro__ attribute or the mro() function.
For example, the order in which classes are checked when an object of the
class C calls a method can be found using C.__mro__ or C.mro(). Let’s print the MRO
in the above example.

class A():
def display(self):
print("This is A")

class B(A):
pass

class C(B):
pass

c = C()
c.display()

print(C.__mro__)
print(C.mro())

Output

This is A
(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
[<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]

And we got the same order: C → B → A → object. You can ignore the object class
at the end, because we will always get it at the end of an MRO.

So this was the depth-first path rule, though we already learned about it in the
method overriding section.

Python Left-right path Rule

Now consider a case where a class has two parent classes and all the three classes
have methods with the same name.
class A():
def display(self):
print("This is A")

class B():
def display(self):
print("This is B")

class C(A, B):


def display(self):
print("This is C")

c = C()
c.display()

Output

This is C
In this example, C is a subclass of two classes A and B and all the three classes
have the display() method.

When the object c of the class C calls the display() method, then no doubt this
method is first checked in class C (because the class whose object calls the method
is checked first).

If the method was not present in C, then according to the depth-first path rule, it is
checked in the parent class of C. But we have two parent classes of C, so which one
is checked first?

This is where the left-right path rule is followed. This rule states that if we have two
parent classes of a class, then the parent classes are checked in the order in which
they are inherited.

For example, in the above program, C inherits A and B as class C(A, B). This
means that it inherited first A and then B, and therefore out of the two parent classes,
A is checked before B. Let’s verify this by displaying the MRO as shown below.

class A():
pass
class B():
pass

class C(A, B):


pass

print(C.mro())

Output

[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]


We got the same order which we were expecting: C → A → B.

Let’s check this in practice in the following example.

class A():
def display(self):
print("This is A")

class B():
def display(self):
print("This is B")

class C(A, B):


pass

c = C()
c.display()

Output

This is A
Since the object of C calls the method, first the method is checked in C.

The method is not present in C, therefore it is checked in one of the parent classes
of C according to the depth-first path rule.

Out of the two parents, the method is checked first in A according to the left-right
path rule. The method is present in A and therefore the method of A got executed.
So these are the two rules which Python follows to detect which class’ method to call
in inheritance. Also, remember that in case of collision, depth-first path rule is
preferred over left-right path rule.

Precedence of depth-first path rule is higher than left-right path rule.

A method can’t be checked in a class more than once.

A class can’t be checked before its direct subclass in MRO.

Let’s look at some more examples of MRO.


Example 1

class A():
pass

class B():
pass

class C(A, B):


pass

class D(C):
pass

print(D.mro())

Output

[<class '__main__.D'>, <class '__main__.C'>, <class '__main__.A'>, <class


'__main__.B'>, <class 'object'>]
This one was easy. Here we are checking the MRO of class D, which means that the
method called by an object of D will be checked in the classes in which order.

We got the MRO as D → C → A → B.

The method is checked first in D because its object called the method.

If the method is not present in D, then it is checked in its parent class C (depth-first
path rule).
If it is not present in C, then it is checked in of the parent classes of C (depth-first
path rule).

Out of the two parents, the method is checked in first A and then in B (left-right path
rule).
Example 2

class A():
pass

class B(A):
pass

class C():
pass

class D(B, C):


pass

print(D.mro())

Output

[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.A'>, <class


'__main__.C'>, <class 'object'>]
We got the MRO as D → B → A → C

If an object of D calls the method, then it is first checked in D.

If the method is not present in D, then it is checked in one of the parent classes of D
(depth-first path rule).

Out of the two parents, the method is checked first in B (left-right path rule).

If the method is not present in B, then according to the depth-first path rule it should
be checked in its parent class A, but according to the left-right path rule it should be
checked in C. Since the precedence of depth-first path rule is higher than left-
right path rule, so the method will be checked in A (depth-first path rule).

If it is not present in A, then it is checked in C.


Example 3

class A():
pass

class B():
pass

class C(A):
pass

class D(B, C):


pass

print(D.mro())

Output

[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class


'__main__.A'>, <class 'object'>]
We got the MRO as D → B → C → A

First D is checked.

If the method is not present in D, then one of its parents (B or C) is checked (depth-
first path rule).

Out of the two parents, the method is checked first in B (left-right path rule).

If the method is not present in B, then it is checked in C (left-right path rule).

If it is not present in C, then it is checked in the parent of C i.e. A (depth-first path


rule).
Exmaple 4

class A():
pass

class B(A):
pass
class C(A):
pass

class D(B, C):


pass

print(D.mro())

Output

[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class


'__main__.A'>, <class 'object'>]
We got the MRO as D → B → C → A

If you calculate the MRO on your own, maybe you will get the answer as D → B → A
→ C → A. Let’s understand why we got a different MRO.

First the method is checked in class D.

If the method is not found in D, then it is searched in one of its parents (B or C)


(depth-first path rule).

Out of the two parents, the method is checked first in B (left-right path rule).

If the method is not found in B, then according to the higher precedence of depth-first
path rule, it should be checked in A and not in C. But we also read that a class can’t
be checked before its direct subclass. Since C is a direct subclass of A, therefore
C is checked before A.

Now, if the method is not found in C, then it is checked in A.


Example 5

class A():
pass

class B():
pass
class C(A, B):
pass

class D(B):
pass

class E(C,D):
pass

print(E.mro())

Output

[<class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class


'__main__.D'>, <class '__main__.B'>, <class 'object'>]
We got the MRO as E → C → A → D → B

Try to understand how we got this MRO on your own. If stuck, you can ask doubts in
the discussion section.

Congratulations! You have just finished the Object Oriented Programming part of
Python. This is a big milestone for anyone learning this language and you can now
confidently call yourself a Python programmer. It is also important to practice a lot of
questions to become better in Python.

Python Errors and Exceptions

You must have got some errors on running your programs when you made some
mistake in the program. It is often frustrating to get errors especially when we can’t
figure out the cause of that error.

Whenever we write something wrong in our program, the interpreter throws an error
when we run the program. For example, if we forget to give proper indentation to the
body of a loop, then on running the program, the execution gets terminated and an
error named IndentationError is thrown.
In this chapter, we will look at the types of errors we can get in Python. Errors are
broadly classified into two types.

1. Syntax Errors
2. Exceptions (Logical Errors)

Python Syntax Errors

We get syntax errors when we don’t write the correct syntax of Python language.

For example, we can get a syntax error when we forget to write a colon : after the
condition of the if statement as shown below.

if 2 < 3
print(2)

Output

File "script.py", line 1


if 2 < 3
^
SyntaxError: invalid syntax

Here the error message states that the error is in line number 1 of your program. On
checking the syntax of the statement written in line number 1, you can identify you
missed a colon.

Look at another example.

print"Sam")

Output

File "script.py", line 1


print"Sam")
^
SyntaxError: invalid syntax
We got a syntax error here because we didn’t put the opening parentheses
bracket ( before “Sam” in line number 1 of the program.

You can see that we get syntax errors when we write some incorrect syntax. In these
examples, our logic was correct but the syntax was wrong and so we got the syntax
errors.

Python Exceptions (Logical Errors)

Exceptions are logical errors which we get when the syntax is correct and there is
something wrong with our logic. Or we can say that, apart from SyntaxError, all other
errors are called Exceptions.

Let’s look at some common Exceptions.

Python NameError

It is raised when a variable is not defined.

print(num)

Output

Traceback (most recent call last):


File "script.py", line 1, in <module>
print(num)
NameError: name 'num' is not defined

We got NameError because the variable num is not defined.

Python IndexError

It is raised when the index of a sequence (like list, tuple, etc) does not exist, or more
technically speaking, it is raised when the index is out of range.
mylist = [1, 2, 3, 4]
print(mylist[10])

Output

Traceback (most recent call last):


File "script.py", line 2, in <module>
print(mylist[10])
IndexError: list index out of range

We got IndexError on printing mylist[10] because there is no index 10 of the list


mylist.

Python KeyError

It is raised when the key of a dictionary is not found.

mydict = {1: 'a', 2: 'b', 3: 'c'}


print(mydict[4])

Output

Traceback (most recent call last):


File "script.py", line 2, in <module>
print(mydict[4])
KeyError: 4

We got KeyError on printing mydict[4] because the dictionary mydict has no key as
4.

Python IndentationError

It is raised when indentation is incorrect.

if 4 < 3:
print("4 is greater than 3")
Output

File "script.py", line 2


print("4 is greater than 3")
^
IndentationError: expected an indented block

We got IndentationError because we didn’t give indentation to the body of the if


statement. Atleast one statement should have indentation after the condition of if is
checked to mark the body of if.

Python ZeroDivisionError

It is raised when some number is divided by zero.

a = 0
b = 10/a

Output

Traceback (most recent call last):


File "script.py", line 2, in <module>
b = 10/a
ZeroDivisionError: division by zero

We got ZeroDivisionError because we divided a number 10 by 0.

Python ValueError

It is raised when the type of the argument passed to a function is incorrect.

a = int("Sam")

Output

Traceback (most recent call last):


File "script.py", line 1, in <module>
a = int("Sam")
ValueError: invalid literal for int() with base 10: 'Sam'

We know that the int() function converts the value passed to it to an integer, and it
can’t convert a string to an integer. Therefore, when we tried to convert the string
“Sam” to integer by passing it to this function , we got ValueError.

We don’t need to remember these exceptions, but it’s always good to have a rough
idea of the types of error or exceptions while debugging. To read about other built-in
exceptions in Python, go to Python Built-in Exceptions

Python Exception Handling

In the last chapter, we looked at some of the built-in Exceptions in Python. We know
that whenever an exception occurs in a program, the program gets terminated and
an error message is displayed. This behaviour is often undesirable especially when
the exception is raised due to some external factor (like due to some value entered
by a user) and not due to some mistake in the code.

For example, take the following program.

num1 = input("Please enter a number")


num2 = int(num1) # will throw exception if num1 is a string
print(num2)

Output

Please enter a number Hello

Traceback (most recent call last):


File "script.py", line 2, in <module>
num2 = int(num1)
ValueError: invalid literal for int() with base 10: '"Hello"'

We know that the int() function can’t convert a string to an integer. So if the user
enters a string which is assigned to num1, then int(num1) throws ValueError.
As you can see, this exception was raised because the user was expected to enter a
number but instead gave the wrong input. In this case the execution of the program
was stopped but wouldn’t it be nice to show the user a message and continue the
normal execution of the program?

In such scenarios, we can check if any exception is raised and simply return a
message informing the user that the input entered is incorrect without leading to the
termination of the program. This handling of exceptions is called Exception
Handling.

Now let’s see how we can handle exceptions in Python.

Python try Clause and except Clause

try and except clauses are used to handle exceptions. The piece of code which is
likely to throw an exception is placed inside the try clause. The statements which we
want to execute if the code inside the try clause throws an exception are placed
inside the except clause.
Let’s see how to use try and except clauses for handling the exception we were
getting in the first example of this chapter.
num1 = input("Please enter a number")
try:
num2 = int(num1)
print(num2)
except:
print("Please enter the correct input")
print("Outside of try-except clauses. This statement is always executed.")

Output

Please enter a number Hello


Please enter the correct input
Outside of try-except clauses. This statement is always executed.

The statements num2 = int(num1) and print(num2) are placed inside the try clause
because they may throw an exception if the user enters the wrong input. The
statement print("Please enter the correct input") is placed inside the except
clause.

If the two statements inside the try clause don't throw any exception, then the
statement inside the except clause is not executed and the rest of the program after
the except clause is executed as usual.

However, if a statement inside the try clause throws an exception, the control gets
transferred to the except clause and the statement inside the except clause is
executed. After that, the rest of the program after the except clause is executed. In
the above example, the statement num2 = int(num1) inside the try clause threw an
exception when the user entered a wrong value. Thus, without executing the second
statement inside the try clause, the control got transferred to the except clause and
"Please enter the correct input" got printed.

So, now if any exception is thrown by the program, it will be handled gracefully
without the termination of the program.

Let’s see what happens if the user gives the correct input in the above program.

num1 = input("Please enter a number")


try:
num2 = int(num1)
print(num2)
except:
print("Please enter the correct input")
print("Outside of try-except clauses. This statement is always executed.")

Output

Please enter a number 5


5
Outside of try-except clauses. This statement is always executed.

The user entered 5. So, the statements inside the try clause got executed without
throwing any exception, and therefore the statement inside the except clause didn’t
get executed.

So, the syntax of try and except clauses is shown below.

try:
# statements in try clause
except:
# statements in except clause (executed when exception thrown in try clause)

The body of the try clause constitutes of the indented statements written after try:
and the body of the except clause constitutes of the indented statements written
after except:.

The except clause must be written immediately after the try clause.

Look at another example.

num1 = input("Please enter a number")


try:
num2 = int(num1)
print(num2)
except:
pass
print("Outside of try-except clauses. This statement is always executed.")

Output

Please enter a number Hello


Outside of try-except clauses. This statement is always executed.
Here we wrote the statement pass in the except clause. Hence, if the try clause
throws an exception, the except clause will do nothing. Passing pass in
the except clause can be useful when you want to do nothing when an exception is
thrown and also don’t want the program to terminate.

Now let’s check different types of except clauses,

Python Handling an Arbitrary Exception

The except clause in the examples we saw above handles all types of exceptions.
We can also handle all types of exception by writing except Exception: instead
of except:. Let’s rewrite one of the previous examples using except Exception:.

num1 = input("Please enter a number")


try:
num2 = int(num1)
print(num2)
except Exception:
print("Please enter the correct input")
print("Outside of try-except clauses. This statement is always executed.")

Output

Please enter a number Hello


Please enter the correct input
Outside of try-except clauses. This statement is always executed.

We got the same result.

Now suppose you also want to display the exception that is thrown in the except
clause. This can be done as follows.

num1 = input("Please enter a number")


try:
num2 = int(num1)
print(num2)
except Exception as e:
print("Exception:", e)
print("Please enter the correct input")
print("Outside of try-except clauses. This statement is always executed.")

Output

Please enter a number Hello


Exception: invalid literal for int() with base 10: 'Hello'
Please enter the correct input
Outside of try-except clauses. This statement is always executed.

We wrote except Exception as e: as the definition of the except clause, which


means that if there is any error (exception) then it will be stored inside e in the except
clause. On printing e, the exception got displayed. This can be useful when we also
want to view the exception while handling it. Note that we can use any other variable
in place of e.

Python Handling a Specific Exception

In the above examples, the except clause which we wrote could handle all types of
exceptions. But, if we want to handle a specific type of exception, then we need to
give the exception type after the except keyword. For example, to handle
only ValueError, we have to define the except clause as except ValueError.

This is implemented below.

num1 = input("Please enter a number")


try:
num2 = int(num1)
print(num2)
except ValueError:
print("Please enter the correct input")
print("Outside of try-except clauses. This statement is always executed.")

Output
Please enter a number Hello
Please enter the correct input
Outside of try-except clauses. This statement is always executed.

We defined the except clause as except ValueError, which means that


our except clause will only handle ValueError exceptions and will not handle any
other type of exceptions. Since the try clause threw an exception of type ValueError,
therefore it got handled by the except clause.

This is useful when we want to handle different exceptions differently. For example,
suppose we are handling a file. In that case, we would want to execute some logic if
a file doesn’t exist and different logic if the file type is different.

Let’s try to throw some exception which is not of type ValueError from
the try clause.

num1 = input("Please enter a number")


try:
num2 = int(num1)
print(num3)
except ValueError:
print("Please enter the correct input")
print("Outside of try-except clauses. This statement is always executed.")

Output

Please enter a number 5


Traceback (most recent call last):
File "script.py", line 4, in <module>
print(num3)
NameError: name 'num3' is not defined

Inside the try clause, we are printing the variable num3 which is not defined.
Therefore, the NameError exception is thrown from the try clause. It is not handled by
the except clause because it only handles ValueError exceptions, and so our
program terminated with an error message.

Python Handling Multiple Exceptions


We looked at how to handle all types of exceptions or a specific exception by
an except clause. Handling all types of exceptions by a single except clause is not a
good idea when you want to take different actions or display different messages
when different types of exceptions are thrown. For example, you might want to ask
the user to enter a numeric value if ValueError is thrown or to enter a non-zero value
if ZeroDivisionError is thrown.

This can be done by defining multiple except clauses for a single try clause as
shown below.

try:
# statements in try clause

except ValueError:
# handles ValueError exceptions

except NameError:
# handles NameError exceptions

except IndexError:
# handles IndexError exceptions

Here we defined three except clauses, the first one handles ValueError exceptions,
the second handles NameError exceptions and the third
handles IndexError exceptions. Exceptions apart from these three types will not be
handled. Also remember that only one except clause is executed for a try clause.

Let’s see an example.

num1 = input("Please enter a number")


try:
num2 = int(num1)
num3 = 10/num2
print(num3)

except ValueError:
print("Please enter a numeric value")

except ZeroDivisionError:
print("Please enter a non-zero value")

Output

Please enter a number 0


Please enter a non-zero value

This program handles ValueError and ZeroDivisionError exceptions in separate


except clauses and displays messages accordingly. We gave 0 as input and hence
the statement num3 = 10/num2 threw the ZeroDivisionError exception. This
exception is handled by the second except clause.

We can also give an additional except clause after all the except clauses to handle
all unhandled exceptions as shown below.

try:
# statements in try clause

except ValueError:
# handles ValueError exceptions

except NameError:
# handles NameError exceptions

except IndexError:
# handles IndexError exceptions

except:
# handles all other exceptions

Here the ValueError, NameError and IndexError exceptions are handled by the first
three except clauses. The rest of the exceptions are handled by the last generic
except clause.

Look at the following example.

num1 = input("Please enter a number")


try:
num2 = int(num1)
num3 = 10/num2
print(num4)

except ValueError:
print("Please enter a numeric value")

except ZeroDivisionError:
print("Please enter a non-zero value")

except:
print("Some error occurred")

Output

Please enter a number 5


Some error occurred

Here we are printing the value of the variable num4 which is not defined. Therefore,
the NameError exception is thrown. The first two except clauses don’t
handle NameError exceptions and hence the last generic except clause handled it
and printed a message.

Now, suppose you want to perform the same task when three different types of
exceptions are thrown. One way to do so is obvious - create three different except
clauses, one for handling each of the three exceptions, and perform the task in their
respective except clauses. Another way is to create an except clause that can
handle multiple exceptions. Yes, in Python, we can also create except clauses which
handle multiple exceptions as shown below.

try:
# statements in try clause

except ValueError:
# handles ValueError exceptions

except NameError:
# handles NameError exceptions

except (IndexError, KeyError):


# handles IndexError and KeyError exceptions

except:
# handles all other exceptions

Here the third except clause handles two types of exceptions


- IndexError and KeyError. For an except clause to handle multiple exceptions, the
comma separated exception types are enclosed within parentheses ( ) and written
after the keyword except in the definition..

Let’s see an example.

num1 = input("Please enter a number")


try:
num2 = int(num1)
num3 = 10/num2
print(num4)

except ValueError:
print("Please enter a numeric value")

except ZeroDivisionError:
print("Please enter a non-zero value")

except (AttributeError, NameError):


print("Some error in the code!")

except:
print("Some error occurred")

Output

Please enter a number 5


Some error in the code!
In this example, the third except clause handles AttributeError exceptions as well
as NameError exceptions. The statement print(num4) in the try clause threw
a NameError exception which is handled by the third except clause.

So, handling exceptions is this easy.

We know that a try clause can have one or more except clauses. A try clause can
also have two other clauses - else and finally. Let’s read about them.

Python else Clause

There can be some cases when we want to execute a piece of code only when the
statements inside the try clause don’t throw an exception. We can place that code
inside the else clause,

Hence, the else clause can be thought as the opposite of except clause.
The except clause is executed if an exception occurs inside the try clause, whereas
the else clause is executed if no exception occurs inside the try clause.

try:
# statements in try clause
except:
# statements in except clause (executed when exception thrown in try clause)
else:
# statements in else clause (executed when no exception thrown in try clause)

else clause is written after all the except clauses.

Look at the following example.

num1 = input("Please enter a number")


try:
num2 = int(num1)
num3 = 10/num2

except ValueError:
print("Please enter a numeric value")
except ZeroDivisionError:
print("Please enter a non-zero value")

except:
print("Some error occurred")

else:
print(num3)

print("Outside of try-except clauses. This statement is always executed.")

Output

Please enter a number 5


2.0
Outside of try-except clauses. This statement is always executed.

The statements inside the try clause got executed without throwing any error and so
the statement inside the else clause got executed. Here we are printing the value
of num3 only when the calculations inside the try clause don’t throw any exception.

Python finally Clause

The finally clause is always executed after the execution of


the try and except clauses. If no exception is thrown inside the try clause, then
the finally clause gets executed directly after the execution of the try clause. If some
exception is thrown inside the try clause, then the finally clause gets executed after
the execution of the respective except clause. After the execution of
the finally clause, the rest of the program is executed.

We generally use the final clause to do clean-up before the execution of the rest of
the program. Cleanup can be anything like assigning a default value to some
variable, deleting something which we created inside the try or the except clause,
closing an opened file (you will learn more about files in a later chapter).

try:
# statements in try clause
except:
# statements in except clause (executed when exception thrown in try clause)
else:
# statements in else clause (executed when no exception thrown in try clause)
finally:
# statements in finally clause (always executed)

finally clause is written after all the except clauses or the else clause (if defined).

Let’s add a finally clause in the last example.

num1 = input("Please enter a number")


try:
num2 = int(num1)
num3 = 10/num2

except ValueError:
print("Please enter a numeric value")

except ZeroDivisionError:
print("Please enter a non-zero value")

except:
print("Some error occurred")

else:
print(num3)

finally:
print("finally")

print("Outside of try-except clauses. This statement is always executed.")

Output

Please enter a number 5


2.0
finally
Outside of try-except clauses. This statement is always executed.

In this example, the statement written inside the finally clause got executed after
the else clause got executed.

If there is no else clause, then the finally clause is executed after


the try or except clause depending upon whether an exception is thrown or not.

Python Raising Exceptions

Normally, exceptions are thrown when there is some problem in the code. In Python,
we can also manually raise an exception.
But why would we raise an exception on our own?

Consider a scenario where we are asking the user to enter their roll number as input.
We know that a roll number can only be a positive number. So, if the user enters a
non-positive value as their roll number, we might want to throw an exception.

There can be more such cases where the values entered by the user or some piece
of code are considered invalid for our program. In those cases, we can manually
raise an exception.

Seems interesting, right? Let’s write a program which throws an error if the user
enters a non-positive roll number.

Python Raising an Exception

try:
roll = int(input("Please enter your roll number"))
if roll <= 0:
raise ValueError()

except ValueError:
print("ValueError Exception thrown")
print("Outside of try-except clauses.")

Output

Please enter your roll number -5


ValueError Exception thrown
Outside of try-except clauses.

Inside the try clause, we are taking the input from the user. Then we are checking if
the number entered by the user is non-positive. If it is, then we are manually raising
the ValueError exception by writing raise ValueError(). We are also handling this
exception using an except clause.

Note that in the above example, we have raised an exception of type ValueError.
However, we can raise any other type of exception as well if the entered value is not
positive. We chose to raise ValueError because normally ValueError exceptions are
raised when some value is incorrect. So, it is advisable to choose the exception type
that best matches the reason you are raising it.

Similarly, an IndexError exception can be raised using raise IndexError().

We can also define our raised exception by passing values to the exception while
raising it. Let’s define the ValueError exception we raised in the previous example.

try:
roll = int(input("Please enter your roll number"))
if roll <= 0:
raise ValueError("The number entered is not positive")

except ValueError as v:
print("ValueError Exception thrown")
print(v)

print("Outside of try-except clauses.")

Output

Please enter your roll number -5


ValueError Exception thrown
The number entered is not positive
Outside of try-except clauses.

In this example, while raising the ValueError exception, we passed a string "The
number entered is not positive" to it. This passed value works as the definition of the
raised exception. Therefore, on printing the ValueError (in the form of v) inside the
except clause, this definition of the exception (the string value passed while raising
the exception) got printed.

We can raise any number of exceptions from a try clause.

Let’s take another example. Suppose we are hosting a webinar in which only the
students of the age group of 5 to 20 years are allowed. If any student who is not in
this age group tries to register, then we will throw an exception.

try:
age = int(input("Please enter your age"))
if age < 5:
raise ValueError("Not allowed! Your age is less than 5")

if age > 20:


raise ValueError("Not allowed! Your age is greater than 20")

except ValueError as v:
print("ValueError Exception thrown")
print(v)

Output

Please enter your age 26


ValueError Exception thrown
Not allowed! Your age is greater than 20

In this example, we are asking the user to enter the age. If the entered age is less
than 5, then we are throwing a ValueError exception with a definition, and if the age
is greater than 20, then we are throwing another ValueError exception with another
definition. You can run this program and check the result for different inputs.

Look at another example.


try:
roll = int(input("Please enter your roll number"))
if roll <= 0:
raise ValueError("The number entered is not positive")
print(num)

except ValueError as v:
print("ValueError Exception thrown")
print(v)

except:
print("Some error occurred")

print("Outside of try-except clauses.")

Output

Please enter your roll number 5


Some error occurred
Outside of try-except clauses.

Here we are forcefully throwing a ValueError exception if the entered number is not
positive. We are handling the ValueError exceptions by the first except clause. All
other types of exceptions are handled by the second generic except block. In this
example, we gave 5 as input and so no ValueError exception was raised, but the
statement print(num) threw a NameError exception because the variable num is not
defined. This exception was handled by the second except clause.

While raising an exception, if we are not sure about which type of exception we
should raise, then we can raise a generic exception Exception as shown below.

try:
roll = int(input("Please enter your roll number"))
if roll <= 0:
raise Exception("The number entered is not positive")

except Exception as e:
print(e)
print("Outside of try-except clauses.")

Output

Please enter your roll number -5


The number entered is not positive
Outside of try-except clauses.

You must have understood this example. We are raising a generic Exception when
the entered value is not positive.

Till now, we have been raising either a generic Exception or specific built-in
exceptions like NameError, ValueError, etc in Python. We can also create our own
exceptions.

Python Defining Our Own Exceptions

Before learning to create new exceptions, let’s see what exception classes are.

All exceptions we studied till now are built-in classes in Python.


Thus, IndexError, TypeError and all the other exceptions are built-in classes.

We also know that all classes are subclasses of the object class. The object class is
a built-in class in Python. Similarly, all the exception classes are direct or indirect
subclasses of the built-in Exception class. Thus, exceptions
like IndexError, TypeError, ValueError, etc are subclasses of the Exception class.
Also, the Exception class itself is a subclass of the object class. Let’s prove this.

print(issubclass(ValueError, Exception))
print(issubclass(Exception, object))

Output

True
True

From this example, we can see that ValueError is a subclass


of Exception and Exception is a subclass of the object class.
Now, let’s create our own exception.

# defining a new exception named MyCustomError


class MyCustomError(Exception):
pass

# raising the error


raise MyCustomError

Output

Traceback (most recent call last):


File "script.py", line 6, in <module>
raise MyCustomError __main__.MyCustomError

We created a new exception named MyCustomError which inherits the Exception


class and raised this exception using the raise keyword by writing raise
MyCustomError.

Look at another example.

# defining a new exception named MyCustomError


class MyCustomError(Exception):
pass

roll = int(input("Please enter your roll number"))


if roll <= 0:
raise MyCustomError("The number entered is not positive")

Output

Please enter your roll number -5


Traceback (most recent call last):
File "script.py", line 7, in <module>
raise MyCustomError("The number entered is not positive")
__main__.MyCustomError: The number entered is not positive

Here if the user enters a non-positive value, we are raising our exception by passing
the value "The number entered is not positive" to it which serves as its description.
You can see that the error message we get when the exception is raised is quite
similar to the one we get when some built-in exception is thrown.

If we want to create multiple custom exception classes, then it is a good practice to


create a custom base exception class and inherit all the other exception classes from
this base class. It is also a good practice to keep minimal code inside the exception
classes.

class Error(Exception):
"""Base class for all exceptions"""
pass

class PasswordSmallError(Error):
"""Raised when the input password is small"""
pass

class PasswordLargeError(Error):
"""Raised when the input password is large"""
pass

class PasswordInvalidError(Error):
"""Raised when the input password is invalid"""
pass

We created a base exception class named Error. All the other exception classes are
created as its subclass.

Now let’s see an example in which we will ask the user to enter a password. If the
password entered by the user is too short or too long, then we will throw exceptions.

# defining exceptions

class Error(Exception):
"""Base class for all exceptions"""
pass

class PasswordSmallError(Error):
"""Raised when the input password is small"""
pass

class PasswordLargeError(Error):
"""Raised when the input password is large"""
pass

try:
password = input("Enter a password")

if len(password) < 6:
raise PasswordSmallError("Password is short!")

if len(password) > 15:


raise PasswordLargeError("Password is long!")

except PasswordSmallError as ps:


print(ps)

except PasswordLargeError as pl:


print(pl)

Output

Enter a password abcd


Password is short!

Python Assert

In the last chapter, we saw how to intentionally raise an exception. Assertion is


another way to raise an exception. Before understanding why we need assertion,
let’s see how we can raise an exception using assertion.

Assertion is written using the assert keyword. It is used to check a condition. If the
condition is True, then it does nothing and the program execution continues
normally. However, if the condition is False, then it throws
the AssertionError exception and the program gets terminated.

Let’s look at an example.

num = int(input("Enter a number"))


assert num > 0
print("The entered number is", num)

Output

Enter a number 5
The entered number is 5

Here we wrote the assert statement as assert num > 0. It checks if the
condition num > 0 is True. If the condition is True, then nothing happens and the next
statement is executed. Here we gave the input as 5, thus the condition num > 0 of
the assert statement is True and so no exception was thrown.

Let’s see what would happen if we pass a negative number as input.

num = int(input("Enter a number"))


assert num > 0
print("The entered number is", num)

Output

Enter a number -5
Traceback (most recent call last):
File "script.py", line 2, in <module>
assert num > 0
AssertionError

Here we gave the input as -5 which made the condition num > 0 of the assert
statement False. Thus, the assert statement threw an AssertionError exception.

The assert statement always throws the AssertionError exception which is a built-in
exception in Python.

The condition of the assert statement must always compute to True or False.

Now that we know what assertion is, let’s discuss where it can be used.
Where is Assertion Used?

Assertions are mostly used for debugging or checking for any invalid values in a
program. Normally, it is used for debugging and testing in large programs.

Let’s take a simplified example. Suppose you own a retail shop and want to get rid of
all the products whose expiry date is less than 5 months. To do that, you can test
whether the expiry date is greater than 5 months using assertion for each product
and reject all the products for which the condition does not satisfy.

Consider another example in which you have a list of one thousand integer values.
Now suppose your program is breaking because some values in the list might not be
of type integer. In that case you can check the data type of those values using
assertion in a loop.

In testing a software, we often use assert. Suppose, we have a list of test cases
which should pass a particular function to ensure correct working of the program. In
that case we use assert and if any of the test cases is not passed, then assert throws
an error and we know that our program is not yet ready for production as it didn’t
pass all test cases.

Now, you must have got an idea of where you can use assertions. So, let's move
forward.

Using Assertion in Python

We have already seen an example in which assertion was used to check the validity
of user input. Let’s look at one more example.

blood_group = ['A+', 'A-', 'B+', 'B-', 'O+', 'O-', 'AB+', 'AB-']

bgroup = input("Enter your blood group")


assert bgroup in blood_group
print("Your blood group is", bgroup)
Output

Enter your blood group AB++


Traceback (most recent call last):
File "script.py", line 4, in <module>
assert bgroup in blood_group
AssertionError

The list blood_group has the different types of blood groups as its elements. We are
asking the user to enter the blood group. The condition of the assert
statement bgroup in blood_group checks if the value bgroup entered by the user is
present in the list blood_group. If it is present, then the condition becomes True, but
if not present, then the condition becomes False and the assert statement throws
the AssertionError.

We can also add a definition or error message to our raised AssertionError by


including the definition also in the assert statement as shown below.

blood_group = ['A+', 'A-', 'B+', 'B-', 'O+', 'O-', 'AB+', 'AB-']

bgroup = input("Enter your blood group")


assert bgroup in blood_group, "You entered an incorrect blood group!"
print("Your blood group is", bgroup)

Output

Enter your blood group AB++


Traceback (most recent call last):
File "script.py", line 4, in <module>
assert bgroup in blood_group, "You entered an incorrect blood group!"
AssertionError: You entered an incorrect blood group!

In this example, we added the description of the exception in the assert statement by
writing assert bgroup in blood_group, "You entered an incorrect blood
group!", where bgroup in blood_group is the condition to be checked and "You
entered an incorrect blood group!" is the description or the error message. In the
output, you can see the error message on giving an invalid input.

We can also handle the AssertionError using try and catch clauses like all other
exceptions.
try:
roll = int(input("Enter your roll number"))
assert roll > 0, "You entered an incorrect Roll Number!"

except AssertionError as ex:


print(ex)

except:
print("Some error occurred.")

else:
print("You entered:", roll)

In this example, in the condition of the assert statement, we are checking if the roll
number entered by the user is greater than 0. Let’s check the output for different
input values.

If the user enters 5, then the condition of the assert statement becomes True and no
exception is thrown. Therefore the statement written inside the else clause gets
executed. We will get the following output.

Enter your roll number 5


You entered: 5

If the user enters -5, then the condition of the assert statement becomes False and
the AssertionError exception is thrown. This exception gets handled by the first
except clause. We will get the following output.

Enter your roll number 5


You entered an incorrect Roll Number!

If the user enters a string like “hello”, then the statement roll = int(input("Enter
your roll number")) throws the ValueError exception which is handled by the
second except clause and the output will be as follows.

Enter your roll number hello


Some error occurred

Python Files Handling


When a program runs, the data is in the memory but when it ends or the computer
shuts down, it gets lost. To keep data permanently, we need to write it in a file.

File is used to store data. You must be using files in your electronic devices to store
many things like projects, reports or even a story.

In this topic, you will learn about reading data from a file and writing data to the file
using a Python program. Yes, we can do that in Python.

Suppose you have a file in your computer and you want to print the content of that
file on the screen. To do that first you will open the file, then read the content of the
file and then print the content. Performing such types of operations involving files is
called file handling.

To perform any read or write operation on file, we first need to open the file. Let’s
see how to open a file.

Python Opening a File

A file can be opened using the open() function. This function receives the filename
as input and returns the file object.

Suppose you have a file named example.txt in your system. Then you can open it by
writing open(“example.txt”) as shown below.

f = open("example.txt")

Here the open() function returns the file object and that file object is assigned to the
variable f. Afterwards, we will be using the file object stored in f to read or write the
content of the file example.txt.

We can also open a file by passing the exact path of the file to the open() function as
shown below.

f = open("C:/Documents/examples/example.txt")

Python File Modes


While opening a file, we can also specify why we want to open the file. This can be
done by passing a mode to the open() function. Python has the following built-in
modes which are used to specify the intention of opening a file.

Mode Description

r Opens a file for reading (read-only mode). This is the default mode.

rb Opens a file for reading binary format.

r+ Opens a file for reading and writing.

rb+ Opens a file for reading and writing in binary format.

Opens a file for writing to that file. If the file does not exist, then it creates a new file, and if the fil
w
already exists, then it overwrites the file.

wb Opens a file for writing in binary format.

w+ Opens a file for writing and reading.

wb+ Opens a file for writing and reading in binary format.


Opens a file for appending, which means the content gets appended to the end of the file. If the file
a
not exist, then it creates a new file.

ab Opens a file for appending in binary format.

a+ Opens a file for appending and reading.

ab+ Opens a file for appending and reading in binary format.

x Opens a file for exclusive creation. If the file already exists, the operation fails.

If we want to open a file to read its content, we need to pass the mode r as an
additional argument to the open() function using any one of the following two
syntaxes.

f = open("example.txt", 'r')
f = open("example.txt", mode = 'r')

The above line of code opens the example.txt file in the read mode. This means that
we will only be able to read the content from this file and won’t be able to write
anything in the file. The ‘r’ mode is also the default mode, so if we don’t pass any
mode to the function, then by default the ‘r’ mode is considered.

Similarly, if we want to open a file to write something in it, the w mode is passed. If
we want to both read and write to a file, we can pass either r+ mode or w+ mode.

f = open("example.txt") # equivalent to open("example.txt", 'r')


f = open("example.txt", 'w') # opens a file to write to it
f = open("example.txt", 'r+') # opens a file to read and write
f = open("example.txt", 'w+') # opens a file to read and write
f = open("example.txt", 'a') # opens a file to append data to it
f = open("example.txt", 'a+') # opens a file to read and append data to it
It is mandatory to pass a mode to the open() function, unless you are opening a file
just for reading its content (because ‘r’ is the default mode). For example, if you want
to write something in a file, then you have to specify that while opening the file by
passing the ‘w’ mode to the open() function.

Giving the modes rb, rb+, wb, wb+, ab and ab+ opens the file in binary mode. These
are used to open non-text files like audio files, image files, PDF or any other file
containing special non-readable characters.

If we try to open a file that doesn’t exist, the interpreter will throw FileNotFound.

f = open("xyz.txt")

Output

FileNotFoundError: [Errno 2] No such file or directory: 'xyz.txt'

In the above example, if the xyz.txt file doesn’t exist in your system, then trying to
open it throws FileNotFoundError.

Python Closing a File

After opening a file, we read the content of the file or write something into it. After
performing these file operations, it is important to close the opened file. After
performing the required operations, the opened file should be closed to free the
resources storing the file object. It means that if we don’t close the file, it will continue
using the memory.

An opened file can be closed using the close() function.

f = open("example.txt") # open the file


# do file operations
f.close() # close the file

In the above example, the opened example.txt file is closed by writing f.close().

Closing using try...finally


Suppose while performing a read or write operation, some exception is thrown. In
that case, the file would remain open and the resource storing the file object won’t be
freed.

We can prevent this by using the try...finally block. We can keep the code containing
the opening of the file and the file operations inside the try clause and the file closing
part inside the finally clause. We know that the finally clause is always executed, no
matter if an exception is thrown or not. Thus, if an exception is thrown inside the try
block, then the file will always get closed inside the finally clause.

try:
f = open("example.txt") # open the file
# do file operations
finally:
f.close() # close the file

If any exception is thrown inside the try clause in the above code,
the f.close() statement inside the finally clause will get executed, thus closing the
file.

Automatic Closing a File using with

Let’s again look at the following example.

f = open("example.txt") # open the file


# do file operations
f.close() # close the file

Here we are opening a file, performing some file operations and then closing it. The
above code can be written using the with keyword as follows.

with open("example.txt") as f:
# do file operations

In the above example involving the with keyword, the file gets automatically closed
after the indented statements inside the with statement get executed, and so this is
a good and preferred way to close files. Hence, we will be using this syntax in the
rest of this chapter.

We now know how to open and close a file. So, let’s see how to perform the read
and write file operations.

Python Reading a File

To read the data stored in a file, it must be opened first in the read ‘r’ mode. After
opening the file, the content of the file is read using the read() function.

Suppose you have a file named example.txt with the following content.

example.txt

Hello learners!
Welcome to the Python course of CodesDope.

Now if we want to print the content of this file, then we will have to first read the
content and then print it.

with open("example.txt") as f:
print(f.read())

Output

Hello learners!
Welcome to the Python course of CodesDope.

In this example, we opened the example.txt file and read its content using f.read()
and printed this read content. The content returned by f.read() is of type string.
Here we didn’t specify a mode while opening the file because read mode ‘r’ is the
default mode.

Note that the string returned by f.read() is 'Hello learners!\nWelcome to the Python
course of CodesDope.\n'. When it is printed using the print() function, ‘\n’ got
printed as a new line and we got the output as shown in the above example.
In the beginning, the cursor (or pointer) is at the start of the content of the file. After
reading the content of the file once, the position of the cursor reaches the end of the
content. So, if we now try to read the content again, we will get an empty string.

with open("example.txt") as f:
print("reading content 1st time")
print(f.read())
print("reading content 2nd time")
print(f.read())

Output

reading content 1st time


Hello learners!

Welcome to the Python course of CodesDope.

reading content 2nd time

In the above example, when we read and printed the file content once, the cursor
reached the end of the content. Now, there is no content after the current position of
the cursor. Therefore, reading the file content for the second time returned and
printed an empty string.

So, f.read() reads the whole content of the file at once. To read a specific number
of characters from the current position of the cursor in the file, we need to pass the
number of characters to be read as the argument to the read() function. For
example, f.read(5) reads the first five characters from the current position of the
cursor in the file.

Let’s see an example.

with open("example.txt") as f:
print("reading content 1st time")
print(f.read(8))
print("reading content 2nd time")
print(f.read(4))
print("reading content 3rd time")
print(f.read())
Output

reading content 1st time


Hello le
reading content 2nd time
arne
reading content 3rd time
rs!
Welcome to the Python course of CodesDope.

Initially, the cursor is placed at the start of the content of the file.
Thus, f.read(8) reads the first 8 characters ‘Hello le’ of the content. So, now the
current position of the cursor is after the 8th character in the content. After
this, f.read(4) reads the next four characters ‘arne’ in the content shifting the
position of cursor further by four characters. At last, f.read() reads the entire
content after the cursor position.

Reading a File Line By Line

Suppose our example.txt file has the following content.

example.txt

Hello learners!
Welcome to the Python course of CodesDope.
Enjoy learning.

This file has three lines of text. Let’s see the different ways we can read the file
content line by line.

We can use a for loop to read one line in each iteration of the loop.

Look at the following example.

with open("example.txt") as f:
for line in f:
print(line)

Output
Hello learners!

Welcome to the Python course of CodesDope.

Enjoy learning.

You must have understood the code. We are iterating through the lines in file content
and printing a line in each iteration. But why is there a blank line after every text line
in the output?

This is because in the first iteration of the for loop, the value of the variable line is
‘Hello learners!\n’. So, when this string is printed, ‘\n’ gets printed as a new line.
Therefore, a blank line got printed after each line. To prevent this, we can pass end =
'' as an argument to the print function to replace new lines by a null string ‘’ on
printing as shown below.

with open("example.txt") as f:
for line in f:
print(line, end = '')

Output

Hello learners!
Welcome to the Python course of CodesDope.
Enjoy learning.

We can see that no blank lines are getting printing now.

Another way to print individual lines of a file content is by using


the readline() function. This function reads one line at a time.

Let’s print the content of the example.txt file line by line.

with open("example.txt") as f:
print("reading 1st line")
print(f.readline())
print("reading 2nd line")
print(f.readline())
print("reading 3rd line")
print(f.readline())
Output

reading 1st line


Hello learners!

reading 2nd line


Welcome to the Python course of CodesDope.

reading 3rd line


Enjoy learning.

Initially, the cursor is placed at the start of the content of the file. The
first f.readline() reads the first line of the content, bringing the cursor after the first
line. Then the second f.readline() reads the second line, bringing the cursor after
the second line, and the third f.readline() printed the third line.

Note that the string returned by the first f.readline() is ‘Hello learners!\n’. When
printed, the ‘\n’ at the end got printed as a new line. Similarly, the other lines are also
printed.

We can also read the lines of a file using the readlines() function. This function
reads the lines from the current cursor position and returns a list containing the read
lines as its elements.

Look at the following example.

with open("example.txt") as f:
print(f.readlines())

Output

['Hello learners!\n', 'Welcome to the Python course of CodesDope.\n', 'Enjoy learning.']

Here, f.readline() read returns a list containing the three lines 'Hello learners!\n',
'Welcome to the Python course of CodesDope.\n' and 'Enjoy learning.' of the content
as its elements.

Look at another example.

with open("example.txt") as f:
print("reading content 1st time")
print(f.read(10))
print("reading content 2nd time")
print(f.readlines())

Output

reading content 1st time


Hello lear
reading content 2nd time
['ners!\n', 'Welcome to the Python course of CodesDope.\n', 'Enjoy learning.']

The cursor is initially placed at the start of the file content. Thus, f.read(10) reads
the first 10 characters ‘Hello lear’ of the content. This makes the current position of
the cursor after the 10th character in the content. After this, f.readlines() reads the
lines from the entire content after the current cursor position.

Now that we know the different ways we can read content of a file, let’s see how we
can write something in a file.

Python Writing to a File

To write some data into a file, the file must be opened first in write ‘ w’, append ‘a’ or
exclusive creation ‘x’ mode. After opening the file, the data can be written into it
using the write() function.

Again suppose you have a file named example.txt with the following content.

example.txt

Hello learners!
Welcome to the Python course of CodesDope.

Now, let’s write two sentences ‘This is a good course.’ and ‘Enjoy learning.’ into this
file.

with open("example.txt", 'w') as f:


f.write("This is a good course.\n")
f.write("Enjoy learning.")

Output
23
15

In this example, we opened the example.txt file in the write mode. The
first write() function writes the line "This is a good course." and the second function
writes the line "Enjoy learning." into the file. We added a ‘\n’ at the end of the string
passed to the first write() function to change the line after the string is written into
the file.

The write() function returns the number of characters in the string passed to it and
hence we got the output as 23 and 15. Let’s see what our example.txt file now looks
like after running the above program.

example.txt

This is a good course.


Enjoy learning.

You can see that the data we added has overwritten the previous data of the file.
This is because when a file is opened in the w mode, writing some data overwrites
the previous data of the file.

Now, instead of overwriting the data, if we want to append some new data at the end
of current data in a file, we need to open the file in the a mode.

Again assume our example.txt file has the following content.

example.txt

Hello learners!
Welcome to the Python course of CodesDope.

Let’s write two sentences ‘This is a good course.’ and ‘Enjoy learning.’ at the end of
the current data in the file.

with open("example.txt", 'a') as f:


f.write(" This is a good course.\n")
f.write("Enjoy learning.")

Output

23
15
In this example, we opened the example.txt file in the append mode. Therefore, the
strings added using the write() functions got appended to the content of the file.
After running this program, the example.txt looks as shown below.

example.txt

Hello learners!
Welcome to the Python course of CodesDope. This is a good course.
Enjoy learning.

So, we now know how to read the data of a file and to write some data into a file. We
also know that we open files in different modes if we want to read or write data to a
file.

Now let’s see how to read or write something from somewhere in the middle of a file
content.

Python tell() and seek()

When we read the content of a file, the interpreter starts reading from the first
character of the content because initially the cursor is before the first character.
However, there may be a case where we want to read the content from the
10th character. This can be possible if we were able to move our cursor after the 9th
character of the content. We can perform such tasks using
the tell() and seek() functions.

The tell() function returns the current position of the cursor.

with open("example.txt", 'r') as f:


print("Cursor position before reading:", f.tell())
f.read(10)
print("Cursor position after reading:", f.tell())

Output

Cursor position before reading: 0


'Hello lear'
Cursor position after reading: 10
Before reading anything from the example.txt file, f.tell() returned the current
position of the cursor as 0. Then we read the first 10 characters of the file, thus
placing the cursor after the 10th character of the content. After reading the first 10
characters, f.tell() returned the current position of the cursor as 10.

Thus, we can know the position of the cursor after any operation using tell().

The seek() function moves the cursor to the specified position. It receives an integer
as argument and places the cursor after the number of characters passed in the
argument. For example, f.seek(10) places the cursor after the 10th character of the
content of the file object stored in f.

Look at the following example to see how this function works.

with open("example.txt", 'r') as f:


print("Cursor position before moving cursor:", f.tell())
f.seek(10)
print("Cursor position after moving cursor:", f.tell())

Output

Cursor position before moving cursor: 0


10
Cursor position after moving cursor: 10

The first f.tell() returned the position of the cursor as 0. f.seek(10) moved the
cursor after the 10th character. After that, f.tell() returned the cursor position as
10. Note that the seek() function returns the value passed to it and
hence seek(10) returned 10.

So this solves our problem, We can start reading from any position in the content.

In the following example, we are reading five characters after the 8 th character of the
file content.

with open("example.txt", 'r') as f:


f.seek(8)
print(f.read(5))

Output
8
arner

f.seek(8) moved the cursor after the 8th character in the file content. After
that, f.read(5) read the next 5 characters from the current position of the cursor.
Therefore, it read from the 9th character till the 13th character.

Similarly, we can write some text at any position in the file content

Python Iterators

So far, we have used the for loop to iterate over sequences. So, the for loop is an
iterator. In this chapter, we will learn to create our own iterators. However, creating a
custom iterator is not that common in programming, but a good programmer should
know how to create one. There can be several benefits of creating our own iterator
like saving memory which we will discuss later in the chapter.

We know that sequences like lists, tuples, strings, etc can be iterated using
the for loop. Thus, the for loop is the iterator and the sequence over which it
iterates is called the iterable.

For example, we can iterate over a list using a for loop as shown below.

mylist = [1, 2, 3, 4, 5, 6]
for num in mylist:
print(num)

Output

1
2
3
4
5
6
We are well aware of how for loop is used to iterate over a sequence like a list. The
list mylist is a sequence of numbers. The for loop iterates over the list and prints
each element of the list. Here, the for loop is the iterator and the list mylist is the
iterable.

for loop is the most common iterator used in Python.

Technically speaking, an iterator is an object that iterates. It is used to return the


data of an iterable by returning one element in each iteration. Whereas,
an iterable is an object which is a collection of data.

Till now, we have used only for loop as the iterator. Let’s see how we can create a
new iterator.

Creating a Python Iterator

We can create a new iterator using the built-in iter() function in Python. This
function takes the iterable which we want to iterate over as the argument and returns
the iterator as the output.

Python also provides another built-in function next() which the iterator returned by
the iter() function uses to do iteration over the iterable.

Seems confusing? Ok, it must have been. Just understand that the iter() function is
used to create an iterator and that iterator uses the next() function to iterate over an
iterable like list, tuple, etc.

Let’s create a simple iterator.

mylist = [1, 2, 3, 4] # iterable

# creating an iterator named my_iterator


my_iterator = iter(mylist)

# iterate over the list mylist using next()


# 1st iteration
print(next(my_iterator))
# 2nd iteration
print(next(my_iterator))

# 3rd iteration
print(next(my_iterator))

# 4th iteration
print(next(my_iterator))

Output

1
2
3
4

The iter() function takes the list mylist as argument and returns an iterator. We
named the returned iterator as my_iterator (You can give any other name to the
iterator). Hence, we got our iterator.

Now, to iterate over the list, the iterator uses the next() function. When the first
time next() is called by writing next(my_iterator), it returns the first element of the
list. On calling next() the second time, it returns the second element of the list. This
goes on for each call of next().

It was this simple to create an iterator.

If in the above example, we call next() one more time, then it will throw
the StopIteration exception because there is no more element in the list to iterate
through. This is shown below.

mylist = [1, 2, 3, 4] # iterable

# creating an iterator named my_iterator


my_iterator = iter(mylist)

# iterate over the list mylist using next()


# 1st iteration
print(next(my_iterator))
# 2nd iteration
print(next(my_iterator))

# 3rd iteration
print(next(my_iterator))

# 4th iteration
print(next(my_iterator))

# this will throw error


print(next(my_iterator))

Output

1
2
3
4
Traceback (most recent call last):
File "script.py", line 20, in <module>
print(next(my_iterator))
StopIteration

So you now know how to create an iterator using the iter() function. Let’s create an
iterator to iterate over a string.

mystring = "Hey there!" # iterable

# creating an iterator named my_iterator


my_iterator = iter(mystring)

# iterate over the list mylist using next()


# 1st iteration
print(next(my_iterator))

# 2nd iteration
print(next(my_iterator))
# 3rd iteration
print(next(my_iterator))

# 4th iteration
print(next(my_iterator))

# 5th iteration
print(next(my_iterator))

Output

H
e
y

We created an iterator named my_iterator for the string mystring, and called
the next() function only five times, thus printing the first five characters of the string.

Python __iter__() and __next__()

We can also use the special functions __iter__() and __next__() in place
of iter() and next() respectively. However, using iter() and next() is preferred
due to shorter syntax.

mylist = [1, 2, 3, 4] # iterable

# creating an iterator named my_iterator


my_iterator = mylist.__iter__()

# iterate over the list mylist using next()


# 1st iteration
print(next(my_iterator))
# 2nd iteration
print(next(my_iterator))

# 3rd iteration
print(my_iterator.__next__())

# 4th iteration
print(my_iterator.__next__())

Output

1
2
3
4

From this example, you can see that


replacing next(my_iterator) by my_iterator.__iter__() and iter(my_iterator) b
y my_iterator.__next__() doesn’t alter the result.

Python Working of for Loop as Iterator

We know that the for loop can iterate over an iterable like list, tuple, etc.,
automatically (without calling next() again and again).

Internally, the for loop also uses the iter() and next() functions for creating an
iterator and for iteration. Let’s take a look at how the for loop is implemented
internally. Note that this information is just for your understanding.

Look at the following syntax which we use to implement the for loop over an
iterable.

for element in iterable:


# perform some task

This is implemented internally as shown below.

# creating an iterator from iterable


iterator_obj = iter(iterable)

# iterating over the iterable


while True:
try:
# returning the next element
element = next(iterator_obj)
# perform some task
except StopIteration:
# if StopIteration exception is thrown, break the loop
break

iterator_obj = iter(iterable) → Internally in the for loop, iter() takes the


iterable (for example, a list) as an argument and returns the iterator (let’s name it
iterator_obj).

Then an infinite while loop is run. Inside the loop, there is a try-except clause. Inside
the try clause, next(iterator_obj) returns the next element of the iterable, after
which any code which is written inside the body of our normal for loop is written.
Inside the except clause, we are breaking the loop if the StopIteration exception is
raised.

Don’t worry if you didn’t understand the above syntax clearly. The following
examples will make it clear.

First of all, let’s see an example in which a for loop prints all the elements of a list.

mylist = [1, 2, 3, 4, 5, 6]
for num in mylist:
print(num)

Output

1
2
3
4
5
6
Now, let’s look at how the for loop implements this internally.

mylist = [1, 2, 3, 4, 5, 6]

iterator_obj = iter(mylist)

while True:
try:
num = next(iterator_obj)
print(num)
except StopIteration:
break

Output

1
2
3
4
5
6

Hope you understood it now.

Python Creating a Custom Iterator

Suppose we have a list of numbers and we created an iterator for the list
using iter(). By default, whenever next() is called, it returns the next element of
the list.

Now, suppose we want that, calling next() should return the next to next element
instead of the next element. In that case, we can create our own custom iterator.

If you learn to create an iterator once, then you can create any other iterator also
because the syntax almost remains the same. So, let’s create an iterator for all the
numbers from 10 to 14.

class Range:
# constructor
def __init__(self, max):
self.max = max

# creates iterator
def __iter__(self):
self.x = 10
return self

# moves to next element


def __next__(self):
x = self.x

# if x becomes greater than max, stops iteration


if x > self.max:
raise StopIteration

# else increments the old value and returns it


self.x = x + 1;
return x

# creating an iterator
obj = Range(14)
iterator_obj = obj.__iter__()

# 1st iteration
print(iterator_obj.__next__())

# 2nd iteration
print(iterator_obj.__next__())

# 3rd iteration
print(iterator_obj.__next__())

# 4th iteration
print(iterator_obj.__next__())
# 5th iteration
print(iterator_obj.__next__())

# 6th iteration
print(iterator_obj.__next__())

Output

10
11
12
13
14
Traceback (most recent call last):
File "script.py", line 43, in <module>
print(iterator_obj.__next__())
File "script.py", line 17, in __next__
raise StopIteration
StopIteration

In this example, we created a class Range with the


methods __iter__() and __next__(). We will be implementing the desired logic
of iter() and next() inside these functions.

obj = Range(14) → While creating the object obj of the Range class, we passed 14
to the constructor, thus making its parameter max equal to 14. Therefore, the attribute
max became equal to 14.

iterator_obj = obj.__iter__() → The object obj called the method __iter__() of


the class. Inside the __iter__() method, an object whose attribute x is initialized to
10 is returned. This returned object having the attributes x (= 10) and max (= 14) is
assigned to iterator_obj. This is our iterator object.

iterator_obj.__next__() → The object iterator_obj called the


method __next__() of the class. Inside the __next__() method, it is checked if the
value of the attribute x is less than that of the attribute max. If x is less than max, then
the value of x is incremented by 1 and returned.
In the first iteration, x is 10 and max is 14. Therefore, x is incremented to 11 and
returned and printed.

In the second iteration, x is 11 and max is 14. Therefore, again x is incremented to 12


and returned.

Going on, when the value of x becomes 15, x becomes greater than 14. In that case,
the StopIteration exception is thrown.

So, you can see that the above example behaves similar to how an iterator behaves.
In the Range class, the __iter__() method creates and returns an iterator object
with the desired attributes. Similarly, the __next__() method returns the next
element based on the desired conditions.

It is fine if you are not able to write the code to create your own custom connector
because it is not required much in Python. However, it is important that you
understand the flow.

We can also use the for loop to create the iterator object and to iterate as shown
below.

class Range:
# constructor
def __init__(self, max):
self.max = max

# creates iterator
def __iter__(self):
self.x = 10
return self

# moves to next element


def __next__(self):
x = self.x

# if x becomes greater than max, stops iteration


if x > self.max:
raise StopIteration
# else increments the old value and returns it
self.x = x + 1;
return x

# creating an iterator
obj = Range(14)

for num in obj:


print(num)

Output

10
11
12
13
14

Do you remember that in the internal implementation of the for loop, the iterator
object calls the __iter__() and __next__() functions. Yes, this is how these
methods got called when using the for loop in the above example.

Now let’s create another iterator for all the even numbers from 10 to 20 (included).

class Range:
# constructor
def __init__(self, max):
self.max = max

# creates iterator
def __iter__(self):
self.x = 10
return self

# moves to next element


def __next__(self):
x = self.x
# if x becomes greater than max, stops iteration
if x > self.max:
raise StopIteration

# else increments the old value and returns it


self.x = x + 2;
return x

# creating an iterator
obj = Range(20)

for num in obj:


print(num)

Output

10
12
14
16
18
20

You must have understood the changes we did here. So, as we said, the syntax of
the class remains almost the same when creating different custom iterators.

Python Infinite Iterator

We can also create iterators that never get exhausted. This means that we will never
get the StopIteration exception while iterating. This can be done by not raising
the StopIteration exception when creating a custom iterator.

Let’s quickly create an infinite iterator for all the even positive numbers.

class Even:
# creates iterator
def __iter__(self):
self.x = 2
return self

# moves to next element


def __next__(self):
x = self.x

# else increments the old value and returns it


self.x = x + 2;
return x

# creating an iterator
obj = Even()
iterator_obj = obj.__iter__()

# 1st iteration
print(iterator_obj.__next__())

# 2nd iteration
print(iterator_obj.__next__())

# 3rd iteration
print(iterator_obj.__next__())

Output

2
4
6

In this example, we will always get the next element whenever we call next(). In the
class Even, we are not raising the StopIteration exception and we also removed
the constructor because we no longer need the max attribute

Python String Formatting


We have already learned about strings. In this section, we will go through some
formatting techniques to make our string more presentable.

Look at the following example.

# taking input from user


amount = int(input("Enter the amount"))

# displaying output
print("Amount is $", amount)

Output

Enter the amount45


Amount is $ 45
In the first statement, we are taking an input from the user and assigning it to the
variable amount. In the second statement, we are printing a string. What if we don’t
want that space between $ and 45 in the output?

This is where the need for formatting a string arises. There can be other such
scenarios where we will need some formatting to obtain the desired string.

So, let’s look at the different ways we can format strings.

Python Modulo Operator (%)

Using % is the old way of formatting.

We use argument specifiers like %s, %d, etc., in strings which are then replaced by
some value. Look at the following example.

some_string = "Hello World"


formatted_string = "The computer says %s" % some_string

# displaying output
print(formatted_string)
Output

The computer says Hello World

Here, the argument specifier %s in the string "The computer says %s" got replaced
by the value of the variable some_string, thus making our formatted string as "The
computer says Hello World". Note that we use the argument specifier %s when we
want to replace it by a string.

As we used %s for string, we can use %d for integer. Let’s look at some argument
specifiers before looking at more examples.

As stated above, using modulo operator (%) is an old way. There are many simpler
ways explained later in this chapter.

Argument Specifier Gets replaced by

%s String

%d Integer

%f Floating point number

%.f Floating point number with specified number of digits after decimal

%c Character

%x Hex representation of Integers (lowercase)


%X Hex representation of Integers (uppercase)

print("%s" % "Python programming")

Output

Python programming

In this example, %s is an argument specifier which got replaced by the string "Python
programming".

Let’s rewrite the code of the first example of this chapter solving the problem we
were facing.

# taking input from user


amount = int(input("Enter the amount"))

# displaying output
print("Amount is $%d" % amount)

Output

Enter the amount45


Amount is $45

Now the extra space between $ and 45 is no more. We used %d as the specifier
because amount is an integer.

If multiple values have to be printed in a single print() function, then for each value
we need to give a separate argument specifier in the string which is to be printed as
the output.

print("My name is %s %s and my age is %d" % ("John", "Doe", 45))

Output

My name is John Doe and my age is 45

Notice that the values are enclosed within parentheses ( ) and separated by
commas. The order of writing the values is also the same as that of the argument
specifiers in the string which is printed. Therefore, the first %s got replaced by “John”,
second %s by “Doe” and %d by 45.

This formatting method can also be used to print a specified number of digits after
the decimal of a floating point value.

num = 2.333333333
print("%.2f" % num)

Output

2.33

Python String format() method

Strings can also be formatted using the str.format() function. We use { } for a
placeholder in the string. The placeholder { } in the string is replaced by the value
passed to the format() function. This will become clear from an example.
print("Hello {}".format("John"))

Output

Hello John

In this example, format() function is applied to the “Hello {}” string. The
placeholder { } in the string is replaced by the value “John” passed to
the format() function, thus making the string as “Hello John”.

Let’s again rewrite the first example of this chapter using this method.

# taking input from user


amount = int(input("Enter the amount"))

# displaying output
print("Amount is ${}".format(amount))

Output

Enter the amount45


Amount is $45

Passing Multiple Arguments to Python format()

If multiple values need to be substituted in a string, then we pass the values


separated by commas to the format() function and add the corresponding
placeholders { } in the output string. This will become more clear from the following
example.

print("My name is {} {} and my age is {}".format("John", "Doe", 45))

Output

My name is John Doe and my age is 45

The first placeholder got replaced by the first value passed to the format() function.
Similarly, the second and third placeholders got replaced by the second and third
values passed respectively.
Passing Positional Arguments to Python format()

In case of multiple values being substituted in a string, to change the order in which
the values appear in the string, give the index numbers of the values in the
placeholders { }. The index of the values start from 0.

print("My name is {1} {2} and my age is {0}".format(45, "John", "Doe"))

Output

My name is John Doe and my age is 45

Based on the order of the values passed to the format() function, the index of 45 is
0, “John” is 1 and “Doe” is 2. Therefore, the placeholders {0}, {1} and {2} got
replaced by 45, “John” and “Doe” respectively.

Index starts from 0 and not 1.

Passing Keyword Arguments to Python format()

Instead of identifying values by their indices, we can also use some names to identify
values in placeholders.

print("My name is {fname} {lname} and my age is {age}".format(age = 45, fname = "J
ohn", lname = "Doe"))

Output

My name is John Doe and my age is 45

The names age, fname and lname are assigned the values 45, “John” and “Doe”
respectively. Therefore, the placeholders {age}, {fname} and {lname} got replaced
by 45, “John” and “Doe” respectively.

Hybrid of Positional and Keyword Arguments to Python


format()
When using a mix of positional and keyword arguments, we need to make sure that
the keyword arguments are always passed at the end.

print("My name is {fname} {lname} and my age is {0}".format(45, fname = "John", ln


ame = "Doe"))

Output

My name is John Doe and my age is 45

Here, 45 is a positional argument and fname = "John" and lname = "Doe" are
keyword arguments. Notice that the keyword arguments are written after the
positional argument.

Let’s see what would happen if we write a keyword argument before a positional
argument.

print("My name is {fname} {lname} and my age is {0}".format(fname = "John", 45, ln


ame = "Doe"))

Output

File "<stdin>", line 1


SyntaxError: positional argument follows keyword argument

As expected, we got an error.

Using Argument Specifiers with Python format()

Using format() also, we can use the same argument specifiers shown in the
previous modulo operator (%) method. The argument specifier is written inside the
placeholder { } following a colon : instead of %.

Look at the following examples.

print("Hello {:s}".format("John"))

Output

Hello John

print("My name is {1:s} {2:s} and my age is {0:d}".format(45, "John", "Doe"))


Output

My name is John Doe and my age is 45

You must have understood these examples. Notice that %s and %d in the previous
formatting method have been replaced by :s and :d here. However, using the
argument specifiers :s and :d was not necessary in these examples.

We can use argument specifiers to round off a floating point number to a specified
number of digits after decimal as shown below.

num = 2.333333333
print("This number is rounded off to 2 digits after decimal - {:.2f}".format(num))

Output

This number is rounded off to 2 digits after decimal - 2.33

Again, %.2f has been replaced by :.2f in this example.

Python Formatted String Literal (f-String)

F-strings were introduced in Python version 3.6. These are quite similar to strings in
Python.

Syntax of f-string

f'str'
Or
f"str"
Or
f'''str'''

So, f-string for the string "Hello John" will be f"Hello John".

print("Hello John")
print(f"Hello John")
Output

Hello John
Hello John

As you can see, the f-string displayed the same result as the other string.

F-strings can also contain braces { }. The value, variable or expression written
within the braces { } gets evaluated and replaces the braces in the string.

name = "John"
print(f"Hello {name}")

Output

Hello John

Here, the value “John” of the variable name replaced the braces in the f-string.

Let’s solve the problem we were facing in the first example of this chapter using this
method.

# taking input from user


amount = int(input("Enter the amount"))

# displaying output
print(f"Amount is ${amount}")

Output

Enter the amount45


Amount is $45

Look at some more examples.

fname, lname, age = "John", "Doe", 45


print(f"My name is {fname} {lname} and my age is {age}")

Output

My name is John Doe and my age is 45

num1, num2 = 1, 2
print(f"Sum of {num1} and {num2} is {num1 + num2}")

Output

Sum of 1 and 2 is 3

Python Lambda Functions

In Python, we can create functions known as anonymous functions which don't have
a name. A lambda function is a type of anonymous function defined using
the lambda keyword.

We know that a regular function is defined using the def keyword. A lambda function
is defined using the lambda keyword and hence got its name.
But why would we ever use a lambda function?

Suppose you have created a function having a single statement in its body and that
function is called at only one place in your program. In this situation, it doesn’t make
much sense to create such short functions for one time use. This is where we can
use lambda function in place of regular function. We can directly define the lambda
function at the place where it is required instead of defining a function separately and
then calling it. This will make the code more readable.

Now the question is why would I ever need a function if I need it only one place and
it contains only a single statement? There may be cases where a different function
needs a function as their input. In those cases, a lambda function would be helpful.

Let’s see what lambda functions are and then you will understand what we are
talking about.

We will discuss more use cases and limitations of lambda functions later.

Using lambda Functions in Python

Before learning to create lambda functions, look at the following function.


def identity(x):
return x + 1

The function identity() has just one statement and it simply returns the parameter
that it receives after incrementing it by 1.

This function can be written in the form of a lambda function as shown below.

lambda x: x + 1

This function created using the keyword lambda doesn’t have a name and is called a
lambda function. It has one argument x that is passed to the function. It has one
expression x + 1 which is evaluated and returned. Thus, this lambda function will
receive an argument x, add 1 to it and then return the result.

Now let’s look at the syntax of a lambda function.

Syntax of lambda function

lambda arguments: expression

lambda is a keyword which is used to define lambda functions.

arguments are the same arguments which we pass to regular functions. There can
be any number of arguments in lambda functions.

expression is some expression which is evaluated and returned. There can be only
one expression in a lambda function.

Now let’s look at some examples of lambda functions.

Examples of lambda function

The following example has a function square() which takes a number and returns
the square of that number.

Python3

def square(x):
return x**2

print(square(3))

Output

Now suppose this square() function is called at only one place in the program. Then
instead of defining and calling it, we can directly define a lambda function at the
place where it is called. Let’s see how.

print((lambda x: x**2)(3))

Output

In this example, we defined a lambda function, passed a value to it and printed the
value returned by the function.

lambda x: x**2 - In this function, x is the argument and x**2 is the expression.
The function receives the argument x, evaluates the expression x**2 and then
returns the result of the evaluation.

(3) - The value 3 is passed to the function, or we can say that 3 is the argument
passed to the function. The values passed to a lambda function are enclosed within
parentheses ( ) and written after the lambda function definition.

Did you notice that the value of the expression is getting returned even without using
the return keyword? In lambda functions, the value of the expression always gets
returned, so make sure to write the expressions accordingly.

We can also assign the lambda function to a variable so that we can use it anywhere
by directly passing the variable name.

square = lambda x: x**2

print(square(3))

Output

9
In this example, the same lambda function is created and assigned to a
variable square. The value 3 is passed to the lambda function by writing square(3).
(square(3) is the same as writing (lambda x: x**2)(3))

Look at another example in which a lambda function takes two arguments and
returns the sum of the arguments.

print(( lambda x, y: x + y)(3, 2))

Output

The lambda function takes two arguments x and y and then returns the sum of the
arguments. Two values 3 and 2 are passed to the lambda function by writing (3,
2) after the function definition. The first value 3 got assigned to x and the second
value 2 got assigned to y.

In the next example, the lambda function created is assigned to a variable sum.

sum = lambda x, y: x + y

print(sum(3, 2))

Output

lambda Function with no argument

Yes, we can also define lambda functions with no argument.

dummy = lambda: 10

print(dummy()) # prints 10

Output

10

The lambda function dummy takes no argument and returns 10.


lambda Function with default argument

We can pass positional, keyword and default arguments to lambda functions.

sum = lambda x, y=3: x + y

print(sum(3))
print(sum(3, 2))

Output

6
5

The lambda function sum takes one positional argument x and one default
argument y. We learned about positional and default arguments in this chapter.

When to Use lambda Functions

Lambda functions are easier to create and get executed somewhat faster than
regular functions. Therefore, lambda functions can be used instead of regular
functions when the functions have only one expression and are of less complexity.
This will be helpful to prevent the situation where separate functions are created for
one expression long code, making the code more readable.

Lambda functions can also be used when a function (having only one expression) is
passed to another function as its parameter. For example, consider a case where a
function named foo() takes another function func() as its parameter,
where func() returns True if the value passed to it is greater than 10, and False
otherwise.

# defining function func


def func(x):
'''Returns True if x > 10, otherwise returns False'''
return x > 10
def foo(y):
'''Do something here'''

foo(func(12))

Here, instead of declaring a separate function func() and calling it for passing it as
an argument to the foo() function, we can directly pass a lambda function as the
argument as shown below.

def foo(y):
'''Do something here'''

foo((lambda x: x > 10)(12))

As you can see, this made the code shorter and more clean.

Therefore, lambda functions should be used when the function logic is less complex
and can be reduced to a single expression. These are used when the function is not
called frequently. In all other scenarios, it is better to use regular functions.

Lambda functions can also be used with built-in functions in Python


like filter() and map().

Lambda Function with filter()

The filter() function is used to filter an iterable like list, tuple, dictionary, set, range,
etc., based on some condition. For example, it can be used if you want to filter out
(remove) all the odd elements of a list.

The filter() function takes two parameters - a function and an iterable.

The function gets called once with each element of the iterable as argument.
The filter() function returns only those elements of the iterable for which
the function returns True.

To understand this, take the same example in which we have a list and want to keep
all the even elements in the list and remove all the odd elements. For that, we will
pass a function which returns True if the value passed to it is even and False if it is
odd as the first argument and the list to be filtered as the second argument to
the filter() function.

Examples

Let’s take an example in which the filter() function filters out the odd elements of a
list mylist.

mylist = [5, 7, 8, 10, 14, 15, 20]

def even(num):
if num % 2 == 0:
return True
else:
return False

new_list = list(filter(even, mylist))


print("Filtered list:", new_list)

Output

Filtered list: [8, 10, 14, 20]

We passed a function even() and a list mylist to the filter() function.


The even() function returns True if the value passed to it is even, and returns False
otherwise. Internally, each element of the list mylist is passed to the even() function
and only those elements for which the function returns True are returned by
the filter() function.

Finally, the list() function creates a list with all the even elements returned by
the filter() function as its elements.

We can pass a lambda function instead of the regular function to make the code
more readable and short. The code for the same is given below.

mylist = [5, 7, 8, 10, 14, 15, 20]


new_list = list(filter(lambda num: num % 2 == 0, mylist))
print("Filtered list:", new_list)

Output

Filtered list: [8, 10, 14, 20]

In the lambda function, the expression num % 2 == 0 returns True if num is divisible
by 2, otherwise it returns False.

Let’s see one more example in which we will filter out all the odd numbers from 1 to
10 (included).

myrange = range(1, 11)

new_list = list(filter(lambda num: num % 2 == 0, myrange))


print("Filtered list:", new_list)

Output

Filtered list: [2, 4, 6, 8, 10]

This example is similar to the previous example, with the difference that instead of
list, we have passed a range of numbers from 1 to 10 to be filtered.

In the above two examples, instead of defining a new function and calling it inside
the filter() function, we have defined a lambda function there itself. This will
prevent us from defining and calling a new function every time a filter() function is
defined.

Lambda Function with map()

The map() function is used to modify each element of an iterable like list, tuple,
dictionary, set, range, etc. For example, it can be used if you want to increase the
value of all the elements of a list by 1.

The map() function takes two parameters - a function and an iterable.


The function gets called once with each element of the iterable as an argument and
returns the modified element. The map() function returns the iterable having the
modified elements.

For example, consider a case when we want to increase the value of each element
of a list by 1. For that, we will pass a function which adds 1 to the value passed to it
and returns the incremented value as the first argument and the list to be modified as
the second argument to the map() function.

Examples

In the following example, each element of the list mylist is multiplied by 2.

mylist = [5, 7, 8, 10, 14, 15, 20]

def multiply(num):
return num * 2

new_list = list(map(multiply, mylist))


print("Modified list:", new_list)

Output

Modified list: [10, 14, 16, 20, 28, 30, 40]

We passed a function multiply() and the list mylist to the map() function. Internally,
the function multiply() takes each element of the list mylist as an argument and
returns the element after multiplying it with 2. Finally, the map() function returns the
iterable with the modified elements.

Finally, the list() function converts the iterable returned by map() to list.

In the next example, the function multiply() is replaced by a lambda function.

mylist = [5, 7, 8, 10, 14, 15, 20]

new_list = list(map(lambda num: num * 2, mylist))


print("Modified list:", new_list)
Output

Modified list: [10, 14, 16, 20, 28, 30, 40]

Using map() with Multiple Iterables

Suppose a function passed to map() takes two iterable arguments. In that case, we
need to pass those iterables separated by comma to map() as well.

list1 = [1, 2, 3, 4, 5]
list2 = [6, 7, 8, 9, 10]

def add(num1, num2):


return num1 + num2

new_list = list(map(add, list1, list2))


print("Modified list:", new_list)

Output

Modified list: [7, 9, 11, 13, 15]

In this example, the function add() takes one element of list1 and one element
of list2, adds both the elements and returns the result.

First, the function takes the first elements of both the lists, adds them and returns the
result. After that, it takes the second elements of both the lists to add them and
return the result. This continues till all the elements of the lists are added.

This example is rewritten using a lambda function below.

list1 = [1, 2, 3, 4, 5]
list2 = [6, 7, 8, 9, 10]

new_list = list(map(lambda num1, num2: num1 + num2, list1, list2))


print("Modified list:", new_list)

Output
Modified list: [7, 9, 11, 13, 15]

The lambda function defined here takes two arguments num1 and num2 and returns
the result of num1 + num2.

Python Nested Functions

We can do a lot with functions like passing a function as an argument to another


function, calling a function from another function, etc. In Python, we can also create
a function inside another function.

A function which is created inside another function is called a nested function or


an inner function. In this chapter, we will read about nested functions and their
significance.

Defining a Nested Function

A nested function is defined by simply creating it using the def keyword inside
another function.

Here is an example.

def outer(): # outer function


print("This is outer function")

def inner():
print("This is inner function")

inner() # calling inner function

outer() # calling outer function

Output
This is outer function
This is inner function

We defined a function named inner() inside another function named outer().


Thus, inner() is a nested function.

When the outer() function is called, its body is executed. Inside its body,
the inner() function is defined and then called. Thus, we first called
the outer() function which in turn called the inner() function.

Note that an inner function is always called from inside the function in which it is
defined. Thus, to call the inner function, we need to call the outer function.

A nested function can access the variables defined in the function in which it is
created. This is demonstrated in the following example.

def outer(): # outer function


x = 10

def inner():
print("Inside inner func", x)

inner() # calling inner function


print("Inside outer func", x)

outer() # calling outer function

Output

Inside inner func 10


Inside outer func 10

In this example, we defined a variable x having a value of 10 in the outer() function.


When we printed the value of x in the nested function, it got printed. This means that
the nested function can access the variable x defined in its parent function.

So, from the above examples you must have understood that nested functions are
just normal functions which are defined and called inside some other function.

Let’s see one more example.

def find_power(num): # outer function


def power(n):
return num ** n

return power(2) # calling inner function and returning the value returned by
it

print(find_power(10)) # calling outer function

Output

100

This was a straight forward example. The find_power() function is called by passing
10 as argument, making its parameter num equal to 10. Inside
the find_power() function, the nested function power() is called by passing 2 as
argument, making the parameter n of the power() function equal to 2. Inside
the power() function, num (10) raised to the power n (2) is returned. Finally, this
value returned by the power() function is returned by the find_power() function.

Significance of Nested Functions

There can be many reasons to use nested functions. Let’s see some of them.

Nested functions can serve as helper functions of the function in which they are
defined. Let’s see an example in which a nested function serves as a helper function
for its parent function.

def print_even(lst):

def find_even(num):
if num % 2 == 0:
return True
else:
return False

new_list = []
for num in lst:
if find_even(num) == True:
new_list.append(num)

print("Final list:", new_list)

mylist = [1, 2, 4, 5, 6, 7, 10, 11, 12]


print_even(mylist)

Output

Final list: [2, 4, 6, 10, 12]

Here the function print_even() receives a list as the argument and prints a new list
containing all the even elements of the list received. You must have understood the
rest of the program.

Inside the print_even() function, we have a nested function find_even() which


receives a number as the argument and returns True if the passed number is even,
else returns False. In this program, the nested function thus works as a helper
function which just checks if the passed number is even and is not affected by any
other code or variable inside the outer print_even() function.

Another use case of nested functions can be seen from the following example.

def get_factorial(num):

def factorial(num):
if num == 0 or num == 1:
return 1
else:
return num * factorial(num - 1)

if not isinstance(num, int):


raise TypeError("Failed! The value must be a number")

if num < 0:
raise ValueError("Failed! The number must be non-negative")
return factorial(num)

print(get_factorial(5))

Output

120

In the above example, the get_factorial() function takes a number as argument


and returns the factorial of that number. Inside the get_factorial() function, we are
doing some error handling, in which we are raising the TypeError exception if the
passed number is not an integer and the ValueError exception is the passed
number is less than 0 (You can learn about raising exceptions from this chapter). If
no exception is thrown in both the checks, then the nested function
named factorial() is called which returns the factorial of the number.

Now suppose if the code for both error handling and factorial calculation was written
in the get_factorial() function without using the nested function factorial(), the
complexity of the code inside get_factorial() would have increased.

So by now, you must have got an idea of when you can use nested functions to
reduce the complexity of your functions. Let’s look at some more cases when nested
functions can be used.

Sometimes we might want to prevent some function from being directly accessed
from anywhere in your program. In that case, we can put that function (say f2()) as
a nested function inside another function (say f1()). We know that a nested function
can’t be accessed outside the function in which it is defined, and hence we won’t be
able to access that function from other parts of the program anymore. In order to call
the nested function f2(), we first will have to call its parent function f1() which will
call f2() inside it. This hiding of some data or code (function in our case) is known
as encapsulation.
But why would we encapsulate a function?

To answer that, let’s take a use case where we want to predict if a company will get
profit or loss. Suppose we have a function which contains a logic that calculates the
expected profit of a company. This logic might contain some information which we
don’t want anyone to know. If we don’t want any other part of the program to directly
access the logic in this function, we can put it as a nested function inside another
function. Look at the following function structure.

def profit_or_loss():

def get_profit():
# calculates profit
return profit

if profit > 0:
return "Profit"
else:
return "Loss"

profit_or_loss()

Suppose get_profit() is the nested function with the logic that calculates and
returns the expected profit, and profit_or_loss() is the function inside which it is
encapsulated. Now, if the user wants to know if there will be profit or loss, then
he/she will call the profit_or_loss() function. This function will call
the get_profit() function to get the profit, and will return “Success” if the profit is
positive, else returns “Loss”. In this way, no other part of the program is able to
access the nested get_profit() function.

Nested functions are also used for creating closures. We will read about closures
in Closures.

Now that you understand what nested functions are and where to use them, start
including them in your programs wherever necessary.

Python Scope of Variables

Till now, we have used variables in most of our programs. However, there are cases
where a variable declared in one part of a program can’t be accessed in some other
part of the program. We will look at those cases in this chapter.
Scope is a region of a program. Scope of a variable is the region in a program
where that variable can be accessed. For example, suppose a variable x can be
accessed only inside a particular function, then the scope of x is inside that function.

Variables are thus of three types depending on the region where these are created
and used.

Python Local Variables

Variables that are declared inside a function can only be used within that function.
Such variables which are declared inside a function are called local variables of that
function and are said to have local scope. Therefore, local variables can only be
used within the function in which they are declared.

Let's take one example to see that local variables belong to the function in which
they are declared.

def func1():
x = 10
print(x)

def func2():
x = 20
print(x)

func1()
func2()

Output

10
20

In the function func1, we declared a variable x and assigned it a value 10. This
variable is in the body of the function func1 and thus gets destroyed when the body
of the function ends. So, when we call func1, 10 gets printed.
We declared another variable x in the function func2 and gave it a value 20. This
variable also gets destroyed as the body of func2 ends and has no relation with the
variable x of func1.

So, the two variables are independent of each other and are limited to only the
function in which they are declared. The scope of the variable x is within the
function func1 and that of y is within the function func2.

If we try to access a local variable outside its scope, an error will be thrown.

def func1():
x = 10

func1()
print(x)

Output

Traceback (most recent call last):


File "<stdin>", line 5, in
NameError: name 'x' is not defined

The variable x is a local variable which is created inside the func1 function and
therefore can be accessed only within the function. We got the error because we
tried to print the variable outside its local scope.

Python Global Variables

Variables that are defined outside of all the functions and are accessible throughout
the program are called global variables and are said to have global scope. Once
declared, these can be accessed by any function in the program.

An example of a global variable is given below.

x = 10 # global variable

def func():
print("Inside func", x)
func()
print("Outside func", x)

Output

Inside func 10
Outside func 10

Here, x is a global variable because it is declared outside of the function. Thus unlike
local variables which can only be used in the function in which they are
declared, x can be used throughout the program and can be used in all the functions
in the program.

Let’s see what happens when we change the value of a global variable inside a
function.

x = 10 # global variable

def func():
x = x + 1
print("Inside func", x)

func()
print("Outside func", x)

Output

Traceback (most recent call last):


File "script.py", line 7, in <module>
func()
File "script.py", line 4, in func
x=x+1
UnboundLocalError: local variable 'x' referenced before assignment

We got the UnboundLocalError error because Python considers x in x + 1 as a local


variable and we have not assigned any value to this local variable inside the
function.
So, to change the value of a global variable inside a function, we first need to declare
that variable as global using the global keyword.

x = 10 # global variable

def func():
global x # declaring that x is global variable
x = x + 1
print("Inside func", x)

func()
print("Outside func", x)

Output

Inside func 11
Outside func 11

Here, writing global x inside the function tells Python that x is a global variable. Thus,
incrementing the value of x by 1 inside the function incremented its value to 11.

Let’s see an example where both local and global variables have the same name.

x = 10 # global variable

def func():
x = 20 # local variable
print("Inside func", x)

func()
print("Outside func", x)

Output

Inside func 20
Outside func 10

In this example, we used the same name x for local and global variables. The
variable x declared inside the function is a local variable and has the value 20,
whereas the variable x declared outside the function is a global variable and has a
value 10.

Python Nonlocal Variables

Nonlocal variables are used in nested functions.

Suppose a function named outer has a nested function named inner. Then a variable
x declared inside the outer function is not a local variable for the inner function. In
this case, this variable x is said to be a nonlocal variable for the inner function.

To understand how to access nonlocal variables, look at the following example.

def outer():
x = 10 # local variable
def inner():
print("Inside inner func", x)
inner()
print("Inside outer func", x)

outer()

Output

Inside inner func 10


Inside outer func 10

A function named outer has a nested function named inner which is called inside
the outer function. A variable x is defined with a value 10 inside the outer function.
Its value is printed inside the outer and the inner functions.

Now, let’s try to change the value of the variable x defined inside the outer function
in the inner function.

def outer():
x = 10 # local variable
def inner():
x = x + 1
print("Inside inner func", x)
inner()
print("Inside outer func", x)

outer()

Output

Traceback (most recent call last): File "script.py", line 9, in <module>


outer()
File "script.py", line 6, in outer
inner()
File "script.py", line 4, in inner
x=x+1
UnboundLocalError: local variable 'x' referenced before assignment

We got the UnboundLocalError error because Python considers x in x + 1 as a local


variable of the inner function but it is not assigned any value inside the inner
function. In other words, the variable x is local to the outer function but not local to
the inner function.

To remove the error and to be able to change the value of the local variable of the
outer function inside the inner function, we first need to declare that variable as non
local using the nonlocal keyword.

Yes, this is similar to using the global keyword.

def outer():
x = 10 # local variable
def inner():
nonlocal x # declaring that x is a non local variable
x = x + 1
print("Inside inner func", x)
inner()
print("Inside outer func", x)

outer()

Output
Inside inner func 11
Inside outer func 11

Writing nonlocal x inside the inner function tells Python that x is not local to
the inner function. Thus, incrementing the value of x by 1 inside the inner function
incremented its value to 11

Python Closures

You already know what nested functions are. Closures can be viewed as the most
useful application of nested functions. Before starting with closures, let’s talk a bit
about nested functions.

We know that a nested function is a function which is defined inside another function.
Let’s look at a simple program having a nested function.

def outer(x):
def inner():
print(x)

inner()

outer("Hey there!")

Output

Hey there!

The function inner() is defined inside the function outer(), and so inner() is a
nested function. We called outer() by passing "Hey there!" as the argument. Thus,
its parameter x got the value "Hey there!". Since a nested function can access the
variable defined in the function in which it is created, we printed the value of x inside
the nested function inner().

Look at another example.

def outer(x):
def inner():
print("This is the inner function")
return x

return inner()

print(outer("Hey there!"))

Output

This is the inner function


Hey there!

The statement return inner() inside the outer() function calls the inner() function
and returns the value returned by the inner() function. Inside the inner() function,
the value of x is returned.

So this was just a recap of nested functions. Let’s now talk about closures.

Closures in Python

In the last example, the outer() function returned the value returned by the
nested inner() function. What if you want to return the entire functionality of
the inner() function from the outer() function?

Yes, we can do so by replacing return inner() by return inner in


the outer() function. return inner() will call the inner() function and then return
the value returned by the inner() function, whereas return inner will return the entire
inner() function. This is demonstrated in the following example.

def outer(x):
def inner():
print("This is the inner function")
print(x)

return inner # returning inner function

func = outer("Hey there!")


print(type(func))
func()

Output

<class 'function'>
This is the inner function
Hey there!

When the outer() function is called with the argument "Hey there!", it returns
the inner() function instead of calling it. The returned inner() function is assigned
to the variable func. Thus, func stores the inner() function and hence
printing type(func) displayed <class 'function'>. Therefore, func now has all the
functionalities of the inner() function and thus behaves as a function.

On calling the function func(), the string "This is the inner function" and the value
of x ("Hey there!") got printed. We got the same output which we would have got on
calling inner(). But wait, x was a variable defined in the outer() function but the
outer function has already finished executing, then how does func() remember the
value of x?

This is exactly what a closure does. A closure is a technique that binds (attaches)
some data to the code. When the outer() function returned the inner() function, it
attached the value of the variable x with the code of the inner() function and then
returned the inner() function. Thus, we have a closure in the above program.

This value attached to the code is remembered even when the function in which it
was defined is no longer executing. Therefore, on calling func(), the value of x got
printed even when the outer function was not getting executed, because the value of
x is attached to the code returned by the outer() function and assigned to func.

Hope you understood the concept of closures. Summing up, a closure is a technique
in which a function returns its nested function after attaching some data (value of x in
the above example) to the code of the nested function. This returned function is
assigned to some variable (func in the above example) which can be used to
execute the code of the assigned function whenever called, while also remembering
the data attached.

In the above example, let’s see what would happen if we delete the outer() function
after returning the inner() function.
def outer(x):
def inner():
print("This is the inner function")
print(x)

return inner # returning inner function

func = outer("Hey there!")


del outer # deleting outer function
func()

Output

This is the inner function


Hey there!

After assigning the returned inner() function to the variable func,


the outer() function is deleted by writing del outer. Then on calling func(), we are
still getting the output. So, even after deleting the original function, the returned
function is still giving the output. Or, we can say that func() remembers the context
(functionality, variables defined, etc) of the deleted function.

Look at another example.

def outer():
x = "Hey there!"

def inner():
print("This is the inner function")
print(x)

return inner # returning inner function

func = outer()
func()

Output
This is the inner function
Hey there!

This is similar to the previous examples. Here also, the inner() function is using the
variable x defined inside the outside function.

Let’s see another example in which a closure is used.

def outer(x):
def inner(y):
print(x + y)

return inner

func = outer(10)
func(5)

Output

15

Before reading the explanation, try to understand what is happening in this program.

The outer() function is called by passing 10 as argument, making its


parameter x equal to 10. It returns its nested function inner() and assigns it to the
variable func. So, func stores the inner() function with x equal to 10. Then
the func() function is called by passing 5 as argument by writing func(5), thus
making its parameter y equal to 5. Inside the body of func(), the sum x + y is
printed.

We can also obtain the above result as follows.

def outer(x):
def inner(y):
return x + y

return inner

func = outer(10)
print(func(5))
Output

15

In the next example, we are returning the sum of the elements of a list.

def outer():
sum = 0

def inner(mylist):
nonlocal sum
for num in mylist:
sum = sum + num
return sum

return inner # returning inner function

func = outer()
mylist = [1, 2, 3, 4, 5, 6]
print(func(mylist))

Output

21

Try to understand the above code yourself. If you have, that means you have
understood closures.

So we have seen different examples of closures. But when should we use closures?
Let’s find the answer to it in the next section.

Significance of Closures

The first benefit of closures is that we can execute the functionality of the nested
function anytime even after its outer function is not executing.

When we have only one or two (preferably one) methods in a class, then also it is
better to use closures instead of class.
Assume that we have the following class with a single
method print_message() inside it.

class CreateMessage:
def __init__(self, msg):
self.msg = msg

def print_message(self, name):


print("Hi", name)
print(self.msg)

message = CreateMessage("Welcome to the Python course")

print("Printing 1st message----")


message.print_message("John")

print("Printing 2nd message----")


message.print_message("Sam")

Output

Printing 1st message----


Hi John
Welcome to the Python course
Printing 2nd message----
Hi Sam
Welcome to the Python course

We created an object message of the class CreateMessage by passing the value


"Welcome to the Python course" to the msg parameter of the constructor. Thus, in
the constructor, self.msg = msg made the attribute msg equal to "Welcome to the
Python course". Then we used the message object to call
the print_message() method with different arguments every time.

Note that we used a class here to store a single method print_message() because
we also wanted to store some data (msg) which is used in the method.
So according to you, is it a good decision to create a class for a single method just
because we want to store some additional data? No, it isn’t. For such scenarios, we
can use closures instead. Let’s see how.

def create_message(msg):
def print_message(name):
print("Hi", name)
print(msg)
return print_message

message = create_message("Welcome to the Python course")

print("Printing 1st message----")


message("John")

print("Printing 2nd message----")


message("Sam")

Output

Printing 1st message----


Hi John
Welcome to the Python course
Printing 2nd message----
Hi Sam
Welcome to the Python course

We defined a function create_message() with a nested function print_message().


When the create_message() function is called, we create a closure by returning the
nested function print_message() to a variable message. So, the variable message
now stores the functionality of the function print_message() with the value of the
variable msg as "Welcome to the Python course". Now whenever the
function message() is called, it implements the functionality of print_inner(). We got
the same result as in the previous example having class.

Thus, we were able to use closures instead of class when it has just one method.

Look at another example.


class CreateMessage:
def __init__(self, msg):
self.msg = msg

def print_message(self, name):


print("Hi", name)
print(self.msg)

print("Object 1----")
message1 = CreateMessage("Welcome to the Python course")

print("Printing 1st message----")


message1.print_message("John")

print("Printing 2nd message----")


message1.print_message("Sam")

print("Object 2----")
message2 = CreateMessage("Welcome to the C# course")

print("Printing 1st message----")


message2.print_message("Maria")

print("Printing 2nd message----")


message2.print_message("Peter")

Output

Object 1----
Printing 1st message----
Hi John
Welcome to the Python course
Printing 2nd message----
Hi Sam
Welcome to the Python course
Object 2----
Printing 1st message----
Hi Maria
Welcome to the C# course
Printing 2nd message----
Hi Peter
Welcome to the C# course

Suppose the students John and Sam want to learn Python and Maria and Peter want
to learn C#. For that, in the above example, we created an object message1 with
attribute msg equal to "Welcome to the Python course", and an object message2
with msg equal to "Welcome to the C# course".

This can be implemented using closures as follows.

def create_message(msg):
def print_message(name):
print("Hi", name)
print(msg)
return print_message

print("Variable 1----")
message1 = create_message("Welcome to the Python course")

print("Printing 1st message----")


message1("John")

print("Printing 2nd message----")


message1("Sam")

print("Variable 2----")
message2 = create_message("Welcome to the C# course")

print("Printing 1st message----")


message2("Maria")

print("Printing 2nd message----")


message2("Peter")
Output

Variable 1----
Printing 1st message----
Hi John
Welcome to the Python course
Printing 2nd message----
Hi Sam
Welcome to the Python course
Variable 2----
Printing 1st message----
Hi Maria
Welcome to the C# course
Printing 2nd message----
Hi Peter
Welcome to the C# course

Here the variables message1 and message2 store the code of


the print_message() function with the values of msg equal to "Welcome to the
Python course" and "Welcome to the C# course" respectively.

If a class has a single method, then using closure instead of class is a better choice.

If the number of methods or the number of attributes in a class increase, then go for
class. But for a single method and a few attributes, using closures is a better option
because it is faster. However, the choice is yours.

Closures are also used for creating decorators. We will read about decorators in the
next chapter.

Python Decorators

A decorator is a very powerful feature in Python that allows us to decorate or modify


a function.

Suppose you are given a task which has to be completed within a day. In the task,
you created 50 functions, and after that you realize that you forgot to add the start
and end time stamp at the beginning and end respectively of the body of each
function. What would you do in that case? In such a scenario, you can use
decorators as your rescue.

You can create a decorator which adds a start time stamp at the beginning and an
end time stamp at the end of the body of the function it is applied to. After creating
this decorator, you can apply it to all the 50 functions to modify them. Hence, in this
way, you can modify the functions without actually changing their code. This is the
beauty of decorators.

You will understand better what we are talking about when we will dive into
decorators, but before that, let’s revise a few concepts which will be used while
creating decorators.

Let’s recap

Let’s briefly look at some concepts related to functions which we have already
studied.

Assigning a Function to a Variable

We can assign a function to a variable and then can use this variable to call the
function.

def multiply(a, b):


return a * b

product = multiply # assigning the function multiply to variable product


print(product(5, 6)) # calling the function using product

Output

30

We assigned the function multiply() to the variable product by writing product =


multiply (because function name is the reference to the function). So, product now
stores the same function. Thus we have two names product and multiply for the
same function and it can be called by any name.

Passing a Function as Argument to another Function

We can pass a function as an argument to another function, just like we pass


integers, strings, lists or any other data type as argument.

def display(func):
print("This is the display function")
func()

def message():
print("Welcome everyone!")

display(message)

Output

This is the display function


Welcome everyone!

The function message() is passed as an argument to the function display(), making


its parameter func equal to the passed function message(). Inside
the display() function, the function func is called by writing func().

Defining a Function inside another Function (Nested


Function)

A function which is defined and called inside another function is called a nested
function. In the last two chapters, we have seen sufficient examples of nested
functions. Let’s again look at an example.

def outer():
def inner(): # defining nested function
print("This is nested function")

print("This is outer function")


inner() # calling nested function

outer() # calling outer function

Output

This is outer function


This is nested function

We defined a function named inner inside another function named outer.


Thus, inner() is a nested function of outer(). The inner() function is called inside
the outer() function by writing inner().

Returning a Nested Function from its Parent Function

We can return a nested function from the function in which it is defined.

def outer():
def inner(): # defining nested function
print("Welcome everyone!")

return inner # returning nested function

message = outer() # calling outer function


message()

Output

Welcome everyone!

The function inner() is a nested function of the function outer(). When


the outer() function was called, it returned the inner() function and assigned it to
the variable message.

Look at another example.


def outer(x):
def inner():
print(x)

return inner # returning inner function

message = outer("Hey there!")


message()

Output

Hey there!

This is an example of closure in which the outer() function returns its nested
function inner() after attaching the value of the variable x to the code of inner().
We learned about closures in the previous chapter.

Having revised all the necessary topics, let’s get started with decorators.

What are Decorators?

To understand decorators, let’s first look at the simplest following example of


functions.

def normal():
print("I am a normal function")

normal()

Output

I am a normal function

Here we have a simple function named normal. Let’s make this function a little bit
pretty.

def decorator_func(func):
def inner():
print("****")
func()
print("****")

return inner # returning inner function

def normal():
print("I am a normal function")

decorated_func = decorator_func(normal)
decorated_func()

Output

****
I am a normal function
****

def decorator_func(func) → We created another function


named decorator_func which receives a function as parameter and has a nested
function named inner. As the name suggests, the decorator_func() function
receives a function as parameter, decorates or modifies the inner() nested function
and then returns the decorated inner() function.

decorated_func = decorator_func(normal) → When the normal() function is


passed to the decorator_func() function, the latter returns the
decorated inner() function and assigns the returned function to the
variable decorated_func. Thus, decorated_func now stores the decorated function.

decorated_func() → Calling the decorated_func() function now prints the modified


output.

In the above example, the decorator_func() function is a decorator.

Wait, we have just created a decorator! Creating a decorator was this easy.

In the above example, we are assigning the returned decorated function to a new
variable decorated_func by writing decorated_func = decorator_func(normal) .
This doesn’t change our original function normal(). Assigning the decorated function
to normal() will modify the normal() function as shown below.

def decorator_func(func):
def inner():
print("****")
func()
print("****")

return inner # returning inner function

def normal():
print("I am a normal function")

normal = decorator_func(normal)
normal()

Output

****
I am a normal function
****

normal = decorator_func(normal) → After taking normal as argument,


the decorator_func() function returns the decorated function to normal only, hence
modifying it.

normal() -> Thus calling the normal() function now prints the modified output.

So, we can say that we decorated the normal() function.

There is a much easier way to apply decorator in Python. We can use the
symbol @ followed by the name of the decorator function before the definition of the
function we want to decorate.

For example, in the above program, we can replace the following piece of code

def normal():
print("I am a normal function")
normal = decorator_func(normal)

with the following code.

@decorator_func
def normal():
print("I am a normal function")

That’s it. Let’s try it out in the previous example.

def decorator_func(func):
def inner():
print("****")
func()
print("****")

return inner # returning inner function

@decorator_func
def normal():
print("I am a normal function")

normal()

Output

****
I am a normal function
****

We got the same output.

Look at another example in which a decorator converts the text returned by a


function to lowercase.

def decorator_lowercase(func):
def to_lower():
text = func()
lowercase_text = text.lower()
return lowercase_text

return to_lower # returning inner function

@decorator_lowercase
def message():
return "I Am a Normal Function"

print(message())

Output

i am a normal function

In this example, the function decorator_lowercase() takes a function as parameter


and has a nested function to_lower(). The nested function to_lower() converts the
string returned by the passed parameter func() to lowercase and then returns the
lowercase string.

Writing @decorator_lowercase before the definition of the function message() means


that decorator_lowercase() is applied as the decorator for message(). This is the
same as writing message = decorator_lowercase(message) .

Passing Parameterized Functions to Decorators

So far, we passed those functions which don’t have parameters to the decorator
functions. Now let’s see how to pass a function that has parameters to the decorator
function.

If we want to apply a decorator to a function that has parameters, then pass the
function as argument to the decorator function and the parameters as arguments to
the nested function of the decorator function.

Consider the following function.

def divide(num1, num2):


return num1/num2
The divide() function has two parameters num1 and num2. On calling the function, if
the argument passed to the parameter num2 is 0, then it will throw an error.

To handle the case of 0 as the second parameter, let’s wrap the function in a
decorator as shown below.

def decorator_division(func):
def division(a, b):
if b == 0:
return "Can't divide!"
else:
return a/b

return division # returning inner function

@decorator_division
def divide(num1, num2):
return num1/num2

print(divide(10, 2))

Output

5.0

In this program, writing @decorator_division before the definition of


the divide() function means that decorator_division() is applied as the decorator
for divide().

Here, the divide() function has two parameters, therefore


the decorator_division() function receives the divide() function as parameter and
its nested function division() takes its two parameters as its parameters. Thus, the
parameters a and b of the nested function division() are the same as the
parameters num1 and num2 respectively of divide().

Inside the nested function, if the second parameter b is equal to 0, then we are
throwing an error message. So, our problem got solved.

Python Chaining Decorators


Till now we have been assigning a single decorator to a function. In Python, we have
the flexibility to apply multiple decorators to a single function, and doing this is quite
easy.

Look at the following example.

def decorator_star(func):
def inner():
print("****")
func()
print("****")

return inner # returning inner function

def decorator_hash(func):
def inner():
print("####")
func()
print("####")

return inner # returning inner function

@decorator_star
@decorator_hash
def normal():
print("I am a normal function")

normal()

Output

****
####
I am a normal function
####
****
We applied two decorators decorator_star and decorator_hash to
the normal() function. Note that the order in which we apply the decorators to the
function matters.

In the above example, the following code

@decorator_star
@decorator_hash
def normal():
print("I am a normal function")

is the same as writing the following statements.

@decorator_star
@decorator_hash
def normal():
print("I am a normal function")

You must have understood the order in which decorators are applied to the function.
The decorator applied later is applied first. Hence first decorator_hash is applied and
after that decorator_star is applied.

Let’s see what will be the output if we reverse the order


of decorator_star and decorator_hash.

def decorator_star(func):
def inner():
print("****")
func()
print("****")

return inner # returning inner function

def decorator_hash(func):
def inner():
print("####")
func()
print("####")

return inner # returning inner function

@decorator_hash
@decorator_star
def normal():
print("I am a normal function")

normal()

Output

####
****
I am a normal function
****
####

And yes we got the output as expected.

So, decorators are used to modify the functionality of a function (or even a class)
without actually changing the code of the function.

Python Docstrings

Generally, adding documentation is always a good practice while creating any


project. In Python also, we can add documentation to functions, classes or class
methods so that we get to know their functionality by reading the documentation.
Such documentation in Python is called docstring.

Defining a Docstring in Python


A docstring is created by adding a string enclosed within triple single quotes '''
''' or triple double quotes """ """. These are written right after the definition of a
function or class.

Python Single Line Docstring

These are the docstrings defined in a single line.

Let’s see an example of a single line docstring.

def cube(x):
'''Returns the cube of the argument x.'''
return x**3

print(cube(2))

Output

As you can see, by reading the docstring we can tell what the function does.
Therefore, docstrings should be written such that we understand the overall
functionality of the function by just reading it.

The same example is written using triple double quotes below.

def cube(x):
"""Returns the cube of the argument x."""
return x**3

print(cube(2))

Output

We should implement the following practices while writing single line docstrings.

 The opening and closing quotes should be on the same line.


 The docstring should begin with a capital letter and end in a period (.).
 No blank line should be left before and after the docstring.
 One line docstring are more suitable for small and less complex functions.

Python Multiline Docstring

These are the docstrings which span into multiple lines.

Let’s see an example of a single line docstring.

def cube(x):
"""Returns the cube of the argument x.

Argument: x must be a number


Returns: cube of x
"""

return x**3

print(cube(2))

Output

We should implement the following practices while writing single line docstrings.

 The docstring should begin with a summary line followed by a blank line,
followed by more details.
 The summary line of the docstring can be on the same line as the opening
triple quotes or on the next line.
 The closing triple quotes should be on a new line after the docstring content
ends.
 Multiline docstrings are more suitable for classes or more complex functions.

Now that we have been introduced to docstrings, let’s see how to write multiline
docstrings for functions and classes.
Python Docstrings for Functions

For small functions, it is advisable to write a single line docstring. For bigger and
complex functions, multiline docstring should be written.

The following points should be kept in mind while writing multiline docstrings for
functions or class methods.

 The docstring should include a summary (preferably one liner summary)


stating the objective of the function. The arguments and return values along
with their data types should also be included in the docstring.
 It should also list the exceptions which are raised

def divide(a, b):


"""Divides two numbers and returns the quotient.

Args:
a (int): An integer
b (int): A non-zero integer

Raises:
ValueError: b should not be 0

Returns:
quotient (float): quotient of the division of a by b
"""

if b == 0:
raise ValueError()

quotient = a/b
return quotient

print(divide(10, 2))
Output

5.0

You can see that we have included a short description of the functionality of the
function in the start of the docstring. We have also included the information about the
arguments, exception raised and the returned value.

Accessing Docstring of Functions

When we add a docstring to a function, it gets added as the __doc__ attribute of the
function. Therefore, to access the docstring of a function, we need to access
its __doc__ attribute.

def divide(a, b):


"""Divides two numbers and returns the quotient.

Args:
a (int): An integer
b (int): A non-zero integer

Raises:
ValueError: b should not be 0

Returns:
quotient (float): quotient of the division of a by b
"""

if b == 0:
raise ValueError()

quotient = a/b
return quotient

print(divide.__doc__)
Output

Divides two numbers and returns the quotient.

Args:

a (int): An integer

b (int): A non-zero integer

Raises:

ValueError: b should not be 0

Returns:

quotient (float): quotient of the division of a by b

We accessed the docstring of the divide function by writing divide.__doc__.

Python Docstrings for Classes

Let’s look at the points which should be considered while writing docstrings for
classes.

 The docstring should include a summary (preferably one liner summary)


stating the behaviour of the class. It should also include all the attributes.
 If the class inherits another class, then the details of its super class should be
included. Also, if any method of the class is overriding a method of its super
class, then the details of that method should be included.
 The constructor and methods inside the class should have separate
docstrings.

class Rectangle():
"""
Rectangle class stores dimensions of rectangle.
Attributes:
length (float): Length of the rectangle
breadth (float): Breadth of the rectangle
"""

def __init__(self, l, b):


"""The constructor to initialize the object.

Args:
l (float): Length of the rectangle
b (float): Breadth of the rectangle
"""
self.length = l
self.breadth = b

def getArea(self):
"Returns the area of the Rectangle object."
return self.length * self.breadth

rect = Rectangle(2,4)

print(rect.getArea())

Output

We added separate docstrings for the class, its constructor and method. In the
docstring of the class, we included a summary of the class along with the details of
the attributes.

Accessing Docstring of Classes, Constructors and Methods

Just like functions, if a docstring is added to a class, it gets added as


its __doc__ attribute.
Therefore, the docstring of the Rectangle class can be accessed
as Rectangle.__doc__. The docstring of its constructor can be accessed
as Rectangle.__init__.__doc__ and its
method getArea as Rectangle.__getArea__.__doc__.

Let’s print all these docstrings in the last example.

class Rectangle():
"""
Rectangle class stores dimensions of rectangle.

Attributes:
length (float): Length of the rectangle
breadth (float): Breadth of the rectangle
"""

def __init__(self, l, b):


"""The constructor to initialize the object.

Args:
l (float): Length of the rectangle
b (float): Breadth of the rectangle
"""
self.length = l
self.breadth = b

def getArea(self):
"Returns the area of the Rectangle object."
return self.length * self.breadth

rect = Rectangle(2,4)

print("**** Docstring of class Rectangle ****")


print(Rectangle.__doc__)

print("**** Docstring of the constructor class Rectangle ****")


print(Rectangle.__init__.__doc__)
print("**** Docstring of the getArea method of class Rectangle ****")
print(Rectangle.getArea.__doc__)

Output

**** Docstring of class Rectangle ****

Rectangle class stores dimensions of rectangle.

Attributes:

length (float): Length of the rectangle

breadth (float): Breadth of the rectangle

**** Docstring of the constructor class Rectangle ****

The constructor to initialize the object.

Args:

l (float): Length of the rectangle

b (float): Breadth of the rectangle

**** Docstring of the getArea method of class Rectangle ****

Returns the area of the Rectangle object.

For classes, we can also use the help() function to read docstrings. This function
reads the docstrings of the class and its methods. For example, to read the
docstrings of the class Rectangle and its methods, we need to
write help(Rectangle).

Let’s print the docstrings in the previous example using help().

class Rectangle():
"""
Rectangle class stores dimensions of rectangle.

Attributes:
length (float): Length of the rectangle
breadth (float): Breadth of the rectangle
"""

def __init__(self, l, b):


"""The constructor to initialize the object.

Args:
l (float): Length of the rectangle
b (float): Breadth of the rectangle
"""
self.length = l
self.breadth = b

def getArea(self):
"Returns the area of the Rectangle object."
return self.length * self.breadth

rect = Rectangle(2,4)

help(Rectangle)

Output

Help on class Rectangle in module __main__:

class Rectangle(builtins.object)

| Rectangle(l, b)

| Rectangle class stores dimensions of rectangle.


|

| Attributes:

| length (float): Length of the rectangle

| breadth (float): Breadth of the rectangle

| Methods defined here:

| __init__(self, l, b)

| The constructor to initialize the object.

| Args:

| l (float): Length of the rectangle

| b (float): Breadth of the rectangle

| getArea(self)

| Returns the area of the Rectangle object.

| ----------------------------------------------------------------------

| Data descriptors defined here:

| __dict__

| dictionary for instance variables (if defined)

| __weakref__

| list of weak references to the object (if defined)


It is always good to have docstrings in your code so that understanding the
functionality and properties of your functions and classes becomes easier whenever
you or someone else goes through the code. So start including them in your
programs.

Python More to do

Congratulations on completing this course! I will tell you about different uses of
Python in different fields. You can go to where your interest lies.

 Website - If you are interested in making websites, then try Django Web
Framework and learn it.

 Games - If you are interested in making games, then try PyGame. You can
make games with graphics and sound.

 3D Games and graphics - You can try Panda 3D for making 3D graphics
and games.

 Mobile application - Kivy is used for doing interfaces on desktops and


mobile platforms.

 Web scraping - Beautifulsoup is used for gathering information from


websites.
 Natural Language Tool Kit for analysing written text and writing things like
spam filters and chat bots.

 Other languages - You can try to learn other languages as well.

 Change from Python 2 to Python 3 - What's new in Python 3

You might also like