Functions
In this chapter you'll learn to write functions, which are named blocks of code that are designed to do one specific job. When you want to perform a particular task that you've defined in a function, you call the name of the function responsible for it.
Defining a Function
Here's a simple function named greet_user()
:
def greet_user():
"""Display a simple greeting."""
print("Hello!")
greet_user()
Output:
Hello!
Information to a Function
The function greet_user()
can be modified to accept a parameter:
def greet_user(username):
"""Display a simple greeting."""
print(f"Hello, {username.title()}!")
greet_user('jesse')
Output:
Hello, Jesse!
- A parameter is a variable used in a function definition (like
username
) - An argument is a value that's passed to a function when it's called (like
'jesse'
)
Passing Arguments
Because a function definition can have multiple parameters, a function call may need multiple arguments.
Positional Arguments
def describe_pet(animal_type, pet_name):
"""Display information about a pet."""
print(f"\nI have a {animal_type}.")
print(f"My {animal_type}'s name is {pet_name.title()}.")
describe_pet('hamster', 'harry')
describe_pet('dog', 'willie')
Output:
I have a hamster.
My hamster's name is Harry.
I have a dog.
My dog's name is Willie.
Order Matters in Positional Arguments
describe_pet('harry', 'hamster')
Output:
I have a harry.
My harry's name is Hamster.
Keyword Arguments
A keyword argument is a name-value pair that you pass to a function:
def describe_pet(animal_type, pet_name):
"""Display information about a pet."""
print(f"\nI have a {animal_type}.")
print(f"My {animal_type}'s name is {pet_name.title()}.")
describe_pet(animal_type='hamster', pet_name='harry')
describe_pet(pet_name='harry', animal_type='hamster') # Order doesn't matter
Default Values
You can define a default value for each parameter:
def describe_pet(pet_name, animal_type='dog'):
"""Display information about a pet."""
print(f"\nI have a {animal_type}.")
print(f"My {animal_type}'s name is {pet_name.title()}.")
# A dog named Willie
describe_pet(pet_name='willie')
describe_pet('willie')
# A hamster named Harry
describe_pet(pet_name='harry', animal_type='hamster')
describe_pet('harry', 'hamster')
describe_pet(animal_type='hamster', pet_name='harry')
When you use default values, any parameter with a default value needs to be listed after all the parameters that don't have default values.
# This will cause a SyntaxError
def describe_pet(animal_type='dog', pet_name): # Wrong!
pass
Avoiding Argument Errors
def describe_pet(pet_name, animal_type='dog'):
"""Display information about a pet."""
print(f"\nI have a {animal_type}.")
print(f"My {animal_type}'s name is {pet_name.title()}.")
describe_pet() # TypeError: missing 1 required positional argument
Return Values
A function doesn't always have to display its output directly. Instead, it can process data and then return a value or set of values.
Returning a Simple Value
def get_formatted_name(first_name, last_name):
"""Return a full name, neatly formatted."""
full_name = f"{first_name} {last_name}"
return full_name.title()
musician = get_formatted_name('jimi', 'hendrix')
print(musician)
Output:
Jimi Hendrix
Making an Argument Optional
def get_formatted_name(first_name, last_name, middle_name=''):
"""Return a full name, neatly formatted."""
if middle_name:
full_name = f"{first_name} {middle_name} {last_name}"
else:
full_name = f"{first_name} {last_name}"
return full_name.title()
musician = get_formatted_name('jimi', 'hendrix')
print(musician)
musician = get_formatted_name('john', 'hooker', 'lee')
print(musician)
Output:
Jimi Hendrix
John Lee Hooker
Returning a Dictionary
def build_person(first_name, last_name):
"""Return a dictionary of information about a person."""
person = {'first': first_name, 'last': last_name}
return person
musician = build_person('jimi', 'hendrix')
print(musician)
Output:
{'first': 'jimi', 'last': 'hendrix'}
You can easily extend this function to accept optional values:
def build_person(first_name, last_name, age=None):
"""Return a dictionary of information about a person."""
person = {'first': first_name, 'last': last_name}
if age:
person['age'] = age
return person
musician = build_person('jimi', 'hendrix', age=27)
print(musician)
Output:
{'first': 'jimi', 'last': 'hendrix', 'age': 27}
Using a Function with a while Loop
def get_formatted_name(first_name, last_name):
"""Return a full name, neatly formatted."""
full_name = f"{first_name} {last_name}"
return full_name.title()
while True:
print("\nPlease tell me your name:")
print("(enter 'q' at any time to quit)")
f_name = input("First name: ")
if f_name == 'q':
break
l_name = input("Last name: ")
if l_name == 'q':
break
formatted_name = get_formatted_name(f_name, l_name)
print(f"\nHello, {formatted_name}!")
Passing a List
You'll often find it useful to pass a list to a function:
def greet_users(names):
"""Print a simple greeting to each user in the list."""
for name in names:
msg = f"Hello, {name.title()}!"
print(msg)
usernames = ['hannah', 'ty', 'margot']
greet_users(usernames)
Output:
Hello, Hannah!
Hello, Ty!
Hello, Margot!
Modifying a List in a Function
def print_models(unprinted_designs, completed_models):
"""
Simulate printing each design, until none are left.
Move each design to completed_models after printing.
"""
while unprinted_designs:
current_design = unprinted_designs.pop()
print(f"Printing model: {current_design}")
completed_models.append(current_design)
def show_completed_models(completed_models):
"""Show all the models that were printed."""
print("\nThe following models have been printed:")
for completed_model in completed_models:
print(completed_model)
unprinted_designs = ['phone case', 'robot pendant', 'dodecahedron']
completed_models = []
print_models(unprinted_designs, completed_models)
show_completed_models(completed_models)
Preventing a Function from Modifying a List
Sometimes you'll want to prevent a function from modifying a list. You can send a copy of a list to a function like this:
print_models(unprinted_designs[:], completed_models)
The slice notation [:]
makes a copy of the list to send to the function.
Passing an Arbitrary Number of Arguments
Sometimes you won't know ahead of time how many arguments a function needs to accept.
def make_pizza(*toppings):
"""Summarize the pizza we are about to make."""
print("\nMaking a pizza with the following toppings:")
for topping in toppings:
print(f"- {topping}")
make_pizza('pepperoni')
make_pizza('mushrooms', 'green peppers', 'extra cheese')
Output:
Making a pizza with the following toppings:
- pepperoni
Making a pizza with the following toppings:
- mushrooms
- green peppers
- extra cheese
The asterisk in the parameter name *toppings
tells Python to make an empty tuple called toppings
and pack whatever values it receives into this tuple.
Mixing Positional and Arbitrary Arguments
def make_pizza(size, *toppings):
"""Summarize the pizza we are about to make."""
print(f"\nMaking a {size}-inch pizza with the following toppings:")
for topping in toppings:
print(f"- {topping}")
make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
Using Arbitrary Keyword Arguments
Sometimes you'll want to accept an arbitrary number of arguments, but you won't know ahead of time what kind of information will be passed to the function:
def build_profile(first, last, **user_info):
"""Build a dictionary containing everything we know about a user."""
user_info['first_name'] = first
user_info['last_name'] = last
return user_info
user_profile = build_profile('albert', 'einstein',
location='princeton',
field='physics')
print(user_profile)
Output:
{'location': 'princeton', 'field': 'physics', 'first_name': 'albert', 'last_name': 'einstein'}
The double asterisks before the parameter **user_info
cause Python to create an empty dictionary called user_info
and pack whatever name-value pairs it receives into this dictionary.
Storing Functions in Modules
One advantage of functions is the way they separate blocks of code from your main program. You can go a step further by storing your functions in a separate file called a module and then importing that module into your main program.
Importing an Entire Module
Create a file called pizza.py
:
# pizza.py
def make_pizza(size, *toppings):
"""Summarize the pizza we are about to make."""
print(f"\nMaking a {size}-inch pizza with the following toppings:")
for topping in toppings:
print(f"- {topping}")
Now create another file in the same directory called making_pizzas.py
:
# making_pizzas.py
import pizza
pizza.make_pizza(16, 'pepperoni')
pizza.make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
Importing Specific Functions
from pizza import make_pizza
make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
Using as to Give a Function an Alias
from pizza import make_pizza as mp
mp(16, 'pepperoni')
mp(12, 'mushrooms', 'green peppers', 'extra cheese')
Using as to Give a Module an Alias
import pizza as p
p.make_pizza(16, 'pepperoni')
p.make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
Importing All Functions in a Module
from pizza import *
make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
It's best to avoid this approach when you're working on projects of your own. Python may see several functions or variables with the same name, and instead of importing all the functions separately, it will overwrite the functions.
Styling Functions
Functions should have descriptive names, and these names should use lowercase letters and underscores. Descriptive names help you and others understand what your code is trying to do.
Every function should have a comment that explains concisely what the function does. This comment should appear immediately after the function definition and use the docstring format.
Summary
In this chapter you learned how to:
- Define functions using the
def
statement - Pass information to functions using parameters and arguments
- Use positional arguments, keyword arguments, and default values
- Return values from functions
- Use functions with lists and dictionaries
- Accept a variable number of arguments using
*args
and**kwargs
- Store functions in modules and import them
- Style your functions according to Python conventions
Functions are essential for writing clean, organized, and reusable code. As your programs become more complex, functions will help you organize your code and make it easier to maintain and debug.