Python Programming[1]
Python Programming[1]
This list is never-ending, with new additions to it every day, but I hope it is
enough to motivate you to learn Python.
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")
1. Download Python3.
2. Install it.
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
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('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
Output
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.
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".
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.
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.
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.
Let’s analyze this error and extract some information which is useful for
us to fix this.
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.
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.
num = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10
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.
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.
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.")
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.
# I am using a variable x
x = 10
print(x)
Output
10
'''
Multiline comment
Using variable x and y.
'''
x = 10
y = 5
print(x+y)
Output
15
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.
and assert as
import in is
or pass raise
False None
Python Identifiers
x = 10
Here, x is an identifier.
Rules for writing Python Identifiers
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.
a = 10
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”.
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"
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.
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).
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).
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.
Python Boolean
A Boolean data type consists of two values - True and False. These two values are
also the keywords reserved by Python.
Output
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.
Output
a = "23"
print(a, "has type", type(a))
b = 23
print(b, "has type", type(b))
Output
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
The items in a list are enclosed within brackets [ ] and separated by commas.
Output
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.
Output
Python Dictionary
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}
a = {1, 2, 3}
print(a, "has type", type(a))
Output
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.
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
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.
print(float(46))
Output
46.0
print(float(True))
print(float(False))
Output
1.0
0.0
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()
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.
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.
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.
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.
Output
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.
Output
Output
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
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.
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.
Output
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.
Output
Here, we passed 12. Thus, x became '12' and then int(x) turned it into an integer.
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'>
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.
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.
Arithmetic Operators
Relational Operators
Logical Operators
Assignment Operators
Identity Operators
Membership Operators
Arithmetic Operators are the type of operators which take numerical values as their
operands and return a single numerical value.
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.
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.
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.
Output
== 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.
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.
Look at the following table in which Exp1 and Exp2 are the two operands.
Exp1 Operator Exp1 Output (Boolean)
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 and or of programming are very much similar to English words 'and' and 'or'.
In English,
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.
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).
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.
Assignment Operators are used to assign values from its right side operands to its
left side operands. The most common assignment operator is =.
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.
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
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.
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.
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.
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.
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.
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.
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".
We can also test for a dictionary with membership operators, but the search happens
only for the key, not for the value.
True
False
a is a dictionary having 1, 2 and ‘default’ as keys and ‘Blue’, ‘Green’ and ‘Orange’ as
the corresponding values.
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
or Left to right
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.
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
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.
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.
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.
if condition:
statement
statement
...
We first write if condition:. This condition is based on the decision that we will be
making.
a = 2
if a == 2:
print("a is 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.
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.
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.
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.
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.
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.
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.
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.
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.
a = 14
if a%2 == 0: #checking divisibility by 2
print("Your number is even")
else:
print("Your number is odd")
Output
if condition:
statement
statement
...
elif condition:
statement
statement
...
elif condition:
statement
statement
...
else:
statement
statement
...
If the conditions of if and all elif are False, then the body of else is executed.
Output
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.
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.
We can use if, if...else or if...elif...else statements in the body of if, elif or else. This is
also called nesting.
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.
Output
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.
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.
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.
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.
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.
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.
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.
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)
Output
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.
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
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.
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.
#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
Make sure to read articles in Further Reading at the end of this chapter
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.
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
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.
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.
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
Output
308
You must have understood the code. If not, then let us explain a bit.
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.
We will look at the use of for loop with lists, strings, tuples and other sequences in
detail in later chapters.
start - Integer from which the sequence starts. It is optional. Its default value is 0.
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.
In the last chapter, we printed the first 10 natural numbers using a while loop. Let’s
do that using a for loop.
Output
1
2
3
4
5
6
7
8
9
10
The next example prints the multiplication table of 14 using a for loop.
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).
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.
In the last two topics, you learned about loops which are used to repeat a certain
process some number of times.
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
Just type break after the statement after which you want to break the loop. As simple
as that!
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.
n = 1
while n < 10:
print("*")
if n == 2:
break
n = n + 1
Output
*
*
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.
continue
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.
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
[] is a list.
a=[]
Output
Output
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.
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.
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.
index 0 1 2 3 4
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.
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.
Negative index -5 -4 -3 -2 -1
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.
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]
# 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.
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.
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.
# reversed list
print(mylist[::-1])
Output
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
[10, 8, 6, 4, 2]
Lists are mutable, which means that their elements can be changed.
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].
mylist = [1, 2, 3, 4, 5, 6]
mylist[2:5] = [10, 11, 12] # changing 3rd to 5th elements
print(mylist) # printing changed list
Output
A range of elements from the 2nd index till the 4th index is changed by assigning a
list of values to mylist[2:5].
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.
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 del
The del keyword can be used to delete a single element, multiple elements or the
entire list.
Output
In the above code, the third element is deleted from the list.
Output
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
In the above code, the entire list colors is deleted. Thus, we got an error on printing
the list after deleting it.
Python remove()
Output
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.
Output
"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.
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.
Output
Brown
['Blue', 'Green', 'Red', 'Orange', 'Yellow']
Python clear()
Output
[]
list1 = [1, 2, 3]
list2 = ['a', 'b', 'c', 'd']
list3 = list1 + list2
print(list3)
Output
list1 = [1, 2]
list2 = list1 * 3
print(list2)
Output
[1, 2, 1, 2, 1, 2]
Output
True
False
False
We can iterate through a list using a for loop. We have already seen examples of
iteration of lists while studying for loops.
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.
Output
Blue
Green
Red
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.
Output
We can also have a list inside another list. In other words, a list can have an element
which is a list.
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.
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].
mylist = []
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.
mylist = list()
print(mylist)
Output
[]
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
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.
Output
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Simple Assignment
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.
list1 = [1, 2, 3, 4]
list2 = list1
list2[0] = 10
print("Original list:", list1)
print("New list:", list2)
Output
Python copy()
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
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.
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
Python list()
list1 = [1, 2, 3, 4]
list2 = list(list1)
list2[0] = 10
print("Original list:", list1)
print("New list:", list2)
Output
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]
The functions available in Python which can be used with lists are shown below.
Python len()
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()
mylist = [3, 2, 1, 5, 6, 4]
print(sum(mylist))
Output
21
Python sorted()
mylist = [3, 2, 1, 5, 6, 4]
print(sorted(mylist))
Output
[1, 2, 3, 4, 5, 6]
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]
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.
Output
True
any(mylist) returned True because all the elements of the list mylist are non-zero
or True.
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.
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.
Output
True
Output
False
In this example, one element is False and so the all() function returned False.
Output
If the specified element occurs more than once in a list, then the index of its first
occurrence is returned as shown below.
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.
Output
4
2
languages.index(‘Java’, 3) returned the index of 'Java' after index 3 (3 is the start
index).
Python count()
Output
Python sort()
Output
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.
In this example, the words in the list are sorted in descending order based on the
dictionary.
sort() changes the original list and doesn’t return anything. Let’s again look at the
following example.
Output
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.
Output
Python reverse()
It reverses a list.
Output
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.
The following example prints the cube of all numbers from 1 to 10 (included) using
a for loop.
cubes = []
print(cubes)
Output
We can do the same in a single line of code using list comprehension as shown
below.
Output
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).
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.
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.
The following example creates a list mylist of numbers from 1 to 5 (included) using
a for loop.
mylist = []
print(mylist)
Output
[1, 2, 3, 4, 5]
We can create the same list using list comprehension as shown below.
Output
[1, 2, 3, 4, 5]
In for i in range(1, 6), the variable i iterates over range(1, 6).
The values of i in the five iterations constitutes the five elements of the list mylist.
Now let’s create a list having five items, each equal to 2, using list comprehension.
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.
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 = []
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.
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
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.
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.
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 = []
print(result)
Output
Output
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.
list1 = [1, 2, 3, 4]
list2 = [1, 20, 30, 4]
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.
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.
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.
Output
Output
Output
Output
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.
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.
index 0 1 2 3
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
!
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])
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.
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 all the characters from the start till the end of a string,
use name_of_string[:].
mystring = "Python@Codesdope"
# 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.
mystring = "Python@Codesdope"
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"
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])
Output
epodsedoC@nohtyP
eosdCnhy
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.
mystring = "Python@Codesdope"
mystring[6] = "a"
Output
mystring = "Python@Codesdope"
mystring = "Java@Codesdope"
print(mystring)
Output
Java@Codesdope
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
mystring = "Python@Codesdope"
del mystring
print(mystring) # printing the string after deleting it
Output
We deleted the entire string mystring. Thus, we got an error on printing the string
after deleting it.
string1 = "Codes"
string2 = "Dope"
string3 = string1 + string2
print(string3)
Output
CodesDope
string1 = "Python"
string2 = string1 * 3
print(string2)
Output
PythonPythonPython
Output
True
False
False
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
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.
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.
mystring = "Hey"
for i in range(len(mystring)):
print(mystring[i])
Output
H
e
y
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.
Output
We got a syntax error. This is because we cannot nest the same type of quotes with
which we enclose the string.
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.
Output
Output
Output
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.
\\ Backslash (\)
\a ASCII Bell
\b ASCII Backspace
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
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
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 '.
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
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.
Output
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.
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).
Output
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"
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
Output
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"
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.
Output
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.
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.
Python len()
Output
14
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
Python lower()
It returns a string with all the characters in lowercase. It doesn’t change the original
string.
Output
Python upper()
It returns a string with all the characters in lowercase. It doesn’t change the original
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.
Output
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.
Output
Python join()
It joins the element of a list to form a string.
Output
Python index()
Output
If the specified substring occurs more than once in a string, then the index of its first
occurrence is returned as shown below.
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
Python rindex()
Output
7
12
2
In the above example, using index() will give the same result.
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()
Output
I was a progrwasmer
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
Python isalnum()
Output
Python isalpha()
string1 = "AbcdEf"
string2 = "Abcd 123dE" # contains a space
string3 = "Abcd123dE" # contains numbers
Output
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
Output
Python islower()
string1 = "codesdope"
string2 = "CodesDope"
Output
Python isupper()
string1 = "CODESDOPE"
string2 = "CodesDope"
Output
Python istitle()
Output
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.
a = ()
Output
Output
Output
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.
Output
(10, 'Hello', 20)
<class 'tuple'>
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.
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'>
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.
mytuple = (4, 9, 6, 2)
print(mytuple[0])
print(mytuple[1])
print(mytuple[2])
print(mytuple[3])
Output
4
9
6
2
The last element of a tuple has index -1, second last element has index -2, and so
on.
mytuple = (4, 9, 6, 2)
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.
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.
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)
# 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.
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.
# 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:
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.
We cannot delete any item from a tuple because tuples are immutable. Instead, we
can delete an entire tuple using the del keyword.
Output
We deleted the entire tuple mytuple. Thus, we got an error on printing the tuple after
deleting it.
tuple1 = (1, 2, 3)
tuple2 = ('a', 'b', 'c', 'd')
tuple3 = tuple1 + tuple2
print(tuple3)
Output
tuple1 = (1, 2)
tuple2 = tuple1 * 3
print(tuple2)
Output
(1, 2, 1, 2, 1, 2)
Output
True
False
False
Like lists, we can iterate through a tuple also using a for loop.
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
mytuple = ()
mytuple = (1, 2, 3, 4)
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.
mytuple = tuple()
print(mytuple)
Output
()
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()
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()
mytuple = (3, 2, 1, 5, 6, 4)
print(sum(mytuple))
Output
21
sorted()
mytuple = (3, 2, 1, 5, 6, 4)
print(sorted(mytuple))
Output
[1, 2, 3, 4, 5, 6]
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.
Output
True
any(mytuple) returned True because all the elements of the tuple are non-zero or
True.
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.
Output
True
False
In this example, one element is False and so the all() function returned False.
We can use the index() and count() functions which we use with lists with tuples as
well.
Output
2
2
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.
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.
tuple is (),
dictionary is {}.
The above code shows that 'mango' is related to 40, 'banana' to 10 and 'cherry' to
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
a = {}
Output
Output
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.
Output
print(fruit)
Output
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:
Output
There are some rules which must be followed while creating dictionaries.
Output
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
A key can be an immutable data type only. However, values can be both immutable
and mutable data types.
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.
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.
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.
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.
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'
Output
50
Since the key 'cherry' is not present in the dictionary fruit, fruit.get("cherry",
50) returned default value of 50.
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.
Output
The value of the key 'age' is changed by simply assigning the new value
to mydict['age'].
Output
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.
Output
Output
Python del
The del keyword can be used to delete a single element or the entire list.
Output
In the above code, the element having the key 'age' is deleted from the dictionary.
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.
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.
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()
{}
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.
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.
Output
False
Iteration Through a Python Dictionary
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.
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.
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
Output
key = name
value = John
key = age
value = 45
key = gender
value = male
mydict = {}
mydict = {}
mydict['name'] = 'John'
mydict['age'] = 45
mydict['gender'] = 'male'
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.
Output
Output
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.
Output
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.
Python copy()
copy() is a function in Python which creates a shallow copy of a dictionary.
Output
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()
Output
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()
Output
sorted()
Output
Output
[1, 2, 3]
To sort the keys in descending order, pass another argument reverse=True to
the sorted function.
Output
[3, 2, 1]
any()
It returns True if any of the keys in a dictionary are True. Otherwise, it returns False.
Output
True
any(mydict) returned True because all the keys of the dictionary mydict are non-
zero or True.
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.
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.
Output
True
Output
False
In this example, one key is False and so the all() function returned False.
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
Python values()
It returns an object which contains the values of a dictionary in the form of a list.
Output
items()
It returns an object which contains the key-value pairs of a dictionary in the form of a
list.
Output
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
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
Let’s take an example which stores the cube of all numbers from 1 to 10 in a
dictionary using a for loop.
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.
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.
Comparing this syntax to the last example, num is key, num**3 is value and for num
in range(1, 11) is for_loop.
print(new_fruits)
Output
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.
Output
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.
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.
Output
Output
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.
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.
print(new_result)
Output
Output
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.
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.
myset = {1, 2, 3}
print(myset)
Output
{1, 2, 3}
In the above example, myset is a set having three elements of type integer.
{1, 2, 3, 'Hello'}
In the above example, myset is a set having elements of different data types.
myset = set()
print(myset)
Output
set()
myset = {1, 2, 3, 2}
print(myset)
Output
{1, 2, 3}
A set is unordered
Elements in a set don’t have a specific order. Their order can get changed every
time you use them.
{'Hello', 1, 2, 3, '1'}
This is also the reason why we can’t access elements of a set through index or
position.
Output
Output
The elements of a set can’t be changed, but new elements can be added.
Python add()
myset = {1, 2, 3}
myset.add(5)
print(myset)
Output
{1, 2, 3, 5}
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
Here, since two lists and a set are passed to the update() function, their elements
got added to the set myset.
Python discard()
Output
If we try to remove an element which is not present in the set, discard() doesn’t
throw an error.
Output
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.
Output
If we try to remove an element which is not present in the set, remove() throws the
“KeyError” as shown below.
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.
In the above example, pop() selected a random element and removed it from the
set.
Python clear()
Output
set()
s1 = {1, 2, 3, 4, 5, 6}
s2 = {4, 5, 6, 7, 8}
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}
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}
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}
Output
{4, 5, 6}
4, 5 and 6 are present in both the sets and hence constitutes the intersection set s1
& s2.
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}
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.
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}
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}
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}
Output
True
False
False
Iteration Through a Python Set
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.
myset = set()
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.
Output
{1, 2, 3}
{'o', 'e', 'C', 'p', 's', 'd'}
{1, 2, 3, 4}
Simple Assignment
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.
set1 = {1, 2, 3, 4}
set2 = set1
set2.add(10)
print("Original set:", set1)
print("New set:", set2)
Output
On changing set2, set1 also got changed. This is because both the variables are
pointing to the memory location of the same set.
Python copy()
set1 = {1, 2, 3, 4}
set2 = set1.copy()
set2.add(10)
print("Original set:", set1)
print("New set:", set2)
Output
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()
set1 = {1, 2, 3, 4}
set2 = set(set1)
set2.add(10)
print("Original set:", set1)
print("New set:", set2)
Output
We created a new set having all the elements of set1 using set(set1) and assigned
this newly created set to set2.
The functions available in Python which can be used with sets are shown below.
len()
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()
myset = {3, 2, 1, 5, 6, 4}
print(sum(myset))
Output
21
sorted()
myset = {3, 2, 1, 5, 6, 4}
print(sorted(myset))
Output
[1, 2, 3, 4, 5, 6]
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.
Output
True
any(mytuple) returned True because all the elements of the set are non-zero or
True.
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.
Output
True
Output
False
In this example, one element is False and so the all() function returned False.
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.
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}
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)
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.
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}
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}
Python issubset()
set1 = {4, 5, 6}
set2 = {4, 5, 6, 7, 8}
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()
set1 = {4, 5, 6}
set2 = {4, 5, 6, 7, 8}
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.
The following example creates three frozensets from a list, a string and a set.
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.
Output
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.
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.
# 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.
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.
# function
def is_even(x):
if x%2 == 0:
print("even")
else:
print("odd")
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")
Output
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
# 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").
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]
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
def is_even(x):
if x%2 == 0:
return True
else:
return False
print(is_even(1))
print(is_even(2))
Output
False
True
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
Output
[43, 6, 2, 5, 3, 6, 4, 2]
Output
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.
Output
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
Output
True
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.
3! = 3*2*1 = 6
2! = 2*1 = 2
1! = 1
Also, 0! = 1
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
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)
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.
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).
f(2) = f(1)+f(0)
= 1+0 = 1
f(3) = f(2)+f(1)
=1+1=2
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
For f(2):
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.
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.
The number of arguments passed in a function call are equal to the number of
parameters in the function definition.
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.
display("John", 45)
Output
In the above example, if only one argument is passed to the function, we will get an
error of missing positional argument.
display("John")
Output
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
TypeError: display() missing 1 required positional argument: 'age'
We can also pass a value assigned to a parameter as named argument. Let’s see
how.
Output
The order (position) in which keyword arguments are passed can be changed. Look
at the following example.
Output
Output
Output
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.
We can assign default values to parameters in function definition. These are called
default arguments.
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.
Let’s see an example in which the function has a positional and a default argument.
display("John")
display("John", 50)
Output
Look at another example in which the function has a mix of positional, keyword and
default arguments.
Output
The following program throws an error because the default argument is given before
a non-default argument in function definition.
Output
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.
An asterisk (*) is used before the parameter name for arbitrary positional arguments.
For example,
def function_name(*parameter_name)
*parameter_name will take all those non-keyword arguments and parameter_name will
be a tuple containing all those arguments.
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.
def product_summary(*products):
print("Products purchased:")
Output
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:")
Output
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.
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.
Let’s now see how classes and objects are created and used in Python.
class Square():
pass
In the body of this class, we have not defined anything yet and so passed the
keyword pass.
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.
# class definition
class Square():
side = 14
Output
14
side = 14 → The class has an attribute named side having a value of 14.
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.
In Python, the terms object and instance are used interchangeably, and the
creation of an object is also called instantiation.
# class definition
class Square():
side = 14
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
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.
We can define attributes for objects without defining those attributes in the class as
shown below.
# class definition
class Rectangle():
pass
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
print(rc2.length)
Output
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.
# class definition
class Rectangle():
length = 10
Output
Length: 10
A class can also define a new attribute outside the class definition as shown below.
# class definition
class Rectangle():
length = 10
Output
Length: 10
Breadth: 20
The class Rectangle accessed its attribute length and defined a new
attribute breadth outside its class definition.
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")
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.
# 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.
# 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
# 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.
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
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
Output
14
14
Here also, side is a class attribute because both the objects of the class are able to
access it.
# 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.
Python Constructor
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.
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
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.
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.
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
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.
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
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)
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.
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.
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.
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
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")
Output
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.
We read about the three types of methods we can deal with in OOP, but mostly we
use instance methods
Python Subclass
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.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)
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.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.
# 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.display1()
emp.display2()
print("Outside both methods: name:", emp.name, "age:", emp.age)
Output
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.display2() →The object emp calls the display2() method of its own 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.
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)
Output
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) .
# 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.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).
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.
# 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.display2()
Output
salary: 8000
name: John
age: 20
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()
# superclass
class Person():
pass
# subclass
class Employee(Person):
pass
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()
# superclass
class Person():
pass
# subclass
class Employee(Person):
pass
print(issubclass(Person, Employee))
print(issubclass(Employee, Person))
Output
False
True
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
Inheritance can be done in a number of ways. Till now, we have seen examples
where a single class inherits another class.
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
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.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.
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
# 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
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().
class Area():
def getArea(self, l, b):
return l * b
class Perimeter():
def getPerimeter(self, l, b):
return 2*(l + 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.
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.
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.
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().
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).
Before looking at the rules of MRO, let’s introduce a new term - 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.
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()
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.
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.
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")
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
print(C.mro())
Output
class A():
def display(self):
print("This is A")
class B():
def display(self):
print("This is B")
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.
class A():
pass
class B():
pass
class D(C):
pass
print(D.mro())
Output
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
print(D.mro())
Output
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).
class A():
pass
class B():
pass
class C(A):
pass
print(D.mro())
Output
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).
class A():
pass
class B(A):
pass
class C(A):
pass
print(D.mro())
Output
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.
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.
class A():
pass
class B():
pass
class C(A, B):
pass
class D(B):
pass
class E(C,D):
pass
print(E.mro())
Output
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.
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)
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
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.
print"Sam")
Output
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.
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.
Python NameError
print(num)
Output
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
Python KeyError
Output
We got KeyError on printing mydict[4] because the dictionary mydict has no key as
4.
Python IndentationError
if 4 < 3:
print("4 is greater than 3")
Output
Python ZeroDivisionError
a = 0
b = 10/a
Output
Python ValueError
a = int("Sam")
Output
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
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.
Output
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.
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
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.
Output
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.
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.
Output
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:.
Output
Now suppose you also want to display the exception that is thrown in the except
clause. This can be done as follows.
Output
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.
Output
Please enter a number Hello
Please enter the correct input
Outside of try-except clauses. This statement is always executed.
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.
Output
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.
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.
except ValueError:
print("Please enter a numeric value")
except ZeroDivisionError:
print("Please enter a non-zero value")
Output
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.
except ValueError:
print("Please enter a numeric value")
except ZeroDivisionError:
print("Please enter a non-zero value")
except:
print("Some error occurred")
Output
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:
# handles all other exceptions
except ValueError:
print("Please enter a numeric value")
except ZeroDivisionError:
print("Please enter a non-zero value")
except:
print("Some error occurred")
Output
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.
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)
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)
Output
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.
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).
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")
Output
In this example, the statement written inside the finally clause got executed after
the else clause got executed.
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.
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
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.
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)
Output
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.
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")
except ValueError as v:
print("ValueError Exception thrown")
print(v)
Output
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.
except ValueError as v:
print("ValueError Exception thrown")
print(v)
except:
print("Some error occurred")
Output
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
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.
Before learning to create new exceptions, let’s see what exception classes are.
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
Output
Output
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.
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!")
Output
Python Assert
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.
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.
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.
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.
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.
Output
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:
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.
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.
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.
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.
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")
Mode Description
r Opens a file for reading (read-only mode). This is the default mode.
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.
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.
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
In the above example, if the xyz.txt file doesn’t exist in your system, then trying to
open it throws FileNotFoundError.
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.
In the above example, the opened example.txt file is closed by writing f.close().
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.
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.
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
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.
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
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.
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.
with open("example.txt") as f:
for line in f:
print(line)
Output
Hello learners!
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.
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
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.
with open("example.txt") as f:
print(f.readlines())
Output
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.
with open("example.txt") as f:
print("reading content 1st time")
print(f.read(10))
print("reading content 2nd time")
print(f.readlines())
Output
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.
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.
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
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.
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.
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.
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.
Output
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.
Output
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.
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.
Till now, we have used only for loop as the iterator. Let’s see how we can create a
new 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.
# 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().
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.
# 3rd iteration
print(next(my_iterator))
# 4th iteration
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.
# 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.
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.
# 3rd iteration
print(my_iterator.__next__())
# 4th iteration
print(my_iterator.__next__())
Output
1
2
3
4
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.
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
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
# 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
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.
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
# creating an iterator
obj = Range(14)
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
# creating an iterator
obj = Range(20)
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.
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
# 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
# displaying output
print("Amount is $", amount)
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.
We use argument specifiers like %s, %d, etc., in strings which are then replaced by
some value. Look at the following example.
# displaying output
print(formatted_string)
Output
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.
%s String
%d Integer
%.f Floating point number with specified number of digits after decimal
%c Character
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.
# displaying output
print("Amount is $%d" % amount)
Output
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.
Output
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
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.
# displaying output
print("Amount is ${}".format(amount))
Output
Output
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.
Output
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.
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
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.
Output
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.
Output
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 %.
print("Hello {:s}".format("John"))
Output
Hello John
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
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.
# displaying output
print(f"Amount is ${amount}")
Output
Output
num1, num2 = 1, 2
print(f"Sum of {num1} and {num2} is {num1 + num2}")
Output
Sum of 1 and 2 is 3
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.
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.
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.
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.
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.
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
dummy = lambda: 10
print(dummy()) # prints 10
Output
10
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.
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.
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'''
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.
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 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.
def even(num):
if num % 2 == 0:
return True
else:
return False
Output
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.
Output
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).
Output
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.
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.
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
def multiply(num):
return num * 2
Output
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.
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]
Output
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.
list1 = [1, 2, 3, 4, 5]
list2 = [6, 7, 8, 9, 10]
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.
A nested function is defined by simply creating it using the def keyword inside
another function.
Here is an example.
def inner():
print("This is inner function")
Output
This is outer function
This is inner 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 inner():
print("Inside inner func", x)
Output
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.
return power(2) # calling inner function and returning the value returned by
it
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.
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)
Output
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.
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 num < 0:
raise ValueError("Failed! The number must be non-negative")
return factorial(num)
print(get_factorial(5))
Output
120
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.
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.
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
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.
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.
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
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.
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.
def outer():
x = 10 # local variable
def inner():
print("Inside inner func", x)
inner()
print("Inside outer func", x)
outer()
Output
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
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.
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().
def outer(x):
def inner():
print("This is the inner function")
return x
return inner()
print(outer("Hey there!"))
Output
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?
def outer(x):
def inner():
print("This is the inner function")
print(x)
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)
Output
def outer():
x = "Hey there!"
def inner():
print("This is the inner function")
print(x)
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.
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.
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
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
Output
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
Output
Thus, we were able to use closures instead of class when it has just one method.
print("Object 1----")
message1 = CreateMessage("Welcome to the Python course")
print("Object 2----")
message2 = CreateMessage("Welcome to the C# course")
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".
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("Variable 2----")
message2 = create_message("Welcome to the C# course")
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
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
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.
We can assign a function to a variable and then can use this variable to call the
function.
Output
30
def display(func):
print("This is the display function")
func()
def message():
print("Welcome everyone!")
display(message)
Output
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")
Output
def outer():
def inner(): # defining nested function
print("Welcome everyone!")
Output
Welcome everyone!
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.
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("****")
def normal():
print("I am a normal function")
decorated_func = decorator_func(normal)
decorated_func()
Output
****
I am a normal function
****
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("****")
def normal():
print("I am a normal function")
normal = decorator_func(normal)
normal()
Output
****
I am a normal function
****
normal() -> Thus calling the normal() function now prints the modified output.
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)
@decorator_func
def normal():
print("I am a normal function")
def decorator_func(func):
def inner():
print("****")
func()
print("****")
@decorator_func
def normal():
print("I am a normal function")
normal()
Output
****
I am a normal function
****
def decorator_lowercase(func):
def to_lower():
text = func()
lowercase_text = text.lower()
return lowercase_text
@decorator_lowercase
def message():
return "I Am a Normal Function"
print(message())
Output
i am a normal function
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.
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
@decorator_division
def divide(num1, num2):
return num1/num2
print(divide(10, 2))
Output
5.0
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.
def decorator_star(func):
def inner():
print("****")
func()
print("****")
def decorator_hash(func):
def inner():
print("####")
func()
print("####")
@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.
@decorator_star
@decorator_hash
def normal():
print("I am a normal function")
@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.
def decorator_star(func):
def inner():
print("****")
func()
print("****")
def decorator_hash(func):
def inner():
print("####")
func()
print("####")
@decorator_hash
@decorator_star
def normal():
print("I am a normal function")
normal()
Output
####
****
I am a normal function
****
####
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
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.
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.
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 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.
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.
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.
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
Args:
a (int): An integer
Raises:
Returns:
Let’s look at the points which should be considered while writing docstrings for
classes.
class Rectangle():
"""
Rectangle class stores dimensions of rectangle.
Attributes:
length (float): Length of the rectangle
breadth (float): Breadth of the rectangle
"""
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.
class Rectangle():
"""
Rectangle class stores dimensions of rectangle.
Attributes:
length (float): Length of the rectangle
breadth (float): Breadth of the rectangle
"""
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)
Output
Attributes:
Args:
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).
class Rectangle():
"""
Rectangle class stores dimensions of rectangle.
Attributes:
length (float): Length of the rectangle
breadth (float): Breadth of the rectangle
"""
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
class Rectangle(builtins.object)
| Rectangle(l, b)
| Attributes:
| __init__(self, l, b)
| Args:
| getArea(self)
| ----------------------------------------------------------------------
| __dict__
| __weakref__
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.