# 1.7 Functions As your programs start to get larger, you'll want to get organized. This section introduces functions. Error handling with exceptions is also introduced. ### Custom Functions Use functions for code you want to reuse. Here is a function definition: ```python def sumcount(n): ''' Returns the sum of the first n integers ''' total = 0 while n > 0: total += n n -= 1 return total ``` Calling a function. ```python # Use parenthesis to call the 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`. ```python # `math` module import math x = math.sqrt(10) # `urllib.request` module 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 the program to stop. Try this in your python REPL. ```pycon >>> int('N/A') Traceback (most recent call last): File "", line 1, in 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. ```python for line in f: 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. ### Raising Exceptions To raise an exception, use the `raise` statement. ```python raise RuntimeError('What a kerfuffle') ``` This will cause the program to abort with an exception traceback. Unless caught by a `try-except` block. ```bash % python3 foo.py Traceback (most recent call last): File "foo.py", line 21, in raise RuntimeError("What a kerfuffle") RuntimeError: What a kerfuffle ``` ## Exercises ### Exercise 1.29: Defining a function You can define a simple function using the `def` statement. For example, ```python >>> 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](06_Files) and turn it into a function `portfolio_cost(filename)`. The 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: ```python def portfolio_cost(filename): ... # Your code here ... cost = portfolio_cost('Data/portfolio.csv') 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: ```bash bash $ python3 -i pcost.py ``` This will allow you to call your function from the interactive mode. ```python >>> portfolio_cost('Data/portfolio.csv') 44671.15 >>> ``` Being able to experiment with your code interactively is useful for testing and debugging. What happens if you try your function on a file with some missing fields? ```pycon >>> portfolio_cost('Data/missing.csv') Traceback (most recent call last): File "", line 1, in 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.31: 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: ```pycon >>> import csv >>> f = open('Data/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.32: Reading from the command line In the `pcost.py` program, the name of the input file has been hardwired into the code: ```python # pcost.py def portfolio_cost(filename): ... # Your code here ... cost = portfolio_cost('Data/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: ```python # pcost.py import sys def portfolio_cost(filename): ... # Your code here ... 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: ```bash bash % python3 pcost.py Data/portfolio.csv Total cost: 44671.15 bash % ``` [Contents](../Contents) \| [Previous (1.6 Files)](06_Files) \| [Next (2.0 Working with Data)](../02_Working_with_data/00_Overview)