Python Functions and Scope
Python Functions and Scope
This tutorial is a part of Data Analysis with Python: Zero to Pandas and Zero to Data Analyst Science Bootcamp.
These tutorials take a practical and coding-focused approach. The best way to learn the material is to execute the
code and experiment with it yourself.
today = "Saturday"
print("Today is", today)
Today is Saturday
def say_hello():
say_hello
print('Hello there!')
print('How are you?')
Note the round brackets or parentheses () and colon : after the function's name. Both are essential parts of
the syntax. The function's body contains an indented block of statements. The statements inside a function's body
are not executed when the function is de ned. To execute the statements, we need to call or invoke the function.
say_hello()
Hello there!
How are you?
Function arguments
Functions can accept zero or more values as inputs (also knows as arguments or parameters). Arguments help us
write exible functions that can perform the same operations on different values. Further, functions can return a
result that can be stored in a variable or used in other expressions.
Here's a function that lters out the even numbers from a list and returns a new list using the return keyword.
def filter_even(number_list):
filter_even
result_list = []
for number in number_list:
if number % 2 == 0:
result_list.append(number)
return result_list
Can you understand what the function does by looking at the code? If not, try executing each line of the function's
body separately within a code cell with an actual list of numbers in place of number_list .
even_list
[2, 4, 6]
Radha is planning to buy a house that costs $1,260,000 . She considering two options to nance 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 be 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, de ning a function to calculate the EMI for a loan would
be a great idea. The inputs to the function would be cost of the house, the down payment, duration of the loan,
rate of interest etc. We'll build this function step by step.
First, let's write a simple function that calculates the EMI on the entire cost of the house, assuming that the loan
must be paid back in one year, and there is no interest or down payment.
def loan_emi(amount):
loan_emi
emi = amount / 12
print('The EMI is ${}'.format(emi))
loan_emi(1260000)
def loan_emi(amount,
loan_emi duration):
emi = amount / duration
print('The EMI is ${}'.format(emi))
Note that the variable emi de ned inside the function is not accessible outside. 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 particular variable is visible. Every function (or
class de nition) de nes a scope within Python. Variables de ned 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-10-e5795bfcf3c1> in <module>
----> 1 emi
amount
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-11-9b8f90fabac0> in <module>
----> 1 amount
duration
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-12-5b6659652103> in <module>
----> 1 duration
We can now compare a 8-year loan vs. a 10-year loan (assuming no down payment or interest).
loan_emi(1260000, 8*12)
loan_emi(1260000, 10*12)
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. 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,
loan_emi duration):
emi = amount / duration
return emi
emi1
13125.0
emi2
10500.0
Optional arguments
Next, let's 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,
loan_emi duration, down_payment=0):
loan_amount = amount - down_payment
emi = loan_amount / duration
return emi
emi1
10000.0
emi2
10500.0
Next, let's add the interest calculation into the function. Here's the formula used to calculate the EMI for a loan:
where:
The derivation of this formula is beyond the scope of this tutorial. See this video for an explanation:
https://youtu.be/Coxza9ugW4E .
def loan_emi(amount,
loan_emi duration, rate, down_payment=0):
loan_amount = amount - down_payment
emi = loan_amount * rate * ((1+rate)**duration) / (((1+rate)**duration)-1)
return emi
Note that while de ning the function, required arguments like cost , duration and rate must appear before
optional arguments like down_payment .
14567.19753389219
While calculating the EMI for Option 2, we need not include the down_payment argument.
15287.276888775077
Named 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. You can also split function invocation
into multiple lines.
emi1 = loan_emi(
amount=1260000,
duration=8*12,
rate=0.1/12,
down_payment=3e5
)
emi1
14567.19753389219
emi2
15287.276888775077
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 that
need to be imported to use the functions they contain.
Modules: Modules are les containing Python code (variables, functions, classes, etc.). They provide a
way of organizing the code for large Python projects into les and folders. The key bene t of using
modules is namespaces: you must import the module to use its functions within a Python script or
notebook. Namespaces provide encapsulation and avoid naming con icts between your code and a
module or across modules.
We can use the ceil function (short for ceiling) from the math module to round up numbers. Let's import the
module and use it to round up the number 1.2 .
import math
help(math.ceil)
ceil(x, /)
Return the ceiling of x as an Integral.
This is the smallest integer >= x.
math.ceil(1.2)
Let's now use the math.ceil function within the home_loan_emi function to round up the EMI amount.
Using functions 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, a function should do
one thing and one thing only. If you nd yourself writing a function that does too many things, consider
splitting it into multiple smaller, independent functions. As a rule of thumb, try to limit your functions to
10 lines of code or less. Good programmers always write short, simple, and readable functions.
def loan_emi(amount,
loan_emi 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 emi
emi1 = loan_emi(
amount=1260000,
duration=8*12,
rate=0.1/12,
down_payment=3e5
)
emi1
14568
emi2
15288
Let's compare the EMIs and display a message for the option with the lower EMI.
Q: Shaun is currently paying back a home loan for a house he bought a few years ago. The cost of the
house was $800,000 . Shaun made a down payment of 25% of the price. He nanced the remaining
amount using a 6-year loan with an interest rate of 7% per annum (compounded monthly). Shaun is
now buying a car worth $60,000 , which he is planning to nance using a 1-year loan with an interest
rate of 12% 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 de ned.
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_house
10230
cost_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_car
5331
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.
1267
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
<ipython-input-45-b684ffbee02d> in <module>
----> 1 emi_without_interest = loan_emi(amount=100000, duration=10*12, rate=0./12)
2 emi_without_interest
Something seems to have gone wrong! If you look at the error message above carefully, Python tells us precisely
what is wrong. Python throws a ZeroDivisionError with a message indicating that we're trying to divide a
number by zero. ZeroDivisonError 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 the
Python interpreter tries to execute it. Errors detected during execution are called exceptions. Exceptions
typically stop further execution of the program unless handled within the program using try - except
statements.
Python provides many built-in exceptions thrown when built-in operators, functions, or methods are used
incorrectly: https://docs.python.org/3/library/exceptions.html#built-in-exceptions. You can also de ne your
custom exception by extending the Exception class (more on that later).
You can use the try and except statements to handle an 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)
When an exception occurs inside a try block, the block's remaining statements are skipped. The except block
is executed if the type of exception thrown matches that of the exception being handled. After executing the
except block, the program execution returns to the normal ow.
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 - except to handle the scenario where the interest rate 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 robust & versatile.
def loan_emi(amount,
loan_emi 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 emi
Q: If you borrow $100,000 using a 10-year loan with an interest rate of 9% per annum, what is the
total amount you end up paying as interest?
1267
834
def loan_emi(amount,
loan_emi 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 emi
In the docstring above, we've provided some additional information that the duration and rate are 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 to improve the function?
help(loan_emi)
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)
jovian.commit(project='python-functions-and-scope', environment=None)
'https://jovian.ai/aakashns/python-functions-and-scope'
The rst time you run jovian.commit , you'll be asked to provide an API Key to securely upload the notebook to
your Jovian account. You can get the API key from your Jovian pro le page after logging in / signing up.
jovian.commit uploads the notebook to your Jovian account, captures the Python environment, and creates a
shareable link for your notebook, as shown above. You can use this link to share your work and let anyone
(including you) run your notebooks and reproduce your work.
Dubai 370 15 80
Mumbai 450 10 70
1. If you're planning a 1-week long trip, which city should you visit to spend the least amount of money?
2. How does the answer to the previous question change if you change the trip's duration to four days, ten days
or two weeks?
3. If your total budget for the trip is $1000, which city should you visit to maximize the duration of your trip?
Which city should you visit if you want to minimize the duration?
4. How does the answer to the previous question change if your budget is $600, $2000, or $1500?
Hint: To answer these questions, it will help to de ne a function cost_of_trip with relevant inputs like ight
cost, hotel rate, car rental rate, and duration of the trip. You may nd the math.ceil function useful for
calculating the total cost of car rental.
# Use these cells to answer the question - build the function step-by-step
This tutorial on functions in Python is by no means exhaustive. Here are a few more topics to learn about:
You are ready to move on to the next tutorial: "Reading from and writing to les using Python".
1. What is a function?
2. What are the bene ts of using functions?
3. What are some built-in functions in Python?
17. Can you access the variables de ned inside a function outside its body? Why or why not?
18. What do you mean by the statement "a function de nes a scope within Python"?
19. Do for and while loops de ne a scope, like functions?
21. What are optional function arguments & default values? Give an example.
22. Why should the required arguments appear before the optional arguments in a function de nition?
23. How do you invoke a function with named arguments? Illustrate with an example.
29. Where can you learn about the modules and functions available in the Python standard library?
32. What problems would you run into if Python modules did not provide namespaces?
35. Can you invoke a function inside the body of another function? Give an example.
36. What is the single responsibility principle, and how does it apply while writing functions?
37. What some characteristics of well-written functions?
38. Can you use if statements or while loops within a function? Illustrate with an example.
39. What are exceptions in Python? When do they occur?
46. How do you handle two different types of exceptions using except? Can you have multiple except blocks
under a single try block?
47. How do you create an except block to handle any type of exception?
51. What are *args and **kwargs? How are they useful? Give an example.
52. Can you de ne functions inside functions?
56. Can functions return other functions as results? Illustrate with an example.
Dubai 370 15 80
Mumbai 450 10 70
1. If you're planning a 1-week long trip, which city should you visit to spend the least amount of money?
2. How does the answer to the previous question change if you change the trip's duration to four days, ten days
or two weeks?
3. If your total budget for the trip is $600, which city should you visit to maximize the duration of your trip?
Which city should you visit if you want to minimize the duration?
4. How does the answer to the previous question change if your budget is $1000, $2000, or $1500?
Hint: To answer these questions, it will help to de ne a function cost_of_trip with relevant inputs like ight
cost, hotel rate, car rental rate, and duration of the trip. You may nd the math.ceil function useful for
calculating the total cost of car rental.
import math
Paris=[200,20,200,'Paris']
London = [250,30,120,'London']
Dubai = [370,15,80,'Dubai']
Mumbai = [450,10,70,'Mumbai']
Cities = [Paris,London,Dubai,Mumbai]
def cost_of_trip(flight,hotel_cost,car_rent,num_of_days=0):
cost_of_trip
return flight+(hotel_cost*num_of_days)+(car_rent*math.ceil(num_of_days/7))
def days_to_visit(days):
days_to_visit
costs=[]
for city in Cities:
cost=cost_of_trip(city[0],city[1],city[2],days)
costs.append((cost,city[3]))
min_cost = min(costs)
return min_cost
1. If you're planning a 1-week long trip, which city should you visit to spend the least amount of
money?
days_to_visit(7)
(540, 'Paris')
2. How does the answer to the previous question change if you change the trip's duration to four days,
ten days or two weeks?
days_to_visit(4)
(480, 'Paris')
days_to_visit(10)
(680, 'Dubai')
days_to_visit(14)
(730, 'Mumbai')
3. If your total budget for the trip is $600, which city should you visit to maximize the duration of your
trip? Which city should you visit if you want to minimize the duration?
def given_budget(budget,less_days=False):
given_budget
days=1
cost=0
while cost<budget:
#copy of city cost
cost_before=cost
try:
#copy of costs dictionary, if exists
costs_before=costs.copy()
except:
#if costs dictionary doesn't exist, create an empty dictionary
costs_before={}
costs={}
for city in Cities:
cost = cost_of_trip(city[0],city[1],city[2],days)
costs[cost] = city[3]
if less_days:
cost=max(list(costs.keys()))
''' The while loop breaks only after cost>600 condition is met.
when the condition is met, the costs dictionary updates to values that are
so we check if it is exceeding, if it does, we return the values from the p
if cost>=budget:
return costs_before[cost_before],days-1
else:
cost=min(list(costs.keys()))
if cost>=budget:
return costs_before[cost_before],days-1
days+=1
city_to_stay_maximum_days=given_budget(600)
print(city_to_stay_maximum_days)
('Paris', 7)
city_to_stay_minimum_days=given_budget(600,less_days=True)
print(city_to_stay_minimum_days)
('Mumbai', 7)
4. How does the answer to the previous question change if your budget is $1000, $2000, or $1500?
city_to_stay_maximum_days=given_budget(1000)
print(city_to_stay_maximum_days)
('Mumbai', 26)
city_to_stay_minimum_days=given_budget(1000,less_days=True)
print(city_to_stay_minimum_days)
('London', 14)
city_to_stay_maximum_days=given_budget(2000)
print(city_to_stay_maximum_days)
('Mumbai', 77)
city_to_stay_minimum=given_budget(2000,less_days=True)
print(city_to_stay_minimum)
('London', 35)
city_to_stay_maximum_days=given_budget(1500)
print(city_to_stay_maximum_days)
('Mumbai', 49)
city_to_stay_minimum_days=given_budget(1500,less_days=True)
print(city_to_stay_minimum_days)
('Paris', 24)