computers and plants

Functions in Python

Boot.dev Blog ยป Tutorials ยป Functions in Python
Lane Wagner
Lane Wagner

Last published December 14, 2024

Subscribe to curated backend podcasts, videos and articles. All free.

Functions in Python allow us to reuse and organize code. For example, say we have some code that calculates the area of a circle:

radius = 5
area = 3.14 * radius * radius

That works! The problem is when we want to calculate the area of other circles, each with its own radius. We could just copy the code and change the variable names like this:

radius = 5
area1 = 3.14 * radius * radius

radius2 = 7
area2 = 3.14 * radius2 * radius2

But we want to reuse our code! Why would we want to redo our work? What if we wanted to calculate the area of thousands of circles??? That’s where functions help.

We’re making all the static content from our Boot.dev courses available for free here on the blog. This one is the “Functions” chapter from Learn to Code in Python. If you want to try the far more immersive version of the course, do check it out!

Instead, we can define a new function called area_of_circle using the def keyword.

def area_of_circle(r):
    pi = 3.14
    result = pi * r * r
    return result

Let’s break this area_of_circle function down:

  • It takes one input (aka “parameter” or “argument”) called r
  • The body of the function is indented - this is the code that will run each time we use (aka “call”) the function
  • It returns a single value (the output of the function). In this case, it’s the value stored in the result variable

To “call” this function (fancy programmer speak for “use this function”) we can pass in any number as the argument (in this case, 5) for the parameter r, and capture the output into a new variable:

area = area_of_circle(5)
print(area)
# 78.5
  1. 5 goes in as the input r
  2. The body of the function runs, which stores 78.5 in the result variable
  3. The function returns the value 78.5, which means the area_of_circle(5) expression evaluates to 78.5
  4. 78.5 is stored in the area variable and then printed

Because we’ve already defined the function, now we can use it as many times as we want with different inputs!

area = area_of_circle(6)
print(area)
# 113.04

area = area_of_circle(7)
print(area)
# 153.86

Functions are tricky! It takes a minute to get used to them, but after that they’ll be second nature to you. You might find yourself slowing down a bit, and if you do, that’s totally normal.

Subscribe to my YouTube channel if this video was helpful!

Let’s break down this function line by line so you can understand every nook and cranny of it.

def area_of_circle(r):
    pi = 3.14
    result = pi * r * r
    return result

radius = 5
area = area_of_circle(radius)
print(area)
# 78.5

Here’s a chronological explanation of what happens when the above code is executed:

  1. def area_of_circle(r): The area_of_circle function is defined for later use, but not called. It accepts a single input, the arbitrarily named r. The body of the function (pi = 3.14… etc) is ignored for now.
  2. radius = 5: A new variable called radius is created and set to the value 5.
  3. area_of_circle(radius): The area_of_circle function is called with radius (in this case 5) as the input. Finally, we jump back to the function definition.
  4. def area_of_circle(r): We will now start executing the body of the function, and r is set to 5.
  5. pi = 3.14: A new variable called pi is created with a value of 3.14.
  6. result = pi * r * r: Some simple math is evaluated (3.14 * 5 * 5) and stored in the result variable.
  7. return result: The result variable is returned from the function as output.
  8. area = area_of_circle(radius): The returned value is stored in a new variable called area (in this case 78.5).
  9. print(area): The value of area is printed to the console.

Multiple Parameters ๐Ÿ”—

Functions can have multiple parameters (“parameter” being a fancy word for “input”). For example, this subtract function accepts 2 parameters: a and b.

def subtract(a, b):
    result = a - b
    return result

The name of a parameter doesn’t matter when it comes to which values will be assigned to which parameter. It’s position that matters. The first parameter will become the first value that’s passed in, the second parameter is the second value that’s passed in, and so on. In this example, the subtract function is called with a = 5 and b = 3:

result = subtract(5, 3)
print(result)
# 2

Here’s an example with four parameters:

def create_introduction(name, age, height, weight):
    first_part = "Your name is " + name + " and you are " + age + " years old."
    second_part = "You are " + height + " meters tall and weigh " + weight + " kilograms."
    full_intro = first_part + " " + second_part
    return full_intro

It can be called like this:

my_name = "John"
my_age = "30"

intro = create_introduction(my_name, my_age, "1.8", "80")
print(intro)
# Your name is John and you are 30 years old. You are 1.8 meters tall and weigh 80 kilograms.

Printing vs Returning ๐Ÿ”—

Some new developers get hung up on the difference between print() and return.

It can be particularly confusing when the test suite we provide simply prints the output of your functions to the console. It makes it seem like print() and return are interchangeable, but they are not!

  • print() is a function that:
    1. Prints a value to the console
    2. Does not return a value
  • return is a keyword that:
    1. Ends the current function’s execution
    2. Provides a value (or values) back to the caller of the function
    3. Does not print anything to the console (unless the return value is later print()ed)

Printing to debug your code ๐Ÿ”—

Printing values and running your code is a great way to debug your code. You can see what values are stored in various variables, find your mistakes, and fix them. Add print statements and run your code as you go! It’s a great habit to get into to make sure that each line you write is doing what you expect it to do.

In the real world, it’s rare to leave print() statements in your code when you’re done debugging. Similarly, you need to remember to remove any print() statements from your code before submitting your work because it will interfere with the tests!

Where to Declare Functions ๐Ÿ”—

You’ve probably noticed that a variable needs to be declared before it’s used. For example, the following doesn’t work:

print(my_name)
my_name = 'Lane Wagner'

It needs to be:

my_name = 'Lane Wagner'
print(my_name)

Code executes in order from top to bottom, so a variable needs to be created before it can be used. That means that if you define a function, you can’t call that function until after the definition.

Order of functions ๐Ÿ”—

All functions must be defined before they’re used.

You might think this would make structuring Python code hard because the order of the functions needs to be just right. As it turns out, there’s a simple trick that makes it super easy.

Most Python developers solve this problem by defining all the functions in their program first, then they call an “entry point” function last. That way all of the functions have been read by the Python interpreter before the first one is called.

Note: conventionally this “entry point” function is usually called main to keep things simple and consistent.

def main():
    health = 10
    armor = 5
    add_armor(health, armor)

def add_armor(h, a):
    new_health = h + a
    print_health(new_health)

def print_health(new_health):
    print(f"The player now has {new_health} health")

# call entrypoint last
main()

None Return ๐Ÿ”—

When no return value is specified in a function, it will automatically return None. For example, maybe it’s a function that prints some text to the console but doesn’t explicitly return a value. The following code snippets all return the same value, None:

def my_func():
    print("I do nothing")
    return None
def my_func():
    print("I do nothing")
    return
def my_func():
    print("I do nothing")

Multiple return values ๐Ÿ”—

A function can return more than one value by separating them with commas.

def cast_iceblast(wizard_level, start_mana):
    damage = wizard_level * 2
    new_mana = start_mana - 10
    return damage, new_mana # return two values

Receiving multiple values ๐Ÿ”—

When calling a function that returns multiple values, you can assign them to multiple variables.

dmg, mana = cast_iceblast(5, 100)
print(f"Damage: {dmg}, Remaining Mana: {mana}")
# Damage: 10, Remaining Mana: 90

When cast_iceblast is called, it returns two values. The first value is assigned to dmg, and the second value is assigned to mana. Just like function inputs, it’s the order of the values that matters, not the variable names. We could just as easily call the variables one and two:

one, two = cast_iceblast(5, 100)
print(f"Damage: {one}, Remaining Mana: {two}")
# Damage: 10, Remaining Mana: 90

That said, descriptive variable names make your code easy to understand, so use them!

What happened to the variables? ๐Ÿ”—

The damage and new_mana variables from cast_iceblast’s function body only exist inside of the function. They can’t be used outside of the function. We’ll explain that more later when we talk about scope.

Parameters vs arguments ๐Ÿ”—

Parameters are the names used for inputs when defining a function. Arguments are the values of the inputs supplied when a function is called.

To reiterate, arguments are the actual values that go into the function, such as 42.0, "the dark knight", or True. Parameters are the names we use in the function definition to refer to those values, which at the time of writing the function, can be whatever we like.

That said, this is all semantics, and frankly, developers are really lazy with these definitions. You’ll often hear the words “arguments” and “parameters” used interchangeably.

# a and b are parameters
def add(a, b):
    return a + b

# 5 and 6 are arguments
sum = add(5, 6)

Default values ๐Ÿ”—

In Python, you can specify a default value for a function argument. It’s nice for when a function has arguments that are “optional”. You as the function definer can specify a specific default value in case the caller doesn’t provide one.

A default value is created by using the assignment (=) operator in the function signature.

def get_greeting(email, name="there"):
    print("Hello", name, "welcome! You've registered your email:", email)
get_greeting("[email protected]", "Lane")
# Hello Lane welcome! You've registered your email: [email protected]
get_greeting("[email protected]")
# Hello there welcome! You've registered your email: [email protected]

If the second parameter is omitted, the default "there" value will be used in its place. As you may have guessed, for this structure to work, optional arguments (the ones with defaults) must come after all required arguments.

Find a problem with this article?

Report an issue on GitHub