Organizing Larger Programs with Functions

PythonPythonBeginner
Practice Now

This tutorial is from open-source community. Access the source code

Introduction

As your programs start to get larger, you'll want to get organized. This section briefly introduces functions and library modules. Error handling with exceptions is also introduced.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("`Python`")) -.-> python/BasicConceptsGroup(["`Basic Concepts`"]) python(("`Python`")) -.-> python/FileHandlingGroup(["`File Handling`"]) python(("`Python`")) -.-> python/ControlFlowGroup(["`Control Flow`"]) python(("`Python`")) -.-> python/DataStructuresGroup(["`Data Structures`"]) python(("`Python`")) -.-> python/FunctionsGroup(["`Functions`"]) python(("`Python`")) -.-> python/ModulesandPackagesGroup(["`Modules and Packages`"]) python(("`Python`")) -.-> python/ErrorandExceptionHandlingGroup(["`Error and Exception Handling`"]) python(("`Python`")) -.-> python/AdvancedTopicsGroup(["`Advanced Topics`"]) python(("`Python`")) -.-> python/PythonStandardLibraryGroup(["`Python Standard Library`"]) python/BasicConceptsGroup -.-> python/comments("`Comments`") python/FileHandlingGroup -.-> python/with_statement("`Using with Statement`") python/BasicConceptsGroup -.-> python/variables_data_types("`Variables and Data Types`") python/BasicConceptsGroup -.-> python/numeric_types("`Numeric Types`") python/BasicConceptsGroup -.-> python/type_conversion("`Type Conversion`") python/ControlFlowGroup -.-> python/conditional_statements("`Conditional Statements`") python/ControlFlowGroup -.-> python/for_loops("`For Loops`") python/ControlFlowGroup -.-> python/while_loops("`While Loops`") python/ControlFlowGroup -.-> python/break_continue("`Break and Continue`") python/ControlFlowGroup -.-> python/list_comprehensions("`List Comprehensions`") python/DataStructuresGroup -.-> python/lists("`Lists`") python/DataStructuresGroup -.-> python/tuples("`Tuples`") python/FunctionsGroup -.-> python/function_definition("`Function Definition`") python/ModulesandPackagesGroup -.-> python/importing_modules("`Importing Modules`") python/ModulesandPackagesGroup -.-> python/standard_libraries("`Common Standard Libraries`") python/ErrorandExceptionHandlingGroup -.-> python/catching_exceptions("`Catching Exceptions`") python/ErrorandExceptionHandlingGroup -.-> python/raising_exceptions("`Raising Exceptions`") python/FileHandlingGroup -.-> python/file_opening_closing("`Opening and Closing Files`") python/FileHandlingGroup -.-> python/file_reading_writing("`Reading and Writing Files`") python/AdvancedTopicsGroup -.-> python/iterators("`Iterators`") python/PythonStandardLibraryGroup -.-> python/math_random("`Math and Random`") python/PythonStandardLibraryGroup -.-> python/os_system("`Operating System and System`") python/BasicConceptsGroup -.-> python/python_shell("`Python Shell`") python/FunctionsGroup -.-> python/build_in_functions("`Build-in Functions`") subgraph Lab Skills python/comments -.-> lab-132706{{"`Organizing Larger Programs with Functions`"}} python/with_statement -.-> lab-132706{{"`Organizing Larger Programs with Functions`"}} python/variables_data_types -.-> lab-132706{{"`Organizing Larger Programs with Functions`"}} python/numeric_types -.-> lab-132706{{"`Organizing Larger Programs with Functions`"}} python/type_conversion -.-> lab-132706{{"`Organizing Larger Programs with Functions`"}} python/conditional_statements -.-> lab-132706{{"`Organizing Larger Programs with Functions`"}} python/for_loops -.-> lab-132706{{"`Organizing Larger Programs with Functions`"}} python/while_loops -.-> lab-132706{{"`Organizing Larger Programs with Functions`"}} python/break_continue -.-> lab-132706{{"`Organizing Larger Programs with Functions`"}} python/list_comprehensions -.-> lab-132706{{"`Organizing Larger Programs with Functions`"}} python/lists -.-> lab-132706{{"`Organizing Larger Programs with Functions`"}} python/tuples -.-> lab-132706{{"`Organizing Larger Programs with Functions`"}} python/function_definition -.-> lab-132706{{"`Organizing Larger Programs with Functions`"}} python/importing_modules -.-> lab-132706{{"`Organizing Larger Programs with Functions`"}} python/standard_libraries -.-> lab-132706{{"`Organizing Larger Programs with Functions`"}} python/catching_exceptions -.-> lab-132706{{"`Organizing Larger Programs with Functions`"}} python/raising_exceptions -.-> lab-132706{{"`Organizing Larger Programs with Functions`"}} python/file_opening_closing -.-> lab-132706{{"`Organizing Larger Programs with Functions`"}} python/file_reading_writing -.-> lab-132706{{"`Organizing Larger Programs with Functions`"}} python/iterators -.-> lab-132706{{"`Organizing Larger Programs with Functions`"}} python/math_random -.-> lab-132706{{"`Organizing Larger Programs with Functions`"}} python/os_system -.-> lab-132706{{"`Organizing Larger Programs with Functions`"}} python/python_shell -.-> lab-132706{{"`Organizing Larger Programs with Functions`"}} python/build_in_functions -.-> lab-132706{{"`Organizing Larger Programs with Functions`"}} end

Custom Functions

Use functions for code you want to reuse. Here is a function definition:

def sumcount(n):
    '''
    Returns the sum of the first n integers
    '''
    total = 0
    while n > 0:
        total += n
        n -= 1
    return total

To call a function.

a = sumcount(100)

A function is a series of statements that perform some task and return a result. The return keyword is needed to explicitly specify the return value of the function.

Library Functions

Python comes with a large standard library. Library modules are accessed using import. For example:

import math
x = math.sqrt(10)

import urllib.request
u = urllib.request.urlopen('http://www.python.org/')
data = u.read()

We will cover libraries and modules in more detail later.

Errors and exceptions

Functions report errors as exceptions. An exception causes a function to abort and may cause your entire program to stop if unhandled.

Try this in your python REPL.

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

For debugging purposes, the message describes what happened, where the error occurred, and a traceback showing the other function calls that led to the failure.

Catching and Handling Exceptions

Exceptions can be caught and handled.

To catch, use the try - except statement.

for line in file:
    fields = line.split(',')
    try:
        shares = int(fields[1])
    except ValueError:
        print("Couldn't parse", line)
    ...

The name ValueError must match the kind of error you are trying to catch.

It is often difficult to know exactly what kinds of errors might occur in advance depending on the operation being performed. For better or for worse, exception handling often gets added after a program has unexpectedly crashed (i.e., "oh, we forgot to catch that error. We should handle that!").

Raising Exceptions

To raise an exception, use the raise statement.

raise RuntimeError('What a kerfuffle')

This will cause the program to abort with an exception traceback. Unless caught by a try-except block.

% python3 foo.py
Traceback (most recent call last):
  File "foo.py", line 21, in <module>
    raise RuntimeError("What a kerfuffle")
RuntimeError: What a kerfuffle

Exercise 1.29: Defining a function

Try defining a simple function:

>>> def greeting(name):
        'Issues a greeting'
        print('Hello', name)

>>> greeting('Guido')
Hello Guido
>>> greeting('Paula')
Hello Paula
>>>

If the first statement of a function is a string, it serves as documentation. Try typing a command such as help(greeting) to see it displayed.

Exercise 1.30: Turning a script into a function

Take the code you wrote for the pcost.py program in Exercise 1.27 and turn it into a function portfolio_cost(filename). This function takes a filename as input, reads the portfolio data in that file, and returns the total cost of the portfolio as a float.

To use your function, change your program so that it looks something like this:

## pcost.py
def portfolio_cost(filename):
    """
    Computes the total cost (shares*price) of a portfolio file
    """
    total_cost = 0.0

    with open(filename, "rt") as f:
        rows = f.readlines()
        headers = rows[0].strip().split(",")
        for row in rows[1:]:
            row_data = row.strip().split(",")
            nshares = int(row_data[1])
            price = float(row_data[2])
            total_cost += nshares * price

    return total_cost


import sys

if len(sys.argv) == 2:
    filename = sys.argv[1]
else:
    filename = input("Enter a filename:")

cost = portfolio_cost(filename)
print("Total cost:", cost)

When you run your program, you should see the same output as before. After you've run your program, you can also call your function interactively by typing this:

$ python3 -i pcost.py

This will allow you to call your function from the interactive mode.

>>> portfolio_cost('portfolio.csv')
44671.15
>>>

Being able to experiment with your code interactively is useful for testing and debugging.

Exercise 1.31: Error handling

What happens if you try your function on a file with some missing fields?

>>> portfolio_cost('missing.csv')
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "pcost.py", line 11, in portfolio_cost
    nshares    = int(fields[1])
ValueError: invalid literal for int() with base 10: ''
>>>

At this point, you're faced with a decision. To make the program work you can either sanitize the original input file by eliminating bad lines or you can modify your code to handle the bad lines in some manner.

Modify the pcost.py program to catch the exception, print a warning message, and continue processing the rest of the file.

Exercise 1.32: Using a library function

Python comes with a large standard library of useful functions. One library that might be useful here is the csv module. You should use it whenever you have to work with CSV data files. Here is an example of how it works:

>>> import csv
>>> f = open('portfolio.csv')
>>> rows = csv.reader(f)
>>> headers = next(rows)
>>> headers
['name', 'shares', 'price']
>>> for row in rows:
        print(row)

['AA', '100', '32.20']
['IBM', '50', '91.10']
['CAT', '150', '83.44']
['MSFT', '200', '51.23']
['GE', '95', '40.37']
['MSFT', '50', '65.10']
['IBM', '100', '70.44']
>>> f.close()
>>>

One nice thing about the csv module is that it deals with a variety of low-level details such as quoting and proper comma splitting. In the above output, you'll notice that it has stripped the double-quotes away from the names in the first column.

Modify your pcost.py program so that it uses the csv module for parsing and try running earlier examples.

Exercise 1.33: Reading from the command line

In the pcost.py program, the name of the input file has been hardwired into the code:

## pcost.py

def portfolio_cost(filename):
    ...
    ## Your code here
    ...

cost = portfolio_cost('portfolio.csv')
print('Total cost:', cost)

That's fine for learning and testing, but in a real program you probably wouldn't do that.

Instead, you might pass the name of the file in as an argument to a script. Try changing the bottom part of the program as follows:

## pcost_1.33.py

import csv


def portfolio_cost(filename):
    """
    Computes the total cost (shares*price) of a portfolio file
    """
    total_cost = 0.0

    with open(filename, "rt") as f:
        rows = csv.reader(f)
        headers = next(rows)  ## Skip header row
        for row in rows:
            if len(row) < 3:
                print("Skipping invalid row:", row)
                continue
            try:
                nshares = int(row[1])
                price = float(row[2])
                total_cost += nshares * price
            except (IndexError, ValueError):
                print("Skipping invalid row:", row)

    return total_cost

import sys


if len(sys.argv) == 2:
    filename = sys.argv[1]
else:
    filename = 'portfolio.csv'

cost = portfolio_cost(filename)
print('Total cost:', cost)

sys.argv is a list that contains passed arguments on the command line (if any).

To run your program, you'll need to run Python from the terminal.

For example, from bash on Unix:

$ python3 pcost.py portfolio.csv
Total cost: 44671.15
bash %

Summary

Congratulations! You have completed the Functions lab. You can practice more labs in LabEx to improve your skills.

Other Python Tutorials you may like