Functions in Python

This section is based on chapter 3 in Sweigart’s Automate the Boring Stuff with Python (second edition).

Python scripts for this section

Write and run a Python script

To write functions in Python, you will be saving files with the .py extension. You can write and save Python files in Atom or in Mu.

To run a script named foobar.py that’s in the current directory, type this at the bash prompt ($) in Mac or the Windows Command Prompt:

python3 foobar.py

On Windows, or in a virtualenv on Mac or Windows, drop the 3 from python3:

python foobar.py

You do not need to be in your virtualenv to do this, but it’s okay if you are.

Put something in, get something out

Sweigart notes that many functions operate as “black boxes”: This describes a function with parameters (it takes arguments) and a return statement. Something goes into it (arguments) and something comes out of it (whatever is returned). You don’t need to know how it works; you just need to know what it does.

a_black_box.py

def add_things(a, b):
   result = (a * 3) + (b * 3)
   return result

To run that function, call it, passing in two strings as arguments:

add_things("love", "Gators")

The function will return this:

loveloveloveGatorsGatorsGators

However, you will not see the returned value unless you capture it, or “catch” it, with a variable, like this:

output = add_things("love", "Gators")

Then you can print output or send it to a database or write it into the HTML of a live web page — or whatever you need.

print(output)

This is true for many functions we use from imported libraries. We know how to call the function, and we know what to expect as output, but we don’t need to know how it works.

Note

We don’t need to know how a toaster toasts bread to get toast out of it. We put in two pieces of bread (the arguments we pass into a function), and toast is returned after the function runs.

Using parameters and returns in functions

Let’s build a function step by step to help you understand parameters and returns. Assume that you buy the same coffee (or other beverage) daily, and you want to calculate how much you spend per week.

# this function has no parameters and returns nothing

def drink_expenses():
    price = 3.99
    count = 7
    total = price * count

# call the function
drink_expenses()

It’s not very useful like that. What if the price of the drink changes? What if you want to calculate a different time period?

# this function has two parameters, but it returns nothing

def drink_expenses(price, count):
    total = price * count

# call the function with two arguments
drink_expenses(3.99, 7)

Now you’re able to pass in any price and any time span you want. However, you can’t use the value for total if there’s no return statement. So — return the total amount spent on coffee.

# this function has two parameters, and it returns a value

def drink_expenses(price, count):
   total = price * count
   return total

# call the function with two arguments
drink_expenses(4.49, 5)

The function is good, but the way you call it needs to change. To assign the returned value of a function to a new variable, follow this pattern:

# this function has two parameters, and it returns a value

def drink_expenses(price, count):
   total = price * count
   return total

# assign the returned value to a new variable, result
result = drink_expenses(4.49, 5)

print(result)

A function can have many lines of instructions inside its code block (including conditionals, loops, and more).

Tip

It’s best to write functions that perform one specific task, rather than several tasks.

Scope

This is a bit of a hard topic for beginners, because you don’t have much experience writing longer or complex scripts.

Consider the following example:

../python_code_examples/functions/f_scope_global_vs_local.py
# example of badly using scope of variables

def breakfast():
    food = 'eggs'
    drink = 'coffee'
    print("Breakfast:")
    print(food, drink, "\n")

def lunch():
    food = 'salad'
    drink = 'iced tea'
    print("Lunch:")
    print(food, drink, "\n")

# run the functions
lunch()
breakfast()

# attempt to print local variables (will not work)
print(food)
print(drink)

Everything will work fine except the last two lines. If you run the script, you’ll see what the two functions print:

Lunch:
salad iced tea

Breakfast:
eggs coffee

But then you’ll see an error:

Traceback (most recent call last):
   File "f_scope_global_vs_local.py", line 20, in <module>
      print(food)
NameError: name 'food' is not defined

The error message (last line) clearly states the problem: You tried to print the value of the variable food, but there is no such variable outside the functions. The script stopped with print(food), but if you tried print (drink) instead, you’d get the same error.

Note that it’s fine to have food in both functions and drink in both functions — there is no conflict or confusion because of local scope.

Key points about scope

  • A variable has local scope or global scope. It cannot have both.

  • A local variable exists only inside a function. It cannot be transmitted outside the function (although its value can be — via return).

  • Sometimes you will make a mistake about the scope of a variable, and then you will get an error — or (worse) your answers or results will not be correct.

  • A global variable exists outside any function. It may be used inside a function.

  • “It is a bad habit to rely on global variables as your programs get larger and larger” (Sweigart, p. 66).

  • Avoid repeating the names of variables in different scopes. If you have a global variable named foobar, do not name a local variable foobar.

  • See four rules, p. 69 in Sweigart

try/except

When your program throws an error, your script stops. In most cases you want to be informed of the error but not have the script come to a halt. The usual way to accomplish this in Python is to write the code you want to run within the try block, and write instructions for handling the error inside the except block.

../python_code_examples/functions/h_try_except.py
# error handling: use this try/except pattern

def test_for_int(num):

    try:
        num = int(num)
        message = str(num) + " is an integer. Good job!"

    # this exception handles any entry that was NOT an integer
    except ValueError:
        message = "Error: You did not provide a whole number."

    return message

# ask for user input
response = input("Enter a number: ")

# run the function, and print what is returned
print( test_for_int(response) )

The code above requires the user to type a whole number. A string or a decimal (float) will trigger the exception.

Hint

To find out what kind of error would be thrown, experiment at the Python command line (error types are case sensitive). Also, there is a list of built-in exceptions. AttributeError and ValueError are common, but there are many others!

Chapter review: chapter 3

These are the takeaways from this chapter. Slides below.

Key points

  1. The def statement (how and where to use it)

  2. When is the code in a function executed? (Know the difference between defining a function and running it.)

  3. Parameters or arguments: Where are these, in a function?

    • How do they work when you define the function?

    • How do they work when you run the function? (Note: Instead of run, we may say call or execute the function. All have the same meaning.)

  4. The return keyword (used in a function)

  5. The NoneType data type: value is None

    • This will be important in scraping! If you get an error that says something does not work because it cannot be applied to NoneType, it means something returned None when you were expecting another value.

    • “Python adds return None to the end of any function definition with no return statement” (Sweigart, p. 62). In other words, a function with no return statement returns None.

    • An empty return statement (that is, the word return alone on a line) also returns None.

  6. Keyword arguments: You can forget this for the time being

  7. Important! Scope of variables

  8. Functions as “black boxes”: Describes a function with parameters (meaning that it takes arguments) and a return statement. One or more values go into the function (arguments) and some value comes out of it (whatever is returned).

  9. Exception handling (also called error handling): try/except pattern

Slides: chapter 3

Python Review 2