• Save
  • Download
  • Clear Output
  • Runtime
  • Run All Cells

Loading Runtime

Functions! Let's learn about functions in Python.

Before we get started I want you to know that this course is addressing the topic of functions much earlier than most other beginner-focused courses would. There are a few reasons for this:

  1. Functions have an important relationship with variables, and since we just talked about variables that will be easier for us to make the connection.

  2. Functions are extremely important in the code that we write. They are one of the most important building blocks of any programming language so the earlier we cover them the more time we'll have to practice and get really good at them.

  3. A lot of Temzee exercises and code challenges are going to ask you to write functions. Not only is this so that we can practice the skill of writing functions but it also makes it much easier for me to evaluate your code and determine if it's doing what it should or not. We'll talk more about why that's the case later on in the lesson.

Up to this point, we've really only talked about concepts like: integers, floats, doing math and using strings. We can write useful functions with just these few simple topics under our belt. I think it's easiest to learn about functions by writing very simple ones until you're comfortable with the syntax.

After this lesson you'll have access to a large number of exercises for practicing writing functions. You don't need to do all of these exercises, in fact, how many you do is totally up to you. I tried to make an excessive amount of exercises because it's better to have to many than to have to little. We're going to make sure that the basics of functions and how they work to are second nature to us before attacking more complex topics.

A Caveat:

HOWEVER! Even though functions are a super duper important topic we're not going to learn absolutely everything about them in this lesson. We're going to peel back the layers of this programming language little by little as we go. I have a teaching philosophy of trying to not overload you with things that aren't going to be immediately useful, so we're going to keep things simple and to the point right now and leave more advanced function-related topics for later-on.

In this lesson we will:

My goal is that by the end of this lesson that you will not only be able to write useful functions and be aware of their major constituent parts, but that you will also be beginning to form some kind of intuition around the role and usefulness of functions in the code that we write.

In order to build this intuition around the purpose and usefulness of functions we're going to explore this through three or four? different lenses (metaphors): (some of these titles will make more sense after we've talked about them).

  1. Functions as variables
  2. Functions for repeatedly executing lines of code
  3. Functions as isolated self-sufficient machines
  4. Functions for abstracting out blocks of code-functionality.

1) Functions are variables that we store code inside of

  • First line of a function (anatomy of a function)
  • We can put lines of code inside a function
  • Invoking a function (invoking parentheses)

What is a function?

A function is a variable, but instead of this variable just holding a number, or a string, or some other value, this special category of variables can hold lines of code -weird, right? Well, once we store some lines of code inside of a variable the nice thing is that we can tell the variable to run that code whenever we want, and as many times as we want. The concept of storing lines of code inside of a variable might be a little bit abstract right now, but I think it will make a lot of sense once we look at a few examples.

What's nice about being able to store lines of code inside of a variable is that now we can run those lines of code whenever we want without having to re-type them. You'll see by the end of this lesson how storing code to be reused instead of retyping it can save us a lot of work.

Our first function

Let's make our first function. We want this first function to be able to display a string in the output section of the code cell –pretty simple.

Writing the first line

The first thing you need to know is that although functions are similar to variables, we don't create functions exactly like we create regular variables. When we make a function, instead of putting the variable name first, we first put the special Python keyword def –all lowercase. (You'll recall that Python keywords are those things that we're not allowed to name variables. So you won't ever be able to make a variable called def, Python's already claimed that word for making functions.) As I type it, you can see it light up a special color, and that coloring indicates to us that it's a reserved keyword that does something special. And it does do something special. It tells Python, "hey, the stuff that comes after this is going to be a function.

By the way, def is short for "define". We could say that we're "defining" a function here –or in other words, "creating" a new function.

after def we put a space and then the name of the function. This name is just like any other variable name and follows the exact same variable naming rules and conventions as any other variable. We're going to call our function say_hello with an underscore between the two words. (So this is a "snake case" variable name, just like we learned about.)

After the variable name we put an opening and closing parentheses. This isn't part of the variable name, just part of the syntax for making a new function. Don't worry about these parentheses yet, I'll explain what they're for a little bit later.

Ok, and after the parentheses we put a colon :. The colon here tells Python that we're done with the first line of our function and that the lines of code that we write below are what we want to store inside of the function. below this line of code, we're going to write all of the lines of code that we want to store inside of the function.

Storing code inside of the function

So, on the second line here let's write a line of code that we want to put inside of the say_hello function. For simplicity's sake, let's just have that line of code be printing out a salutation of sorts. We'll print "Hello, my name is Ryan". Feel free to change Ryan to your name or any name that you like really.

print("Hello, my name is Ryan.")

After all we did name the function say_hello. It's a good idea to try and give the function a name that describes what it does or (in other words) what the code that's stored inside of it does.

Ok, we're almost done writing our first function. There's just one extremely important final detail.

The code that I've written here, is actually not inside of the function right now. When we write larger programs, we're going to have lines of code all over the place. Python has a special way to indicate which lines of code belong to this function –and should be stored inside of it– and which lines of code are outside of the function.

Any code that we want to be stored *inside * the function has to come below this first line and be indented. If the line of code isn't indented then it's not considered to be inside of the function.

Use the tab key or press the space bar 4 times to indent a line of code.

"Calling" our function

Now this function's finally done. Let's run the code cell so that this function gets stored within our notebook's memory.

Excellent, now that we've "defined" our function, how do we tell it to run?

Well, first, we write the name of the function (the variable name) so that Python knows which function we want to run, but this alone is not enough. We have to do something extra with this variable that's specific to functions in order to get the code stored inside it to actually run. If I run this code cell with just the name of the function I'll get this funky output: <function say_hello at #########>. This output is telling me that Python did indeed find the say_hello function stored in the notebook's memory, but that's it. It hasn't been told to run it yet.

<function say_hello at 0x1153850>

In order to tell the function to actually run, after the variable name we put a set of opening and closing parenthesis, in this specific order: ()

These are sometimes called the "invoking parentheses". "Invoke" is another word for running a function, you'll also commonly hear this referred to as "calling" a function, or "executing" a function, or simply just "running" a function. These words all mean the same thing in this context.

Sometimes I like to imagine that little circle that these parentheses make looks like an imaginary button that needs to be pushed in order to kick off the running of the function. They are absolutely essential. If I leave then off, then nothing happens.

Now, when we run the code cell the print statement that we stored inside of the function is run, and the print statement successfully displays our message to the output section of the code cell.

Hello, my name is Ryan.

Please note that the message was displayed in the code cell where we "called" the function. Not in the code cell were we defined the function. The code cell where we defined the function only caused the newly defined function to be saved to our notebook's memory. But the code that's stored inside of the function, is considered to be executed at the location in our program where the function was called, not where it was defined.

With this first example I hope you can see how a function is like a variable that stores lines of code inside of it. Even if it does require a few additional symbols and "syntax" compared to a regular variable.

2) Functions make it easy to repeatedly execute lines of code.

Now, what happens if I call this function, say, four times times in a row?

Hello, my name is Ryan.Hello, my name is Ryan.Hello, my name is Ryan.Hello, my name is Ryan.

Well, it probably does what you would expect it to. The print function (that's stored inside) is executed once each time the function is called. But I have a question for you: Why is this code any better than say, just repeating the print statement four times? –like so:

Hello, my name is Ryan.Hello, my name is Ryan.Hello, my name is Ryan.Hello, my name is Ryan.

Well, I guess calling the function is a little bit less typing than the writing out the full print statements. We'll answer this question more fully in the next two sections. For now, just keep it in mind: Why is putting code in a function and then calling the function any better than just writing the lines of code directly? Let's explore.

3) Functions are like machines that can take in inputs and spit out outputs.

Improving "hard-coded" functions

Remember how I said that we'd talk about the parentheses a little bit more. Well now's that time. The say_hello function is what programmers would call "hard coded." One characteristic of something that is "hard coded" is that its operation is static and can't be changed without modifying the original function definition.

If I want to change what the say_hello function does, I would need to go back up and change the lines of code that are stored inside of it. As it's currently written, it's a one-trick-pony, it can only do one thing.

"Hard coding" isn't always bad, but code that is "hard coded" is typically not the most useful version of the code that we could write. The secret to addressing this problem, making our function more useful and allowing is to customize its behavior each time we call it –is the parentheses that we skipped over before.

Right now our function can only introduce itself as "Ryan" -every single time. Let's write a new function that can say "hi" to any person that we tell it to.

To start off writing this new function, let's first write our lines of code outside of a function and then move them inside of a function once we feel like we've got them working the way that we want.

Let's write a print statement, and print the string 'Hi, but then we're going to put a comma and put the variable name after the string.

If I run this line of code right now, what do you think will happen?

File "<exec>", line 1, in <module> NameError: name 'name' is not defined

OK, we see an error message that tells us that the variable name is not defined. That means that we haven't created the variable name anywhere inside of our notebook. So let's do that. Let's make a variable called name and assign it a value on the line just above our print function. I'm going to use the name "Ryan" for starters here.

Hi Ryan

Now the code works as expected. The print statement is taking the value that is stored to the variable name and printing it out immediately after the string 'Hi'. So now, we can say hi to –whatever name we store to this variable. However, this code is still "hard-coded" because I can't change it's operation without altering the code. Let's change the value that we're assigning to the name variable and run the code again.

Let's change the name to Steven.

Hi Steven

Nice, now our code has done something different -which is progress. the print statement's output changed because the value stored to the variable name changed.

Our goal is to create a function that can customize this string and print it out on-demand with with any name that we provide to it.

Let's start off by making our function by writing the first line:

def say_hi():

And then we'll put our print statement inside of the function. Don't forget to indent the line so that Python knows that this code is inside of the function!

And then we'll also invoke the function on line four. It actually doesn't matter what line you call the function on as long as you call it after it has been defined and you don't indent the code where you're calling it. Indented lines are inside of the function. Unindented lines are outside of the function.

Hi Steven

Cool, our function is printing out "Hi Steven" because Steven was the last value that we stored inside of the name variable.

Now let's use those parentheses. At the top of the function let's put the variable name first_name inside of the parentheses on the first line. And in the print statement let's change name to first_name as well. I'm using this new first_name variable instead of the name we used previously, because I don't want this function to have anything to do with the code that we wrote above. By using a new variable, I'm removing any references to the previous code.

Now, where we're calling the function, let's put a string inside of those parentheses. Let's use the string "Gerald".

After making those changes, go ahead and run the code cell .

Hi Gerald

Very cool, now as a demonstration I'm going to call this function a bunch more times, but each time I'm going to put a different string inside of the "invoking parentheses".

Hi ArnoldHi SamanthaHi Olivia

Oooh, now we see that instead of printing out the same message each time, the function's behavior changed each time. This function is no longer hard-coded. Its behavior is not static, and I can change its behavior without having to rewrite any part of the original function. Let's dive into the nitty gritty of how this is achieved.

"Parameters" vs "Arguments"

Back up where we defined the function originally we put the variable first_name inside of these parentheses at the top. There's a term for this variable here. This variable is called a "parameter". That's some very important terminology so I'm going to say it again. the first_name variable on the first line of the function definition is called a "parameter".

Including this "parameter" in the first line of the function tells Python. "Hey, this function, it requires an input in order for it to run properly.

When we call the function, the value that we put inside of the invoking parentheses is called an "argument" that's also a very important piece of terminology that you need to memorize.

In programmer-speak, we say that we "pass-in" values when we call a function. In more plain English, instead of saying we're "passing-in arguments to the function" we might say that we're "providing the function with a required value that it needs in order to do its job.

Let's try and call the function without passing in any arguments and see what happens.

File "<exec>", line 1, in <module> TypeError: say_hi() missing 1 required positional argument: 'first_name'

Because we've already talked about some of this terminology, I hope this error message makes a little bit of sense. It's telling us that the function requires an argument, but it's missing. We're not providing one.

Parameters tell us what inputs are expected or needed by the function. Arguments are specifically what we provide or "pass-in" to the function as inputs so that it can do its work.

I like to think of parameters as if they're asking the question "what?". WHAT's the first name? And arguments as answering that question –"Olivia". Olivia is what the first name is!

"Positional" vs "Keyword" arguments

There's something about arguments and parameters that always confused me when I was starting out. And it's a question that I'd like to pose to you.

Where in the above code did the variable first_name get assigned the value of 'Gerald'? Well, as the function is written right now there's no place where that explicitly happens, the function does this for us behind the scenes.

Although this is something that the function just does for us automatically, we can be more explicit about this assignment if we want, but this is totally optional.

When I'm calling the function I can explicitly assign the string 'Gerald' to the variable first_name and then there will be no more mystery.

Hi Gerald

When we explicitly specify which parameter the argument should be assigned to like this, this is called using a "keyword argument".

When we don't explicitly specify the assignment in the function call, we say that we're using a "positional argument". It's called a positional argument because Python decides what parameter to assign the passed-in value to purely based on the order in which the arguments were provided. Because we're only working with one value here it's hard to demonstrate that. In a minute we'll make a new function that requires multiple parameters and we'll see positional arguments in action again.

Also, that's why in the error message that we saw when we called the function without any arguments we saw it say that there was "1 required positional argument".

As you get started writing functions, I would encourage you to write your functions explicitly using keyword arguments so that there isn't any confusion. However, this is totally optional and the function runs exactly the same even if we just use the positional argument.

Hi GeraldHi Gerald

Functions requiring multiple arguments/parameters

Well, what about functions that require more than one parameter? Then what? Let's write a function, that requires multiple parameters.

Hello, my name is Ryan Allred

If a function requires multiple parameters we simply list both of them inside of the parentheses separated by a comma: , –simple as that. There's no theoretical limit to the number of parameters that can be required by a function. Practically however, you have to remember that every parameter you add requires an corresponding argument. So whenever you can only require parameters that are truly essential for the operation of the function's contents.

There are ways to make parameters optional, but we're not going to talk about that in this lesson. We'll cover it at a later time.

When we have functions that require multiple arguments we can really get a feel for the biggest differences between positional and keyword arguments. Look at what happens to the output if I reverse the order of positional arguments:

Hello, my name is Allred Ryan

Positional arguments get assigned to their corresponding parameters based on their position hence the name. So when I call the function like we did here: full_introduction('Allred', 'Ryan'). "Allred" gets assigned to the first_name parameter, and Ryan gets assigned to the last_name parameter. Python assumes that you're providing them in the same order in which the function definition has them listed. This doesn't throw any kind of an error, but the behavior may not be what you intended.

The nice thing about keyword arguments is that because you're explicitly assigning arguments to parameters, the order doesn't matter and the behavior will always be consistent. This is another reason why I recommend that beginners get in the habit of using keyword arguments whenever it's convenient. It removes a potential source of unexpected behavior.

Hello, my name is Ryan Allred
Hello, my name is Ryan Allred

As we wrap up our discussion of parameters vs arguments here, I just want to point out that in some instances the words "parameter" and "argument" are interchangeable. For example the sentence: "The function expects multiple parameters" is equally as true as the sentence "The function expects multiple arguments". Because these vocab words are often interchangeable, programmers (including myself) will sometimes get lazy about differentiating between the two and will default to calling everything a "parameter". In a perfect world we would always use the most precise terminology, but we don't. So just be aware of this. Regardless you should know the difference between the two and be able to use the correct terminology.

  • Parameters are the variable names listed in the function definition and that are used as placeholders throughout the body of the function.
  • Arguments are the real values passed to and received by the function.

Function Outputs

We've talked a lot about how to provide inputs to functions, but what about getting an output out of a function? So far our functions haven't provided any output. They have displayed printed messages to the "output section" of our code cells, but these printed values are more of a side-effect of the code's execution rather than a true output.

To demonstrate how we can get output from a function and why that might be useful, let's write a new function called add_together. All that this function is going to do is it's going to take in two numbers num1 and num2, and then in the body of the function we're going to add them together and store the sum to a new variable called... well, sum, and then... here's the new part –this time we're not going to print out this value. Instead we're going to add new line at the end of our function. This final line is called the "return statement".

The return statement always starts off with the python keyword return –all lowercase like usual. and then after this keyword we put the value (or variable that holds the value in this case) that we want to spit out of the function. We could also say that this is the value that we want to "return" from the function.

Now that our function is written, let's call it down below on a new line by writing the function name add_together, adding our invoking parentheses, and then passing-in two numeric arguments. In this case I'm going to use two small integers but you could put in any numeric values that you like here. I'll just use 3, and 4.

7

When we run the function we see that the number 7 (the sum that's being returned from the function) is automatically displayed in the output section of the code cell even though we didn't use a print statement. This is a behavior of notebooks that we've talked about previously. If the very last line of code in a code cell returns a value the notebook will automatically display it for us in the output section.

The return statement is really all that it takes to get value back out of our function. We'll make a few more examples here in a minute to really see how this can be powerful, but before we get to that, I want to emphasize that the return statement should be the very last line of any function.

When the Python interpreter that's executing your code arrives at a return statement it calculates the value that should be returned and then it immediately exits the function. Any lines of code that you put after a return statement will be unreachable by the Python interpreter and will never be executed.

Watch this. What happens if I put a print statement after the return statement and then call the function? Will the print statement be executed?

7

No, the print function never runs because Python exits the add_together function on the line above it. So, in a previous video when I said that the print function will always run no matter where it is found in the code cell, that wasn't 100% true. Any lines of code no matter what they do –that are written after a return statement within a function will not be executed.

print() is NOT a substitute for return

A common mistake that I see a lot of beginners making is assuming that the print() function does the same thing as a return statement because at the end of the day they both (in certain instances) cause values to be shown in the output section of a code cell. I want to disabuse you of that notion and help you grow in your intuition around what it means for a value to be returned from a function and how returned values can play a role in the rest of our code in a way that printed values simply can't.

Let's write two functions here. They're going to be so simple that they're not really going to be useful functions, but I hope that the simplicity here helps illustrate the point that I'm trying to make, bear with me.

The first function we'll call return_5 and there won't be any logic inside of this function, we're just going to return the number 5 every single time. Yes, this function is hard-coded and will do the exact same thing every time, but that's ok.

Down below we can call the function and see that the number 5 is displayed in the output section of the code cell after it's returned from the function.

5

Okay, and let's contrast this with another function that we'll write called print_5 that's not going to do anything but print out the number 5 to the output section. And we'll call it down below as well.

5

Okay, so far it looks like these two functions do the exact same useless thing, right? They just cause the number 5 to be displayed. I want to make it clear to you that these functions are not doing the same thing even though the output section of the code cell looks the same. Let me show you how these are two very different things.

Whenever we call a function that returns a value, we can imagine that the bit of code where we call the function is getting replaced by the returned value. Notice that if I literally do this, if I just replace return_5() with the literal number 5 in my code cell the number 5 will still be shown in the output section.

5

But now, imagine with me for a moment that this expression return_5 was part of some larger code operation. For example, let's take the number 10 and add return_5() to it.

15

Now we see the number 15 displayed in the output section.

As Python read this line of code it saw the number 10, and then the plus sign, and said "Ok, looks like we're doing some addition here." And then it got to the function call and Python said, "Uh oh. I can't finish this addition until I know what both values are that I'm adding together, let's execute this function and we'll use whatever it returns in place of the function call as the second number in the addition.

So return_5() was replaced with the number 5 and then Python finished the addition by processing 10 + 5 resulting in 15.

I hope I'm not belaboring this point too much, but it's extremely important to me that whenever you you see a function call in your code you say to yourself, "ok, that function call is going to get replaced with the value that is returned from the function.

This is not the case with a print statement. Print statements don't get replaced with anything. They just display stuff, and that's it. So what if I try and do the same thing with our print_5() function?

File "<exec>", line 1, in <module> TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'

In this case we get an error. Python's trying to do the addition. and it sees an int on the left-hand side of the plus sign + as the first "operand", but when it looks on the right-hand side of the plus sign, it sees the print_5() function and tries to execute the function, but the function doesn't return anything so this NoneType object is put in place of the function call instead -because there wasn't any value returned, and it can't do addition between an integer and nothing, so it throws an error.

I think it's important that you see this error message because seeing this NoneType error (indicating the absence of a return value) pop up in your code, it can be an indicator to you that you might have forgotten to add a return statement to your function. In the vast majority of circumstances a function should return some kind of a value.

To further emphasize this, what happens if I just print out the function calls here in a code cell?

5
5None

When we print out print_5() the print statement inside of the function still runs and is what's causing the first number 5 to be outputted. The value of None that is displayed is the NoneType object indicating that nothing is being returned from the function.

Putting it all together

Let's write a more useful function to demonstrate everything that we've learned up to this point.

We're going to write a function that can calculate a person's Body-Mass Index or BMI. BMI is a comparison between a person's height and weight that (while not a perfect metric) can be a useful indicator for whether or not a person has a healthy bodyweight.

If we use metric units for height and weight (kilograms for weight and meters for height) then the BMI calculation is as follows:

bmi = weight_in_kg / height_in_m**2

I want to point out that height in meters –in the denominator– gets squared because... that's how the metric is calculated. I don't make the rules.

Ok, so this is what we want our function to do . Let's pick some values for height and weight, and make them as variables. Let's say that a person weighs 90 kilograms and is 1.7 meters tall.

31.14186851211073

That person's BMI would be about 31.14. Any BMI of 30 and above is considered "obese" according to the powers that be, so this is probably not the healthiest weight for this person.

Let's formalize this slightly more by storing the result of this calculation to the variable bmi and then printing the value out.

31.14186851211073

Cool! so we can make a useful calculation. Now we want to put that calculation into a function where we can provide the height and weight as inputs (or arguments) and get then return the final BMI from the function. That way we can provide any height and weight values (in the right units) and get a custom BMI. Let's do it!

We'll call our function calculate_bmi. It requires two parameters: weight_in_kg and height_in_m. We'll calculate bmi just like we did above within the function and then return the final bmi value.

Remember, you can call your parameter variables whatever you want –as long as you call them the same thing and use them correctly within the body of the function. I could have called these kg and m and that would be fine, but I like to be overly specific with my variable names. It's up to you.

Once we've written our function, let's call it using keyword arguments and our notebook will automatically display the returned result for us.

31.14186851211073

I also want to point out, that we're not required to store the final value to a variable before returning it, we can also just return the result of the calculation directly. Whichever version of this function you choose to write is a matter of personal preference.

31.14186851211073

To demonstrate how the function call gets replaced in our code by the final returned value, let's print out a message that includes this calculated BMI. This time we'll use positional arguments.

Gerald's Body-Mass Index is: 31.14186851211073

Helper Functions

Next, I want to present a simple yet powerful concept called "helper functions." A helper function is a term used to describe a regular plain-old function that helps some other function to do its job. Imagine we wanted to customize the BMI message that we printed out above with any person's name. We're going to make a new function, and all that it's going to do is customize the print statement that we wrote above with a user-provided name –rather than the hard-coded name of "Gerald".

Let's call this function personalized_bmi and we'll add a new name parameter to the first line here. Then we can copy our print statement from above, and replace the first part of the string (where we put "Gerald" previously) with whatever name that gets passed-in.

Then we'll call this function with the name "Sally" and we'll say she weighs less than Gerald (70 kgs), but she's the same height (1.7m). 1.7 meters is about five foot seven –for any of you Americans, Liberians or Burmese people that might be watching.

See how the personalized_bmi function contains a call to the calculate_bmi function within it? In this instance calculate_bmi is acting as a "helper function" to personalized_bmi.

In simplest terms, you could say that a helper function is any function that get's called within the body of another function. We don't have to use the calculate_bmi function here at all in fact. We could have also chosen to repeat the entire bmi calculation inside of personalized_bmi (see below).

However, when we do this, our print statement is starting to get a bit large and unwieldy. the function name calculate_bmi was helpful in that it describes in plain English (well, I guess in camel-case variable-name English) what the function's purpose is a little bit more descriptively than the raw calculation itself.

Over time you'll grow in your intuition about when to take a piece of functionality and break it out into its own function. When to do something like this is largely subjective is something that you'll form your own opinions on over tiem.

When used properly, helper functions can make our code more readable, maintainable, and reusable. Once a function is written within a program it doesn't ever have to be written again, but it can be called and thereby reused as many times as is needed.