Python Functions and Modules

by Allison Parrish

This chapter is about functions and modules in Python. Functions are a handy way to isolate a particular part of your program’s functionality and make it reusable. Modules are a way to collect a number of helpful functions in one file, which you can then use in multiple projects and share with other programmers.

We’ve already been using functions extensively in our programs; functions are one of the main building blocks of Python programming. Whenever you’ve typed something like len(x) or type(y) or even random.choice([1, 2, 3]), you’ve been using functions. It’s just that these functions come pre-defined by Python. In this chapter, we’re going to learn how to write our own functions.

To begin my explanation of why functions are useful, and how they work syntactically, I’m going to explain a scenario that I run into fairly frequently: capitalizing strings in Python.

The Case of the Capitalized String

I have a problem with Python, which is that string values don’t have a built-in method for capitalizing strings. Or more precisely, it has two methods, neither of which does exactly what I want. The .title() method capitalizes each word in the string:

>>> "this is a test, Arlene".title() # capitalizes each word
'This Is A Test, Arlene'

The .capitalize() method capitalizes the first letter of the string, but converts the rest of the string to lowercase:

>>> "this is a test, Arlene".capitalize() # converts rest of string to lowercase
'This is a test, arlene'

Notice how Arlene in the original string is converted to arlene. Not what I wanted at all! What I really want is some way to capitalize the first letter of a string and then keep the rest of the string in the case that it was originally. Of course, it’s easy to write an expression that does this:

>>> s = "this is a test, Arlene"
>>> s[0].upper() + s[1:]
'This is a test, Arlene'

The expression s[0].upper() + s[1:] does exactly what I want—assuming the string I want to capitalize is in a variable s. So great, right? I’ve figured that out. Whenever I want to capitalize a string, I can just remember that expression and then re-write the expression with the relevant variable.

Make it into a function

Wouldn’t it be nice, though, if instead of having to retype that expression whenever I wanted to capitalize a string, I could write something a bit more mnemonic—something that looks like a Python built-in function, like this?

>>> print ucfirst("this is a test, Arlene")

(I’m using ucfirst here as an homage to the venerable Perl function.)

“Impossible,” you say. “Only the great cabal of Python Language Designers can work such trickery. For us mere mortals, it is forbidden. Forbidden! I will not tolerate this heresy.” I’m not sure why you say that. You’re really taking this programming thing seriously.

In any case, it turns out we can make this dream come true! We can define own functions in Python. A function definition looks like this:

def your_function_name(parameters):
  statements
  return expression

That is to say, a function definition consists of:

  • The keyword def (which is short for “define”), followed by the name of your function. The name of your function can be any valid Python identifier.
  • A pair of parentheses after the function name, which is a list of parameters to the function. (We’ll talk more about this below.)
  • A colon after the parentheses.
  • Potentially some Python statements. (These statements will be executed when your function is called.)
  • The keyword return, followed by an expression. This expression is what the function will evaluate to when it’s called.

Here’s a definition of our ucfirst function, which evaluates to a copy of the string that you passed to it, with its first character capitalized and the rest pf the string left alone:

def ucfirst(s):
  return s[0].upper() + s[1:]

You can define a function in the interactive interpreter simply by typing it in. (The prompt will change from >>> to ... when you’re typing the body of the function.) Here’s an interactive interpreter session that shows me defining the ucfirst function and then using it on a string:

>>> def ucfirst(s):
...   return s[0].upper() + s[1:]
... 
>>> ucfirst("this is a test, Arlene")
'This is a test, Arlene'

Once we’ve defined the function ucfirst, we can call it by typing the name of the function, followed by a pair of parentheses. When you call a function, Python stops executing statements in the normal flow of the program, and “jumps” to the function and executes the statements there instead. Once it’s done executing those statements, the entire function call is effectively “replaced” with the value of whatever expression is to the right of the return keyword inside the function. We say that the function “evaluates to” the value to the right of the return keyword.

When Python starts executing the function, any values inside the parentheses in the function call (in the example above, the string this is a test, Arlene) are available to the function as the variables named in the the function definition’s parentheses. In the case of the ucfirst function, the value this is a test, Arlene is now available as the variable s inside the function.

Using functions in a program

Here’s a program that defines the ucfirst function, and then uses it to print out each line of standard input with its first character converted to uppercase.

import sys

def ucfirst(s):
  return s[0].upper() + s[1:]

for line in sys.stdin:
  line = line.strip()
  if len(line) > 0:
    print ucfirst(line)
  else:
    print line
Program: make_it_upper.py


Here’s what it looks like, using sea_rose.txt as input:

$ python make_it_upper.py <sea_rose.txt
Rose, harsh rose,
Marred and with stint of petals,
Meagre flower, thin,
Spare of leaf,

More precious
Than a wet rose
Single on a stem --
You are caught in the drift.

Stunted, with small leaf,
You are flung on the sand,
You are lifted
In the crisp sand
That drives in the wind.

Can the spice-rose
Drip such acrid fragrance
Hardened in a leaf?

More on parameters

A function can have more than one parameter. Let’s write a function that takes two strings as parameters, and returns the first half of one string appended to the second half of the second string. We’ll call this function halfsies:

>>> def halfsies(left, right):
...   left_part = left[:len(left)/2]
...   right_part = right[len(right)/2:]
...   return left_part + right_part
... 
>>> halfsies("Interactive Telecommunications Program", "International House of Pancakes")
'Interactive Telecomouse of Pancakes'

In this case, the left and right parameters receive the values Interactive Telecommunications Program and International House of Pancakes, respectively. Inside the function, there are a few statements to get the half-parts of both strings, and then the function returns the result of joining those two strings together with the + operator.

Parameters to functions don’t have to be strings, of course: they can be values of any type. Here’s a function, human_join, which joins together strings in a more “human” way (joining them together with a comma, and then using a conjunction between the penultimate and ultimate items). This function takes a two parameters: a list of strings to join together, and a “conjunction” that will be used to join them together.

>>> def human_join(parts, conjunction):
...   if len(parts) == 1:
...     return parts[0]
...   first_join = ', '.join(parts[:-1])
...   return first_join + " " + conjunction + " " + parts[-1]
... 
>>> some_things = ["an abacus", "a belfry", "a cuisinart"]
>>> human_join(some_things, "and")
>>> human_join(["just this one thing!"], "and")
'an abacus, a belfry and a cuisinart'
'just this one thing!'

Here, the parts parameter to the function is intended to be a list. This function also uses an if statement right off the bat to check to see if the list only has one element; if it does, it simply returns the single item from the list without further processing. (You can’t join together items from a list with fewer than two items, after all.)

Exercise: Modify the human_join function so that it takes a third parameter, oxford, which determines whether or not the function will use an Oxford comma.

Default values for parameters

One helpful feature of Python is the ability to have function parameters with default values. A default value is a value assigned to a function parameter if no value is passed for that parameter in the function call. You can set a default value for a parameter in the function definition by putting an equal sign (=) after the parameter name, followed by the value you’d like to use as a default.

For example, here’s a function that generates restaurant names according to a very simple template. If you pass no parameters to the function, it uses the value “House” as a default:

>>> def restaurant(building="House"):
...   return "International " + building + " of Pancakes"
...
>>> restaurant() # returns "International House of Pancakes"
>>> restaurant("Hovel") # returns "International Hovel of Pancakes"
'International House of Pancakes'
'International Hovel of Pancakes'

More than one parameter can have a default value. Let’s extend the example above to generate restaurant names with both a building and a foodstuff:

>>> def restaurant(building="House", foodstuff="Pancakes"):
...   return "International " + building + " of " + foodstuff
...
>>> restaurant() # returns "International House of Pancakes"
>>> restaurant("Hovel") # returns "International Hovel of Pancakes"
>>> restaurant("Duplex", "Casseroles") # etc.
'International House of Pancakes'
'International Hovel of Pancakes'
'International Duplex of Casseroles'

As the above example demonstrates, if a function has more than one parameter with a default value, parameters passed in the function call are assigned to parameters in the function from left to right—i.e., in the second call above, the value Hovel is assigned to the parameter building, not to foodstuff (which instead uses its default value).

Named parameters

Python has another helpful function-related feature, which is that you can specify parameters by name in your function call. The primary benefit of this is that you don’t have to remember exactly in what order to specify parameters for a particular function. (This is especially helpful for functions with a lot of parameters.)

To take advantage of this feature, include the name of the parameter you want to specify in the function call, followed by an equal sign (=), followed by the value you want to pass for that parameter. To illustrate, let’s use the restaurant function from the previous example.

>>> def restaurant(building="House", foodstuff="Pancakes"):
...   return "International " + building + " of " + foodstuff
...
>>> restaurant(building="Hangar") # International Hangar of Pancakes
>>> restaurant(foodstuff="Quinoa") # International House of Quinoa
>>> restaurant(foodstuff="Pineapple", building="Pyramid") # etc etc
'International Hangar of Pancakes'
'International House of Quinoa'
'International Pyramid of Pineapple'

As you can see in the above example, using named parameters has several benefits. Using named parameters allows you to specify values for particular parameters, leaving other parameters with their defaults, even if they’re in an order that would otherwise prevent this. Named parameters also allows you to specify parameters to functions in an out-of-order fashion.

Example: a random restaurant name generator

Here’s an example program using our simple restaurant name generator function, our human_join function, and ucfirst, in concert with sowpods.txt, to generate a list of randomly generated absurdist restaurant names.

import sys
import random

def ucfirst(s):
  return s[0].upper() + s[1:]

def restaurant(building="House", foodstuff="Pancakes"):
  return "International " + building + " of " + foodstuff

def human_join(parts, conjunction):
  if len(parts) == 1:
    return parts[0]
  first_join = ', '.join(parts[:-1])
  return first_join + " " + conjunction + " " + parts[-1]

words = list()
for line in sys.stdin:
  line = line.strip()
  words.append(ucfirst(line))

for i in range(10):
  number_to_sample = random.randrange(1, 5)
  things = random.sample(words, number_to_sample)
  print restaurant(random.choice(words), human_join(things, "and"))
Program: restaurants_forever.py


Here’s what it looks like:

$ python restaurants_forever.py <sowpods.txt
International Pronunciation of Cuttiest, Transparentize, Synagogal and Eschar
International Townsman of Betulaceous
International Ohia of Sceptring
International Tedeschi of Beltways
International Sprang of Dialoged and Sextoness
International Vasectomized of Shamiyanahs, Osmometries, Recentrifuged and Chlamydomonades
International Elastance of Somniloquize
International Sinuosities of Moutered
International Sigla of Platanes and Technography
International Tutorism of Nestfuls and Ozone


So! As demonstrated here, there are a few benefits to breaking your program up into functions.

  • Your code is easier to read, especially for someone who want to read your code and just understand the gist of it, but not necessarily the deeper details. (This person will probably be you, a week after you wrote the code.) Writing human_join is much clearer than writing out the whole procedure inside the for loop.
  • You’ve cut down on repetitiveness. Once you’ve defined a function, you can use it anywhere in your program without having to type it out again.
  • You’ve isolated where you need to make changes to your program. If you used (e.g.) the restaurant function more than one place in your code, and you wanted to change it to return strings in the format of International [foodstuff] [building] (or whatever), you would need to make that change in only one place.

Modules

A Python module is a file that contains one or more function definitions. Modules are a handy way of keeping related functions together, so that you can easily reuse them between projects, or share them with other programmers.

Making a module is easy. Just make a file that has a .py extension and contains only import statements and function definitions. (You can also define plain variables in a module if you want.) Here’s a module called restaurantutils that contains many of the functions we’ve worked on in this chapter.

def ucfirst(s):
  return s[0].upper() + s[1:]

def halfsies(left, right):
  left_part = left[:len(left)/2]
  right_part = right[len(right)/2:]
  return left_part + right_part

def restaurant(building="House", foodstuff="Pancakes"):
  return "International " + building + " of " + foodstuff

def human_join(parts, conjunction):
  if len(parts) == 1:
    return parts[0]
  first_join = ', '.join(parts[:-1])
  return first_join + " " + conjunction + " " + parts[-1]
Program: restaurantutils.py

Notice that if you try to run this file, nothing happens:

$ python restaurantutils.py

That’s because the module doesn’t actually do anything—there’s no code in there that operates on standard input, or prints something to the screen. It’s just function definitions. But once we’ve created the file, we can use those functions in another program, or in the interactive interpreter. How? Using the import keyword:

>>> import restaurantutils
>>> print restaurantutils.restaurant(building="Embassy", foodstuff="Ganache")
International Embassy of Ganache

A module you create is just like any other module in the Python standard library, or like any other module you’ve installed with pip (with the caveat that it needs to be in your current working directory for import to work.) You can use from X import Y to get just one particular function from a module:

>>> from restaurantutils import halfsies
>>> print halfsies("It was the best of times", "It was the worst of times")
It was the borst of times

And you can use your module in other programs. Here’s restaurants_forever.py rewritten to use the functions defined in restaurantutils:

import sys
import random
from restaurantutils import ucfirst, restaurant, human_join

words = list()
for line in sys.stdin:
  line = line.strip()
  words.append(ucfirst(line))

for i in range(10):
  number_to_sample = random.randrange(1, 5)
  things = random.sample(words, number_to_sample)
  print restaurant(random.choice(words), human_join(things, "and"))
Program: restaurants_forever_with_import.py


The output should be essentially the same:

$ python restaurants_forever_with_import.py <sowpods.txt
International Posthorses of Sassywoods, Tubuliflorous and Transportations
International Ouzo of Chalupas
International Paperworks of Misprograming and Foremast
International Frigidity of Shadowgraphies and Oversauces
International Netted of Communes, Curarisations and Rouleaux
International Palisaded of Deliverymen, Tapioca and Cerulean
International Goldthreads of Fridged and Floused
International Pyknics of Scolex, Farrucas, Scrimmaging and Nomadizations
International Embrowning of Jill, Prepensing and Litharge
International Gulfs of Coryphee, Detractress, Collectorate and Plantae

Modules that are also programs

Occasionally it’s useful to make modules that also function as scripts, so that you can import them to use the functions contained therein but also run them from the command-line. As an example, let’s say we have a module called ucfirst.py that has just one function defined, the ucfirst function from the discussion above.

def ucfirst(s):
  return s[0].upper() + s[1:]
Program: ucfirst.py

I can use this file in the interactive interpreter like so:

>>> from ucfirst import ucfirst
>>> print ucfirst("let's take this outside, Biff")
Let's take this outside, Biff

But it’d also be nice if I could use this module on the command-line directly, to do something useful—like, say, take every line of standard input and print it out with the first letter capitalized. With the module as-is, we’d have to create a separate Python program that imported the module and then used the function, which is a hassle.

Fortunately, there’s a little trick you can use: if __name__ == '__main__'. If you include that if statement inside of your module, then any code in the if block will be executed only when your module is run from the command-line (and not if it’s imported from the interactive interpreter or another Python program). Let’s change the ucfirst.py file to try it out.

def ucfirst(s):
  return s[0].upper() + s[1:]

if __name__ == '__main__':
  import sys
  for line in sys.stdin:
    line = line.strip()
    if len(line) > 0:
      print ucfirst(line)
    else:
      print line
Program: ucfirst.py

Now we can use the module as a stand-alone program, like so:

$ python ucfirst.py <sea_rose.txt
Rose, harsh rose,
Marred and with stint of petals,
Meagre flower, thin,
Spare of leaf,

More precious
Than a wet rose
Single on a stem --
You are caught in the drift.

Stunted, with small leaf,
You are flung on the sand,
You are lifted
In the crisp sand
That drives in the wind.

Can the spice-rose
Drip such acrid fragrance
Hardened in a leaf?

… but it still works as a module as well:

>>> from ucfirst import ucfirst
>>> print ucfirst("my favorite first names are Gertrude, Jehosephat and Gary")
My favorite first names are Gertrude, Jehosephat and Gary

Onward!