This tutorial covers the following topics:
- Branching with
if,elseandelif - Nested conditions and
ifexpressions - Iteration with
whileloops - Iterating over containers with
forloops - Nested loops,
breakandcontinuestatements
This tutorial is an executable Jupyter notebook. Click the Open in Colab button at the top of this page to execute the code.
Jupyter Notebooks: This notebook is made of cells. Each cell can contain code written in Python or explanations in plain English. You can execute code cells and view the results instantly within the notebook. Jupyter is a powerful platform for experimentation and analysis. Don't be afraid to mess around with the code & break things - you'll learn a lot by encountering and fixing errors. You can use the "Kernel > Restart & Clear Output" menu option to clear all outputs and start again from the top.
Branching with if, else and elif
One of the most powerful features of programming languages is branching: the ability to make decisions and execute a different set of statements based on whether one or more conditions are true.
The if statement
In Python, branching is implemented using the if statement, which is written as follows:
if condition:
statement1
statement2
The condition can be a value, variable or expression. If the condition evaluates to True, then the statements within the if block are executed. Notice the four spaces before statement1, statement2, etc. The spaces inform Python that these statements are associated with the if statement above. This technique of structuring code by adding spaces is called indentation.
Indentation: Python relies heavily on indentation (white space before a statement) to define code structure. This makes Python code easy to read and understand. You can run into problems if you don't use indentation properly. Indent your code by placing the cursor at the start of the line and pressing the
Tabkey once to add 4 spaces. PressingTabagain will indent the code further by 4 more spaces, and pressShift+Tabwill reduce the indentation by 4 spaces.
For example, let's write some code to check and print a message if a given number is even.
a_number = 34if a_number % 2 == 0:
print("We're inside an if block")
print('The given number {} is even.'.format(a_number))We're inside an if block
The given number 34 is even.
We use the modulus operator % to calculate the remainder from the division of a_number by 2. Then, we use the comparison operator == check if the remainder is 0, which tells us whether the number is even, i.e., divisible by 2.
Since 34 is divisible by 2, the expression a_number % 2 == 0 evaluates to True, so the print statement under the if statement is executed. Also, note that we are using the string format method to include the number within the message.
Let's try the above again with an odd number.
another_number = 33if another_number % 2 == 0:
print('The given number {} is even.'.format(another_number))As expected, since the condition another_number % 2 == 0 evaluates to False, no message is printed.
The else statement
We may want to print a different message if the number is not even in the above example. This can be done by adding the else statement. It is written as follows:
if condition:
statement1
statement2
else:
statement4
statement5
If condition evaluates to True, the statements in the if block are executed. If it evaluates to False, the statements in the else block are executed.
a_number = 34if a_number % 2 == 0:
print('The given number {} is even.'.format(a_number))
else:
print('The given number {} is odd.'.format(a_number))The given number 34 is even.
another_number = 33if another_number % 2 == 0:
print('The given number {} is even.'.format(another_number))
else:
print('The given number {} is odd.'.format(another_number))The given number 33 is odd.
Here's another example, which uses the in operator to check membership within a tuple.
the_3_musketeers = ('Athos', 'Porthos', 'Aramis')a_candidate = "D'Artagnan"if a_candidate in the_3_musketeers:
print("{} is a musketeer".format(a_candidate))
else:
print("{} is not a musketeer".format(a_candidate))D'Artagnan is not a musketeer
The elif statement
Python also provides an elif statement (short for "else if") to chain a series of conditional blocks. The conditions are evaluated one by one. For the first condition that evaluates to True, the block of statements below it is executed. The remaining conditions and statements are not evaluated. So, in an if, elif, elif... chain, at most one block of statements is executed, the one corresponding to the first condition that evaluates to True.
today = 'Wednesday'if today == 'Sunday':
print("Today is the day of the sun.")
elif today == 'Monday':
print("Today is the day of the moon.")
elif today == 'Tuesday':
print("Today is the day of Tyr, the god of war.")
elif today == 'Wednesday':
print("Today is the day of Odin, the supreme diety.")
elif today == 'Thursday':
print("Today is the day of Thor, the god of thunder.")
elif today == 'Friday':
print("Today is the day of Frigga, the goddess of beauty.")
elif today == 'Saturday':
print("Today is the day of Saturn, the god of fun and feasting.")Today is the day of Odin, the supreme diety.
In the above example, the first 3 conditions evaluate to False, so none of the first 3 messages are printed. The fourth condition evaluates to True, so the corresponding message is printed. The remaining conditions are skipped. Try changing the value of today above and re-executing the cells to print all the different messages.
To verify that the remaining conditions are skipped, let us try another example.
a_number = 15if a_number % 2 == 0:
print('{} is divisible by 2'.format(a_number))
elif a_number % 3 == 0:
print('{} is divisible by 3'.format(a_number))
elif a_number % 5 == 0:
print('{} is divisible by 5'.format(a_number))
elif a_number % 7 == 0:
print('{} is divisible by 7'.format(a_number))15 is divisible by 3
Note that the message 15 is divisible by 5 is not printed because the condition a_number % 5 == 0 isn't evaluated, since the previous condition a_number % 3 == 0 evaluates to True. This is the key difference between using a chain of if, elif, elif... statements vs. a chain of if statements, where each condition is evaluated independently.
if a_number % 2 == 0:
print('{} is divisible by 2'.format(a_number))
if a_number % 3 == 0:
print('{} is divisible by 3'.format(a_number))
if a_number % 5 == 0:
print('{} is divisible by 5'.format(a_number))
if a_number % 7 == 0:
print('{} is divisible by 7'.format(a_number))15 is divisible by 3
15 is divisible by 5
Using if, elif, and else together
You can also include an else statement at the end of a chain of if, elif... statements. This code within the else block is evaluated when none of the conditions hold true.
a_number = 49if a_number % 2 == 0:
print('{} is divisible by 2'.format(a_number))
elif a_number % 3 == 0:
print('{} is divisible by 3'.format(a_number))
elif a_number % 5 == 0:
print('{} is divisible by 5'.format(a_number))
else:
print('All checks failed!')
print('{} is not divisible by 2, 3 or 5'.format(a_number))All checks failed!
49 is not divisible by 2, 3 or 5
Conditions can also be combined using the logical operators and, or and not. Logical operators are explained in detail in the first tutorial.
a_number = 12if a_number % 3 == 0 and a_number % 5 == 0:
print("The number {} is divisible by 3 and 5".format(a_number))
elif not a_number % 5 == 0:
print("The number {} is not divisible by 5".format(a_number))The number 12 is not divisible by 5
Non-Boolean Conditions
Note that conditions do not necessarily have to be booleans. In fact, a condition can be any value. The value is converted into a boolean automatically using the bool operator. This means that falsy values like 0, '', {}, [], etc. evaluate to False and all other values evaluate to True.
if '':
print('The condition evaluted to True')
else:
print('The condition evaluted to False')The condition evaluted to False
if 'Hello':
print('The condition evaluted to True')
else:
print('The condition evaluted to False')The condition evaluted to True
if { 'a': 34 }:
print('The condition evaluted to True')
else:
print('The condition evaluted to False')The condition evaluted to True
if None:
print('The condition evaluted to True')
else:
print('The condition evaluted to False')The condition evaluted to False
Nested conditional statements
The code inside an if block can also include an if statement inside it. This pattern is called nesting and is used to check for another condition after a particular condition holds true.
a_number = 15if a_number % 2 == 0:
print("{} is even".format(a_number))
if a_number % 3 == 0:
print("{} is also divisible by 3".format(a_number))
else:
print("{} is not divisibule by 3".format(a_number))
else:
print("{} is odd".format(a_number))
if a_number % 5 == 0:
print("{} is also divisible by 5".format(a_number))
else:
print("{} is not divisible by 5".format(a_number))15 is odd
15 is also divisible by 5
Notice how the print statements are indented by 8 spaces to indicate that they are part of the inner if/else blocks.
Nested
if,elsestatements are often confusing to read and prone to human error. It's good to avoid nesting whenever possible, or limit the nesting to 1 or 2 levels.
Shorthand if conditional expression
A frequent use case of the if statement involves testing a condition and setting a variable's value based on the condition.
a_number = 13
if a_number % 2 == 0:
parity = 'even'
else:
parity = 'odd'
print('The number {} is {}.'.format(a_number, parity))The number 13 is odd.
Python provides a shorter syntax, which allows writing such conditions in a single line of code. It is known as a conditional expression, sometimes also referred to as a ternary operator. It has the following syntax:
x = true_value if condition else false_value
It has the same behavior as the following if-else block:
if condition:
x = true_value
else:
x = false_value
Let's try it out for the example above.
parity = 'even' if a_number % 2 == 0 else 'odd'print('The number {} is {}.'.format(a_number, parity))The number 13 is odd.
Statements and Expressions
The conditional expression highlights an essential distinction between statements and expressions in Python.
Statements: A statement is an instruction that can be executed. Every line of code we have written so far is a statement e.g. assigning a variable, calling a function, conditional statements using
if,else, andelif, loops usingforandwhileetc.
Expressions: An expression is some code that evaluates to a value. Examples include values of different data types, arithmetic expressions, conditions, variables, function calls, conditional expressions, etc.
Most expressions can be executed as statements, but not all statements are expressions. For example, the regular if statement is not an expression since it does not evaluate to a value. It merely performs some branching in the code. Similarly, loops and function definitions are not expressions (we'll learn more about these in later sections).
As a rule of thumb, an expression is anything that can appear on the right side of the assignment operator =. You can use this as a test for checking whether something is an expression or not. You'll get a syntax error if you try to assign something that is not an expression.
# if statement
result = if a_number % 2 == 0:
'even'
else:
'odd' File "<ipython-input-30-f24978c5423e>", line 2
result = if a_number % 2 == 0:
^
SyntaxError: invalid syntax
# if expression
result = 'even' if a_number % 2 == 0 else 'odd'The pass statement
if statements cannot be empty, there must be at least one statement in every if and elif block. You can use the pass statement to do nothing and avoid getting an error.
a_number = 9if a_number % 2 == 0:
elif a_number % 3 == 0:
print('{} is divisible by 3 but not divisible by 2') File "<ipython-input-33-77268dd66617>", line 2
elif a_number % 3 == 0:
^
IndentationError: expected an indented block
if a_number % 2 == 0:
pass
elif a_number % 3 == 0:
print('{} is divisible by 3 but not divisible by 2'.format(a_number))9 is divisible by 3 but not divisible by 2
Iteration with while loops
Another powerful feature of programming languages, closely related to branching, is running one or more statements multiple times. This feature is often referred to as iteration on looping, and there are two ways to do this in Python: using while loops and for loops.
while loops have the following syntax:
while condition:
statement(s)
Statements in the code block under while are executed repeatedly as long as the condition evaluates to True. Generally, one of the statements under while makes some change to a variable that causes the condition to evaluate to False after a certain number of iterations.
Let's try to calculate the factorial of 100 using a while loop. The factorial of a number n is the product (multiplication) of all the numbers from 1 to n, i.e., 1*2*3*...*(n-2)*(n-1)*n.
result = 1
i = 1
while i <= 100:
result = result * i
i = i+1
print('The factorial of 100 is: {}'.format(result))The factorial of 100 is: 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
Here's how the above code works:
-
We initialize two variables,
resultand,i.resultwill contain the final outcome. Andiis used to keep track of the next number to be multiplied withresult. Both are initialized to 1 (can you explain why?) -
The condition
i <= 100holds true (sinceiis initially1), so thewhileblock is executed. -
The
resultis updated toresult * i,iis increased by1and it now has the value2. -
At this point, the condition
i <= 100is evaluated again. Since it continues to hold true,resultis again updated toresult * i, andiis increased to3. -
This process is repeated till the condition becomes false, which happens when
iholds the value101. Once the condition evaluates toFalse, the execution of the loop ends, and theprintstatement below it is executed.
Can you see why result contains the value of the factorial of 100 at the end? If not, try adding print statements inside the while block to print result and i in each iteration.
Iteration is a powerful technique because it gives computers a massive advantage over human beings in performing thousands or even millions of repetitive operations really fast. With just 4-5 lines of code, we were able to multiply 100 numbers almost instantly. The same code can be used to multiply a thousand numbers (just change the condition to
i <= 1000) in a few seconds.
You can check how long a cell takes to execute by adding the magic command %%time at the top of a cell. Try checking how long it takes to compute the factorial of 100, 1000, 10000, 100000, etc.
%%time
result = 1
i = 1
while i <= 1000:
result *= i # same as result = result * i
i += 1 # same as i = i+1
print(result)402387260077093773543702433923003985719374864210714632543799910429938512398629020592044208486969404800479988610197196058631666872994808558901323829669944590997424504087073759918823627727188732519779505950995276120874975462497043601418278094646496291056393887437886487337119181045825783647849977012476632889835955735432513185323958463075557409114262417474349347553428646576611667797396668820291207379143853719588249808126867838374559731746136085379534524221586593201928090878297308431392844403281231558611036976801357304216168747609675871348312025478589320767169132448426236131412508780208000261683151027341827977704784635868170164365024153691398281264810213092761244896359928705114964975419909342221566832572080821333186116811553615836546984046708975602900950537616475847728421889679646244945160765353408198901385442487984959953319101723355556602139450399736280750137837615307127761926849034352625200015888535147331611702103968175921510907788019393178114194545257223865541461062892187960223838971476088506276862967146674697562911234082439208160153780889893964518263243671616762179168909779911903754031274622289988005195444414282012187361745992642956581746628302955570299024324153181617210465832036786906117260158783520751516284225540265170483304226143974286933061690897968482590125458327168226458066526769958652682272807075781391858178889652208164348344825993266043367660176999612831860788386150279465955131156552036093988180612138558600301435694527224206344631797460594682573103790084024432438465657245014402821885252470935190620929023136493273497565513958720559654228749774011413346962715422845862377387538230483865688976461927383814900140767310446640259899490222221765904339901886018566526485061799702356193897017860040811889729918311021171229845901641921068884387121855646124960798722908519296819372388642614839657382291123125024186649353143970137428531926649875337218940694281434118520158014123344828015051399694290153483077644569099073152433278288269864602789864321139083506217095002597389863554277196742822248757586765752344220207573630569498825087968928162753848863396909959826280956121450994871701244516461260379029309120889086942028510640182154399457156805941872748998094254742173582401063677404595741785160829230135358081840096996372524230560855903700624271243416909004153690105933983835777939410970027753472000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
CPU times: user 905 µs, sys: 362 µs, total: 1.27 ms
Wall time: 974 µs
Here's another example that uses two while loops to create an interesting pattern.
line = '*'
max_length = 10
while len(line) < max_length:
print(line)
line += "*"
while len(line) > 0:
print(line)
line = line[:-1]*
**
***
****
*****
******
*******
********
*********
**********
*********
********
*******
******
*****
****
***
**
*
Can you see how the above example works? As an exercise, try printing the following pattern using a while loop (Hint: use string concatenation):
*
**
***
****
*****
******
*****
****
***
**
*
Here's another one, putting the two together:
*
***
*****
*******
*********
***********
*********
*******
*****
***
*
Infinite Loops
Suppose the condition in a while loop always holds true. In that case, Python repeatedly executes the code within the loop forever, and the execution of the code never completes. This situation is called an infinite loop. It generally indicates that you've made a mistake in your code. For example, you may have provided the wrong condition or forgotten to update a variable within the loop, eventually falsifying the condition.
If your code is stuck in an infinite loop during execution, just press the "Stop" button on the toolbar (next to "Run") or select "Kernel > Interrupt" from the menu bar. This will interrupt the execution of the code. The following two cells both lead to infinite loops and need to be interrupted.
# INFINITE LOOP - INTERRUPT THIS CELL
result = 1
i = 1
while i <= 100:
result = result * i
# forgot to increment i---------------------------------------------------------------------------
KeyboardInterrupt Traceback (most recent call last)
<ipython-input-41-5234d8c241fc> in <module>
5
6 while i <= 100:
----> 7 result = result * i
8 # forgot to increment i
KeyboardInterrupt: # INFINITE LOOP - INTERRUPT THIS CELL
result = 1
i = 1
while i > 0 : # wrong condition
result *= i
i += 1---------------------------------------------------------------------------
KeyboardInterrupt Traceback (most recent call last)
<ipython-input-42-c4abf72fce4d> in <module>
5
6 while i > 0 : # wrong condition
----> 7 result *= i
8 i += 1
KeyboardInterrupt: break and continue statements
You can use the break statement within the loop's body to immediately stop the execution and break out of the loop (even if the condition provided to while still holds true).
i = 1
result = 1
while i <= 100:
result *= i
if i == 42:
print('Magic number 42 reached! Stopping execution..')
break
i += 1
print('i:', i)
print('result:', result)Magic number 42 reached! Stopping execution..
i: 42
result: 1405006117752879898543142606244511569936384000000000
As you can see above, the value of i at the end of execution is 42. This example also shows how you can use an if statement within a while loop.
Sometimes you may not want to end the loop entirely, but simply skip the remaining statements in the loop and continue to the next loop. You can do this using the continue statement.
i = 1
result = 1
while i < 20:
i += 1
if i % 2 == 0:
print('Skipping {}'.format(i))
continue
print('Multiplying with {}'.format(i))
result = result * i
print('i:', i)
print('result:', result)Skipping 2
Multiplying with 3
Skipping 4
Multiplying with 5
Skipping 6
Multiplying with 7
Skipping 8
Multiplying with 9
Skipping 10
Multiplying with 11
Skipping 12
Multiplying with 13
Skipping 14
Multiplying with 15
Skipping 16
Multiplying with 17
Skipping 18
Multiplying with 19
Skipping 20
i: 20
result: 654729075
In the example above, the statement result = result * i inside the loop is skipped when i is even, as indicated by the messages printed during execution.
Logging: The process of adding
Iteration with for loops
A for loop is used for iterating or looping over sequences, i.e., lists, tuples, dictionaries, strings, and ranges. For loops have the following syntax:
for value in sequence:
statement(s)
The statements within the loop are executed once for each element in sequence. Here's an example that prints all the element of a list.
days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']
for day in days:
print(day)Monday
Tuesday
Wednesday
Thursday
Friday
Let's try using for loops with some other data types.
# Looping over a string
for char in 'Monday':
print(char)M
o
n
d
a
y
# Looping over a tuple
for fruit in ('Apple', 'Banana', 'Guava'):
print("Here's a fruit:", fruit)Here's a fruit: Apple
Here's a fruit: Banana
Here's a fruit: Guava
# Looping over a dictionary
person = {
'name': 'John Doe',
'sex': 'Male',
'age': 32,
'married': True
}
for key in person:
print("Key:", key, ",", "Value:", person[key])Key: name , Value: John Doe
Key: sex , Value: Male
Key: age , Value: 32
Key: married , Value: True
Note that while using a dictionary with a for loop, the iteration happens over the dictionary's keys. The key can be used within the loop to access the value. You can also iterate directly over the values using the .values method or over key-value pairs using the .items method.
for value in person.values():
print(value)John Doe
Male
32
True
for key_value_pair in person.items():
print(key_value_pair)('name', 'John Doe')
('sex', 'Male')
('age', 32)
('married', True)
Since a key-value pair is a tuple, we can also extract the key & value into separate variables.
for key, value in person.items():
print("Key:", key, ",", "Value:", value)Key: name , Value: John Doe
Key: sex , Value: Male
Key: age , Value: 32
Key: married , Value: True
Iterating using range and enumerate
The range function is used to create a sequence of numbers that can be iterated over using a for loop. It can be used in 3 ways:
range(n)- Creates a sequence of numbers from0ton-1range(a, b)- Creates a sequence of numbers fromatob-1range(a, b, step)- Creates a sequence of numbers fromatob-1with increments ofstep
Let's try it out.
for i in range(7):
print(i)0
1
2
3
4
5
6
for i in range(3, 10):
print(i)3
4
5
6
7
8
9
for i in range(3, 14, 4):
print(i)3
7
11
Ranges are used for iterating over lists when you need to track the index of elements while iterating.
a_list = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']
for i in range(len(a_list)):
print('The value at position {} is {}.'.format(i, a_list[i]))The value at position 0 is Monday.
The value at position 1 is Tuesday.
The value at position 2 is Wednesday.
The value at position 3 is Thursday.
The value at position 4 is Friday.
Another way to achieve the same result is by using the enumerate function with a_list as an input, which returns a tuple containing the index and the corresponding element.
for i, val in enumerate(a_list):
print('The value at position {} is {}.'.format(i, val))The value at position 0 is Monday.
The value at position 1 is Tuesday.
The value at position 2 is Wednesday.
The value at position 3 is Thursday.
The value at position 4 is Friday.
break, continue and pass statements
Similar to while loops, for loops also support the break and continue statements. break is used for breaking out of the loop and continue is used for skipping ahead to the next iteration.
weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']for day in weekdays:
print('Today is {}'.format(day))
if (day == 'Wednesday'):
print("I don't work beyond Wednesday!")
breakToday is Monday
Today is Tuesday
Today is Wednesday
I don't work beyond Wednesday!
for day in weekdays:
if (day == 'Wednesday'):
print("I don't work on Wednesday!")
continue
print('Today is {}'.format(day))Today is Monday
Today is Tuesday
I don't work on Wednesday!
Today is Thursday
Today is Friday
Like if statements, for loops cannot be empty, so you can use a pass statement if you don't want to execute any statements inside the loop.
for day in weekdays:
passNested for and while loops
Similar to conditional statements, loops can be nested inside other loops. This is useful for looping lists of lists, dictionaries etc.
persons = [{'name': 'John', 'sex': 'Male'}, {'name': 'Jane', 'sex': 'Female'}]
for person in persons:
for key in person:
print(key, ":", person[key])
print(" ")name : John
sex : Male
name : Jane
sex : Female
days = ['Monday', 'Tuesday', 'Wednesday']
fruits = ['apple', 'banana', 'guava']
for day in days:
for fruit in fruits:
print(day, fruit)Monday apple
Monday banana
Monday guava
Tuesday apple
Tuesday banana
Tuesday guava
Wednesday apple
Wednesday banana
Wednesday guava
With this, we conclude our discussion of branching and loops in Python.
Further Reading and References
We've covered a lot of ground in just 3 tutorials.
Following are some resources to learn about more about conditional statements and loops in Python:
- Python Tutorial at W3Schools: https://www.w3schools.com/python/
- Practical Python Programming: https://dabeaz-course.github.io/practical-python/Notes/Contents.html
- Python official documentation: https://docs.python.org/3/tutorial/index.html
Questions for Revision
Try answering the following questions to test your understanding of the topics covered in this notebook:
- What is branching in programming languages?
- What is the purpose of the
ifstatement in Python? - What is the syntax of the
ifstatement? Give an example. - What is indentation? Why is it used?
- What is an indented block of statements?
- How do you perform indentation in Python?
- What happens if some code is not indented correctly?
- What happens when the condition within the
ifstatement evaluates toTrue? What happens if the condition evaluates forfalse? - How do you check if a number is even?
- What is the purpose of the
elsestatement in Python? - What is the syntax of the
elsestatement? Give an example. - Write a program that prints different messages based on whether a number is positive or negative.
- Can the
elsestatement be used without anifstatement? - What is the purpose of the
elifstatement in Python? - What is the syntax of the
elifstatement? Give an example. - Write a program that prints different messages for different months of the year.
- Write a program that uses
if,elif, andelsestatements together. - Can the
elifstatement be used without anifstatement? - Can the
elifstatement be used without anelsestatement? - What is the difference between a chain of
if,elif,elif… statements and a chain ofif,if,if… statements? Give an example. - Can non-boolean conditions be used with
ifstatements? Give some examples. - What are nested conditional statements? How are they useful?
- Give an example of nested conditional statements.
- Why is it advisable to avoid nested conditional statements?
- What is the shorthand
ifconditional expression? - What is the syntax of the shorthand
ifconditional expression? Give an example. - What is the difference between the shorthand
ifexpression and the regularifstatement? - What is a statement in Python?
- What is an expression in Python?
- What is the difference between statements and expressions?
- Is every statement an expression? Give an example or counterexample.
- Is every expression a statement? Give an example or counterexample.
- What is the purpose of the pass statement in
ifblocks? - What is iteration or looping in programming languages? Why is it useful?
- What are the two ways for performing iteration in Python?
- What is the purpose of the
whilestatement in Python? - What is the syntax of the
whitestatement in Python? Give an example. - Write a program to compute the sum of the numbers 1 to 100 using a while loop.
- Repeat the above program for numbers up to 1000, 10000, and 100000. How long does it take each loop to complete?
- What is an infinite loop?
- What causes a program to enter an infinite loop?
- How do you interrupt an infinite loop within Jupyter?
- What is the purpose of the
breakstatement in Python? - Give an example of using a
breakstatement within a while loop. - What is the purpose of the
continuestatement in Python? - Give an example of using the
continuestatement within a while loop. - What is logging? How is it useful?
- What is the purpose of the
forstatement in Python? - What is the syntax of
forloops? Give an example. - How are for loops and while loops different?
- How do you loop over a string? Give an example.
- How do you loop over a list? Give an example.
- How do you loop over a tuple? Give an example.
- How do you loop over a dictionary? Give an example.
- What is the purpose of the
rangestatement? Give an example. - What is the purpose of the
enumeratestatement? Give an example. - How are the
break,continue, andpassstatements used in for loops? Give examples. - Can loops be nested within other loops? How is nesting useful?
- Give an example of a for loop nested within another for loop.
- Give an example of a while loop nested within another while loop.
- Give an example of a for loop nested within a while loop.
- Give an example of a while loop nested within a for loop.
Writing Reusable Code Using Functions in Python
This tutorial is the fourth in a series on introduction to programming using the Python language. These tutorials take a practical coding-based approach, and the best way to learn the material is to execute the code and experiment with the examples.
Creating and using functions
A function is a reusable set of instructions. A function takes one or more inputs, performs certain operations, and often returns an output. Python provides many in-built functions like print, and also allows you to define your own functions.
You can define a new function using the def keyword.
def say_hello():
print('Hello there!')
print('How are you?')Note the round brackets or parantheses () and colon : after the function's name. Both are essential parts of the syntax for defining a function. The body of the function can contain one or more statements which are to be executed when the function is called. Simlar to conditional statements and loops, the statements must be indented by 4 spaces.
The statements inside a function's body are not executed when a function is defined. To execute the statements, we need to call or invoke the function.
say_hello()Hello there!
How are you?
Function arguments
Functions can also accept one or more values as inputs (also knows as arguments or parameters). Arguments help us write flexible functions which can perform the same operation on different values. Further, functions can also return a value as a result using the return keyword, which can be stored in a variable or used in other expressions.
Here's a function that filters out the even numbers from a list.
def filter_even(number_list):
result_list = []
for number in number_list:
if number % 2 == 0:
result_list.append(number)
return result_listeven_list = filter_even([1, 2, 3, 4, 5, 6, 7])even_list[2, 4, 6]Writing great functions in Python
As a programmer, you will spend most of your time writing and using functions, and Python offers many features to make your functions powerful and flexible. Let's explore some of these by solving a problem:
Radha is planning to buy a house that costs
$1,260,000. She considering two options to finance her purchase:
- Option 1: Make an immediate down payment of
$300,000, and take loan 8-year loan with an interest rate of 10% (compounded monthly) for the remaining amount.- Option 2: Take a 10-year loan with an interest rate of 8% (compounded monthly) for the entire amount.
Both these loans have to paid back in equal monthly installments (EMIs). Which loan has a lower EMI among the two?
Since we need to compare the EMIs for two loan options, it might be helpful to define a function to calculate the EMI for a loan, given inputs like the cost of the house, the down payment, duration of the loan, rate of interest etc. We'll build this function step by step.
To begin, let's write a simple function that calculates the EMI on the entire cost of the house, assuming that the loan has to be paid back in one year, and there is no interest or down payment.
def loan_emi(amount):
emi = amount / 12
print('The EMI is ${}'.format(emi))loan_emi(1260000)The EMI is $105000.0
Local variables and scope
Let's add a second argument to account for the duration of the loan, in months.
def loan_emi(amount, duration):
emi = amount / duration
print('The EMI is ${}'.format(emi))Note that the variable emi defined inside the function is not accessible outside the function. The same is true for the parameters amount and duration. These are all local variables that lie within the scope of the function.
Scope: Scope refers to the region within the code where a certain variable is visible. Every function (or class definition) defines a scope within Python. Variables defined in this scope are called local variables. Variables that are available everywhere are called global variables. Scope rules allow you to use the same variable names in different functions without sharing values from one to the other.
emi---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-9-e5795bfcf3c1> in <module>
----> 1 emi
NameError: name 'emi' is not definedamount---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-10-9b8f90fabac0> in <module>
----> 1 amount
NameError: name 'amount' is not definedduration---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-11-5b6659652103> in <module>
----> 1 duration
NameError: name 'duration' is not definedWe can now compare a 6-year loan vs. a 10-year loan (assuming no down payment or interest).
loan_emi(1260000, 8*12)The EMI is $13125.0
loan_emi(1260000, 10*12)The EMI is $10500.0
Return values
As you might expect, the EMI for the 6-year loan is higher compared to the 10-year loan. Right now we're printing out the result, but it would be better to return it and store the results in variables for easier comparison. We can do this using the return statement
def loan_emi(amount, duration):
emi = amount / duration
return emiemi1 = loan_emi(1260000, 8*12)emi2 = loan_emi(1260000, 10*12)emi113125.0emi210500.0Optional arguments
Let's now add another argument to account for the immediate down payment. We'll make this an optional argument, with a default value of 0.
def loan_emi(amount, duration, down_payment=0):
loan_amount = amount - down_payment
emi = loan_amount / duration
return emiemi1 = loan_emi(1260000, 8*12, 3e5)emi110000.0emi2 = loan_emi(1260000, 10*12)emi210500.0Next, let's add the interest calculation into the function. Here's the formula used to calculate the EMI for a loan:
where:
Pis the loan amount (principal)nis the no. of monthsris the rate of interest per month
The derivation of this forumula is beyond the scope of this tutorial. See this video for an explanation: https://youtu.be/Coxza9ugW4E
def loan_emi(amount, duration, rate, down_payment=0):
loan_amount = amount - down_payment
emi = loan_amount * rate * ((1+rate)**duration) / (((1+rate)**duration)-1)
return emiNote that while defining the function, required arguments like cost, duration and rate must appear before optional arguments like down_payment.
Let's calculate the EMI for Option 1
loan_emi(1260000, 8*12, 0.1/12, 3e5)14567.19753389219While calculating the EMI for Option 2, we need not include the down_payment argument.
loan_emi(1260000, 10*12, 0.08/12)15287.276888775077Named arguments
Invoking a function with many arguments can often get confusing, and is prone to human errors. Python provides the option of invoking functions with named arguments, for better clarity. Function invocation can also be split into multiple lines.
emi1 = loan_emi(
amount=1260000,
duration=8*12,
rate=0.1/12,
down_payment=3e5
)emi114567.19753389219emi2 = loan_emi(amount=1260000, duration=10*12, rate=0.08/12)emi215287.276888775077Modules and library functions
We can already see that the EMI for Option 1 seems to be lower than the EMI for Option 2. However, it would be nice to round up the amount to full dollars, rather than including digits afer the decimal. To achieve this, we might want to write a function which can take a number and round it up to the next integer (e.g. 1.2 is rounded up to 2). That would be a good exercise to try out!
However, since rounding numbers is a fairly common operation, Python provides a function for it (along with thousands of other functions) as part of the Python Standard Library. Functions are organized into modules, which need to imported in order to use the functions they contain.
Modules: Modules are files containing Python code (variables, functions, classes etc.). They provide a way of organizing the code for large Python projects into files and folders. The key benefit offered by modules is namespaces - a module or a specific function/class/variable from a module has to imported before it can be used within a Python script or notebook. This provides encapsulation and avoid naming conflicts between your code vs. a module, or across modules.
For rounding up our EMI amounts, we can use the ceil function (short for ceiling) from the math module. Let's import the module and use it to round up the number 1.2 .
import mathhelp(math.ceil)Help on built-in function ceil in module math:
ceil(x, /)
Return the ceiling of x as an Integral.
This is the smallest integer >= x.
math.ceil(1.2)2Let's now use the math.ceil function within the home_loan_emi function to round up the EMI amount.
Using function to build other functions is a great way to reuse code and implement complex business logic while still keeping the code small, understandable and manageable. Ideally, one a fuction should do one thing, and one thing only. If you find yourself doing too many things within a single function, you should consider splitting it into 2 or more smaller, independent functions. As a rule of thumb, try to limit your functions to 10 lines of code or less. Good programmers always write small, simple and readable functions.
def loan_emi(amount, duration, rate, down_payment=0):
loan_amount = amount - down_payment
emi = loan_amount * rate * ((1+rate)**duration) / (((1+rate)**duration)-1)
emi = math.ceil(emi)
return emiemi1 = loan_emi(
amount=1260000,
duration=8*12,
rate=0.1/12,
down_payment=3e5
)emi114568emi2 = loan_emi(amount=1260000, duration=10*12, rate=0.08/12)emi215288Let's compare the EMIs and display a message for the option with the lower EMI.
if emi1 < emi2:
print("Option 1 has the lower EMI: ${}".format(emi1))
else:
print("Option 2 has the lower EMI: ${}".format(emi2))Option 1 has the lower EMI: $14568
Reusing and improving functions
Now we know for cetain that "Option 1" has the lower EMI among the two options. But what's even better is that we now have a handy function loan_emi that can be used to solve many other similar problems with just a few lines of code. Let's try it with a couple more problems.
Q: Shaun is currenly paying back a home loan for a house a few years go. The cost of the house was
$800,000. Shaun made a down payment of25%of the cost, and financed the remaining amount using a 6-year loan with an interest rate of7%per annum (compounded monthly). Shaun is now buying a car worth$60,000, which he is planning to finance using a 1-year loan with an interest rate of12%per annum. Both loans are paid back in EMIs. What is the total monthly payment Shaun makes towards loan repayment?
This question is now straightforward to solve, using the loan_emi function we've already defined.
cost_of_house = 800000
home_loan_duration = 6*12 # months
home_loan_rate = 0.07/12 # monthly
home_down_payment = .25 * 800000
emi_house = loan_emi(amount=cost_of_house,
duration=home_loan_duration,
rate=home_loan_rate,
down_payment=home_down_payment)
emi_house10230cost_of_car = 60000
car_loan_duration = 1*12 # months
car_loan_rate = .12/12 # monthly
emi_car = loan_emi(amount=cost_of_car,
duration=car_loan_duration,
rate=car_loan_rate)
emi_car5331print("Shaun makes a total monthly payment of ${} towards loan repayments.".format(emi_house+emi_car))Shaun makes a total monthly payment of $15561 towards loan repayments.
Exceptions and try-except
Q: If you borrow
$100,000using a 10-year loan with an interest rate of 9% per annum, what is the total amount you end up paying as interest?
One way to solve this problem is to compare the EMIs for two loans: one with the given rate of interest, and another with a 0% rate of interest. The total interest paid is then simply the sum of monthly differences over the duration of the loan.
emi_with_interest = loan_emi(amount=100000, duration=10*12, rate=0.09/12)
emi_with_interest1267emi_without_interest = loan_emi(amount=100000, duration=10*12, rate=0./12)
emi_without_interest---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
<ipython-input-44-2550005b6f67> in <module>
----> 1 emi_without_interest = loan_emi(amount=100000, duration=10*12, rate=0./12)
<ipython-input-34-ad16168becb0> in loan_emi(amount, duration, rate, down_payment)
1 def loan_emi(amount, duration, rate, down_payment=0):
2 loan_amount = amount - down_payment
----> 3 emi = loan_amount * rate * ((1+rate)**duration) / (((1+rate)**duration)-1)
4 emi = math.ceil(emi)
5 return emi
ZeroDivisionError: float division by zeroSomething seems to have gone wrong! If you look at the error message above carefully, Python tells us exactly what is gone wrong. Python throws a ZeroDivisionError with a message indicating that we're trying to divide a number by zero. This is an exception that stops further execution of the program.
Exception: Even if a statement or expression is syntactically correct, it may cause an error when an attempt is made to execute it. Errors detected during execution are called exceptions. We refer to exceptions as being typically stop further execution of the program, unless they are handled within the program using
try-exceptstatements.
Python provides many built-in exceptions that are thrown when built-in operators, functions or methods are used in an incorrect manner: https://docs.python.org/3/library/exceptions.html#built-in-exceptions . You can also define your own custom exception by extending Exception class (more on that later).
You can use the try and except statements to handle and exception. Here's an example:
try:
print("Now computing the result..")
result = 5 / 0
print("Computation was completed successfully")
except ZeroDivisionError:
print("Failed to compute result because you were trying to divide by zero")
result = None
print(result)Now computing the result..
Failed to compute result because you were trying to divide by zero
None
When an exception occurs in the code inside a try block, the rest of the statements in the block are skipped, and except statement is executed. If the type of exception throw matches the type of exception being handled by the except statement, then the code inside the except block is executed and the program execution then returns to the normal flow.
You can also handle more than one type of exception using multiple except statements. Learn more about exceptions here: https://www.w3schools.com/python/python_try_except.asp .
Let's enhance the loan_emi function to use try-execpt to handle the scenario where the rate of intersest is 0%. It's common practice to make changes/enhancements to functions over time, as new scenarios and use cases come up. It makes functions more flexible & powerful.
def loan_emi(amount, duration, rate, down_payment=0):
loan_amount = amount - down_payment
try:
emi = loan_amount * rate * ((1+rate)**duration) / (((1+rate)**duration)-1)
except ZeroDivisionError:
emi = loan_amount / duration
emi = math.ceil(emi)
return emiWe can use the updated loan_emi function to solve our problem.
Q: If you borrow
$100,000using a 10-year loan with an interest rate of 9% per annum, what is the total amount you end up paying as interest?
emi_with_interest = loan_emi(amount=100000, duration=10*12, rate=0.09/12)
emi_with_interest1267emi_without_interest = loan_emi(amount=100000, duration=10*12, rate=0)
emi_without_interest834total_interest = (emi_with_interest - emi_without_interest) * 10*12print("The total interest paid is ${}.".format(total_interest))The total interest paid is $51960.
Documenting functions using Docstrings
We can add some documentation within our function using a docstring. A docstring is simply a string that appears as the first statement within the function body, and is used by the help function. A good docstring describes what the function does, and provides some explanation about the arguments.
def loan_emi(amount, duration, rate, down_payment=0):
"""Calculates the equal montly installment (EMI) for a loan.
Arguments:
amount - Total amount to be spent (loan + down payment)
duration - Duration of the loan (in months)
rate - Rate of interest (monthly)
down_payment (optional) - Optional intial payment (deducted from amount)
"""
loan_amount = amount - down_payment
try:
emi = loan_amount * rate * ((1+rate)**duration) / (((1+rate)**duration)-1)
except ZeroDivisionError:
emi = loan_amount / duration
emi = math.ceil(emi)
return emiIn the docstring above, we've provided some additional information that the duration and rate are both measured in months. You might even consider naming the arguments duration_months and rate_monthly, to avoid any confusion whatsoever. Can you think of some other ways in which the function can be improved?
help(loan_emi)Help on function loan_emi in module __main__:
loan_emi(amount, duration, rate, down_payment=0)
Calculates the equal montly installment (EMI) for a loan.
Arguments:
amount - Total amount to be spent (loan + down payment)
duration - Duration of the loan (in months)
rate - Rate of interest (monthly)
down_payment (optional) - Optional intial payment (deducted from amount)
Summary and Further Reading
With this we complete our discussion of functions in Python. We've covered the following topics in this tutorial:
- Creating and using functions
- Functions with one or more arguments
- Local variables and scope
- Returning values using
return - Using default arguments to make a function flexible
- Using named arguments while invoking a function
- Importing modules and using library functions
- Reusing and improving functions to handle new use cases
- Handling exceptions with
try-except - Documenting functions using docstrings
This is by no means an exhaustive or comprehensive tutorial on functions in Python. Here are few more topics to learn about:
- Functions with an arbitrary number of arguments using (
*argsand**kwargs) - Defining functions inside functions (and closures)
- A function that invokes itself (recursion)
- Functions that accept other functions as arguments or return other functions
- Functions that enhance other functions (decorators)
Following are some resources to learn about more functions in Python:
- Python Tutorial at W3Schools: https://www.w3schools.com/python/
- Practical Python Programming: https://dabeaz-course.github.io/practical-python/Notes/Contents.html
- Python official documentation: https://docs.python.org/3/tutorial/index.html
