Functions and Modules

Function

A function is a block of code consisting of one or more Python statements which are invoked together as a sequence of instructions, when the function is called. A function can be defined with parameters and can receive arguments for the defined parameters. We have already called several functions in this book. E.g.; print("hello"), input("please type in anything") etc.. These functions were already available in Python and we used them. Now we will create our own function. Here is an example of a function which prints out event numbers up to given number:


# define the function with one parameter - limit
def generate_even_numbers(limit):  
    #All reference documentation should go in between ''' block
    '''
    This method generates even numbers from 0 to the value 
    passed in the 'limit' parameter, excluding the 'limit' value
    '''
    even_number_list = []  # create an empty list 
    for i in range(limit):
        if (i%2 == 0):  # remainder 0 indicates even
            even_number_list.append(i) # add to the list
    return even_number_list # return statement and function definition ends

# call the defined function with one argument having a value of 10 for limit parameter
print(generate_even_numbers(10))   
# help on a function will print out the text within '''. 

help(generate_even_numbers)

Output:

[0, 2, 4, 6, 8]
Help on function generateevennumbers in module __main:

generate_even_numbers(limit) This method generates even numbers from 0 to the value passed in the 'limit' parameter, excluding the 'limit' value

You can send different values for the argument to the function while invoking and accordingly your results will be different.

Function Definitions With Default Values

Sometimes you may have to define a function with one or more default argument values. In such cases all the default arguments can be defined after all the non-default arguments are defined. Here is an example;

def myfunc(state, county, country="US"):
    print(country, state, county)

myfunc('MI','Wayne')
myfunc('MI','Wayne', "United States of America")

output:

US MI Wayne United States of America MI Wayne

With the above function definition, you can invoke the function with or without a value for 'country'. If you pass country value then the passed in value would be considered. If it is missing then the default value defined in the function definition will be used.

Function Definitions with Variable args and kwargs

Occasionally you may require a function which can accept variable arguments. In such cases you can use the * (asterisk) and/or ** (double asterisk) against the argument names to receive a list and/or keyword arguments respectively. Here is an example;

def myfunc(*args, **kwargs):
    print(kwargs)  # receives a dictionary of keyword arguments
    print(args)   # receives a tuple of all arguments.

myfunc(1, 2, 3, country='US',county='Wayne')

output:

{'country': 'US', 'county': 'Wayne'} (1, 2, 3)

 

Highlights

  • def keyword is used to define a function
  • A function is a block of code which can be called by the same or different program
  • A function can take 0 or more arguments as long as the respective parameters are defined in the function definition.
  • You can set default values to certain arguments by defining the default argument values after all the non-default arguments are defined.
  • You can also send variable length of arguments to a function by using an asterisk before the argument name.
  • You can also send a variable length of keyword arguments by using double asterisk before the keyword arguments name.

 

Points to note

  • Multiple arguments are separated by a comma in both the definition and invocation. The order of arguments is important when you call the function- you should maintain the same order as the definition, while passing the values to the arguments unless when the function is defined for arbitrary arguments and/or keyword arguments list.
  • A return statement, if present will return any object back from the function to the calling program.
  • Function definition should come before the function is called otherwise you will get NameError

 

Naming convention

  • It is a recommended practice to name all variables with a noun and all function names should be a verb. The reasoning behind this is, a variable holds a value and inherently does not do anything else with it so it should be a noun. A function on the other had is doing something on the arguments passed. It is taking some action on it and hence should be named with a verb.

Modules

A module is a Python program file containing one or more functions. Module name for import will be same as the file name except you do not use the .py of the filename.

As an entry level Data Analyst, instead of writing your own custom modules, you would focus on using ready made modules and its functions available in open source libraries. Rich libraries (library is nothing but a group of one or more Python program files bundled together) are written for Python by many open source developers, which you can use by using the Module system.

Python also provides built-in modules which are readily available. In addition, Anaconda package also contains 100's of modules already downloaded for us. For any other module which is not part of Anaconda, you first have to install (in other words download the module) the module before you import them. For all others you can start using the functions of a module by using the import keyword.

In the example below we import random module to use its random function.

# this package is readily available for import
import random   
random_number = random.random()
print(random_number)

output:

A random real number between 0 and 1 is printed out.

If you want to know all the functions and usage of each of the functions for random module, you can type in:

help(random)

This prints out the official documentation which is part of the module. Output is not shown as it is very verbose. Documentation text is added to a Python file between three single quotes '''. Any text between three single quotes will be printed out when you use help() function. To get help on a specific function you can invoke help on that function. For e.g., random module has a function called randint to know more about this type in:

help(random.randint)

output:

Help on method randint in module random:

randint(a, b) method of random.Random instance Return random integer in range [a, b], including both end points.

As you can see randint method gives you a random integer between two numbers a and b which you can specify. In the above example you used a dot operator (.) on random to invoke randint function of random. To reduce clutter and directly use randint you can use the below import

from random import randint

Full code:

from random import randint 
random_number = randint(2, 6)
print(random_number)

output:

Any random number between 2 and 6 is printed out.

Basic Python modules used by all

Module Description
random Functions for generating random numbers
pickle Functions for data storage
tkinter Functions for front end GUI applications
decimal Functions for working with decimals

Advanced modules used by Data Analytics commnity

Module Description
numpy Functions for efficient handling of arrays. In Data Analytics we use NumPy arrays more than native Python arrays
pandas Functions which are built on top of NumPy and provide very efficient implementations for manipulating tabular data.
matplotlib Functions for handling 2D plotting - graphics.
sklearn (skikit learn) Functions for image processing and machine learning
seaborn Functions for creating colorful visualizations of statistical models such as bar plots, violin plots, heat maps etc..

Exception handling

Programs throw error when the Python interpreter encounters a statement which is illegal and cannot be executed. This could be a programmer error or user input error or system error. Such a condition is called throwing an exception. For e.g,, if you try to import a package cheesecake which does not exist then you receive ModuleNotFoundError on the console. We have earlier come across IndexError in String Operations chapter when we try to access an element position which is out of bounds for a given string.

When an error occurs during processing of a large amount of data, then the program crashes and all the work that the program would have completed until that error point is lost. This is not desirable when processing massive amount of data which takes significant amount of time to process.

Instead of crashing the program, it is desirable to collect the all the issues and complete the given execution and after the execution, you can look into the error situations and give a fix for all the errors, the next time it is run. We use Exception handling using try and except key words to achieve this task.

In the code below, we fix the erroneous import by using try block. Put all the code which could potentially throw an error inside the try block and when an exception is thrown, the program of execution jumps to except block which will handle the exception meaningfully. After handling the exception, the program of execution continues further on with the next statements after except without crashing the program.

try:
    import cheesecake
except ImportError:
    print("Such a module does not exist")
print("program continues after the exception is excepted")

Note however, that it is not always a good thing to move on when an exception happens during execution. If the cheesecake module is absolutely required by you to process all your data then you should crash the program. However, supposing if you are processing a million records of users input data, you may come across a few invalid entries in the middle of otherwise good entries. Then in such situations you can collect all the invalid records separately and complete the processing of the rest of the million records. Then when the program finishes you can look into invalid entries and take action appropriately for all of those.

An optional finally block can be added to the try catch block. Finally block will be executed irrespective of weather an exception is thrown on not. Typically, finally blocks contain code for releasing resources like closing open file handles, releasing database connections etc., which were established in the try block. Then upon either completing the try block successfully or upon an exception situation, irrespective of the situation, the database connection or files should be closed. Such type of code is added in finally block. If you do not release the resources in the finally block and an exception happens during execution, then the program crashes without releasing the resources in a graceful way.

Note that file and database operations will be covered in the next book. Although these two concepts are very important, we rarely use pure Python to open files or databases. Instead we use pandas module for these operations and hence we cover them in NumPy and Pandas book.

Below is a program which handles invalid user inputs elegantly by informing the user what exactly went wrong and asks user to input the correct values instead of crashing the program.


print("This program calculates the rate of interest for your deposit. \nPlease input your deposit and the total interest earned/year\nand I will calculate the rate of interest\n")
print("To quit the program enter 'quit' in small case\n\n")

# program in an infinite loop till user enters quit
while True:
    try:  # try statement encloses statements which could throw an exception
        user_input1 = input("Enter Deposit Amount:")

        if (user_input1 == 'quit'):
            print("Good bye! Hope you had fun getting your interest rates!")
            break

        user_input2 = input('Enter total interest earned:')
        deposit_amount = float(user_input1) 
        interest_earned = float(user_input2)
        interest_rate = (interest_earned / deposit_amount) * 100
         # round function rounds the result to 2 decimal places
        print("Rate of interest:"+ str(round(interest_rate, 2)))
    except: # Catch all exceptions if nothing specified.
        print("You entered an invalid integer. Please type a valid value.")
        continue;
    finally:
        # Typically resources are released in this block
        print("This will be executed no matter what")

The program starts by letting the user know that he has to input deposit amount and total the interest earned. The user has to input quit to exit the program. Once the user inputs valid numerical values, the program calculates the interest. If the user inputs illegal values, the program is catch in except block where the user is informed of what went wrong. The except block in the above program catches all exceptions. The two types of erroneous input the user can give are:

  • alpha characters for number
  • deposit value 0 - exception thrown due to division by 0

You can refine the program to add more clarity on the exception raised with the below program:

print("""This program calculates the rate of interest for your deposit. 
Please input your deposit and the total interest earned/year
and I will calculate the rate of interest\n""")

print("To quit the program enter 'quit' in small case\n\n")

while True:

    try: # try statement encloses statements which could throw an exception
        user_input1 = input("Enter Deposit Amount:")

        if (user_input1 == 'quit'):
            print("Good bye! Hope you had fun getting your interest rates!")
            break
        deposit_amount = float(user_input1) 

        user_input2 = input('Enter total interest earned:')
        interest_earned = float(user_input2)

        interest_rate = (interest_earned / deposit_amount) * 100
        # round function rounds the result to 2 decimal places
        print("Rate of interest:"+ str(round(interest_rate , 2))) 
    except ValueError:  # catch ValueError. This happens when the user inputs alpha instead of numeric value
        print("You entered an invalid integer. Please type a valid value")
        continue
    except ZeroDivisionError:  # This happens when the deposit amount is 0
        print("You entered a deposit value of 0... ha ha I still did not crash! Input a valid deposit amount please!")
        continue
    except:  # User input issues are already caught so crash the program
        raise
    finally:
        print("finally block will be executed no matter what") # typically resources are released in this block

In the first two except blocks we mention the Error name that could occur. In the last except block, we catch all other exceptions that may occur. However when it reaches the third block we have exhausted all possible error conditions due to the user input. Then, in that case when it reaches this block, it has to be some system fault and in that case we have to crash. For that reason we have used the raise statement which rethrows the catch exception and crashes the program. The last except block can be completely removed if we are only rethrowing the exception as it will crash anyways if that block is not there. This is just added here to explain raise statement.

results matching ""

    No results matching ""