Module 6 - Custom Functions Header

Module 6 - Custom Functions

What's a Custom Function?

So far in Python, we've been using several different functions that are built-in parts of the Python language.  An example of a built-in or standard Python function that we've been using quite a bit is the print() function. The print() function takes whatever object you pass it between the parenthesis, and prints it to the Python console as output, like this:

print("Hello!")

This outputs:

Hello!

It seems simple, but the print() function actually does a number of things, including starting a new line, deciding how to print whatever it is you. give it depending on the type of thing it is, and so on.  The point is, Python functions are simple commands that sometimes actually do very complex things.

Python version 3.11.4 has 68 built-in functions, which might sound like a lot at first.  However, it doesn't take very long for most budding programmers to realize that they want to do more than the basic things that Python offers.  We also often have a good reason to package complex actions into a single command, so that they can be executed over and over without repeating all of the steps.

This is where custom functions come in very handy.

Here are some of the benefits of using custom functions:

  • Reusability: Custom functions can be reused in multiple places in your program. This saves you time and effort from having to write the same code over and over again.   
  • Readability: Custom functions can make your code more readable by grouping related code together and giving it a name. This makes it easier for other people to understand what your code does.   
  • Maintainability: Custom functions can make your code easier to maintain by isolating changes to specific parts of your code. This makes it easier to find and fix bugs.   

Let's say, somewhere in some program I want to write, I'd like it to print out the following Haiku about Python:

Python, the language
Of simplicity and power
A joy to program in

We might print out those three lines using three print statements, like this:

print("Python, the language")
print("Of simplicity and power")
print("A joy to program in")

This would be fine, if we only wanted to print it out one time.  But if the Haiku needed to appear several times throughout the code, it could lead to a lot of wasted effort, redundant coding, and possibly even increased errors in the program.  It would be a much easier, cleaner solution if we turned the Haiku into a custom function, like this:

def writeHaiku():
print("Python, the language")
print("Of simplicity and power")
print("A joy to program in")

Now, each time we called the function writeHaiku(), it would reliably spit out those same three lines. That might not seem like a big deal yet, but this is just the very tip of the iceberg when it comes to custom functions.

Let's take apart the writeHaiku() function, and get a feel for the important parts of a function:

  • def - This keyword is used to define a custom function. This tells Python that we are creating a custom function.
  • writeHaiku - This is the name of the function. It's a good idea to name a function something that let's everyone (especially you) know what the function does.  I like to use camel case for my function names.
  • ( ) - The parenthesis always come after the function name, even if they are empty (they won't often be empty when you start creating more powerful functions).  You might have noticed already, but it's common to include them after the name of a function when you are mentioning it in a narrative block of text, like this one:  I wrote a function called writeHaiku() that prints out a three-line poem.  Doing this helps people see that you are referring to a function name in your text, without even saying so.
  • - The syntax of colons and indented code blocks should be starting to feel more familiar to you now.  Functions use the same convention, where the indented code block below the declaration line of the function consists of all the lines of code that are executed when the function is called.

To call a function is to execute the code inside the function's definition. Calling a function is easy, especially if the function doesn't require any parameters.  All you do is type the name of the function, including the parenthesis after the name, like this:

writeHaiku()

The result of this would be the following:

Python, the language
Of simplicity and power
A joy to program in


Parameters

Parameters allow us to create even more powerful functions, by allowing us to provide a function with additional information that it can use, right at the time when the function is called.

Take a look at the following code as an example:

def sayHello (strName):
print("Hello, " + strName)

sayHello("Bill")

Let's take each piece of the code above, and see how it all works together:

  • def - Again, we start the definition of a function with the def keyword, telling Python what we are about to do.
  • sayHello - This part is the name of the function.  This becomes the command that we will use in the rest of our code, in order to call the function.
  • (strName) - This part, which is inside the parenthesis which are always required even when empty, creates a parameter called strName. Based on what the parameter is called, we might infer that it is a string, and also the name of something.  Essentially, we are creating a variable that is is now required when calling the function, and that can be used inside the function.  NOTE: Parameters are only accessible from inside the function, meaning code outside the function can't do anything with strName.
  • print("Hello, “ + strName) - Here, we are using the print ( ) function to print a complex string to the console.  The string is made up of two parts: The first part is ”Hello, " and represents those exact characters.  The second part of the string is whatever is in the strName parameter.  The data that is supplied to this variable needs to be a string, or there will be an error.

That part of the code was the definition of the function.  In the next line we call the function by using the name of the function and providing it with what is called an argument.  An argument is the data that we provide to a parameter of the function.  In this case, the string that we send as the argument is “Bill”, which then gets concatenated (this is the word we use when adding two or more strings together) with the other string and printed out, resulting in the output:

Hello, Bill

Here is a slightly more complicated function that has two parameters, and also includes comments explaining each line:

# Define the addNumbers function
def addNumbers(intNum1, intNum2):
# Create a variable intAnswer, and set it to the value of intNum1 plus intNum2
intAnswer = intNum1 + intNum2
# Use an f-string to print out the values of the variables in a sentence.
print(f"The sum of {intNum1} and {intNum2} is {intAnswer}.")

# Call the function, using 5 and 7 as the arguments.
addNumbers(5, 7)

You can see in the code above that the function, called addNumbers, takes two parameters: intNum1 and intNum2.

The function is then called using the arguments 5 and 7.

Inside the function, the parameters behave like variables of the same name, so intNum1 equals 5, and intNum2 equals 7.

The result of this code, when it is run, is:

The sum of 5 and 7 is 12.


The Return Keyword

So far, we've been looking at custom functions that do something. Namely, we've been making functions that print something out when called, whether they are functions that require arguments or not.

There are two big categories of functions, and sometimes a function can be in both categories:

  1. Functions that do something.
  2. Functions that return something.

When a function returns something, that means that whatever the function does, it produces some kind of result that gets sent back to the program.  That result can be fed into another function, set as the value of a variable, or printed out. If a function that returns something is called, but nothing is done with what it returns, then nothing will happen in the console.

Take the following code for example:

# Define a function that says "Hi Bill!"
def sayHiBill():
# Create a string for the message
strHiBill = "Hi Bill!"
# Send the message back to Python
return strHiBill

This code creates a very simple function. However, notice that instead of printing something out, it just returns a message in the form of a string.  So, let's call this function, like this:

sayHiBill()

Nothing appears to happen. However, something actually did happen - you just didn't see the result because we didn't tell Python to do anything that would be visible to a user.  We call the sayHiBill function, which returns a message. However, since we don't tell Python what to do with the message, it just goes away.

If we call the same function, and assign the return value to a variable, then we can print it out, like this:

# Set strMessage to the value of what is returned by the sayHiBill() function.
strMessage = sayHiBill()
# Print out the value of strMessage
print(strMessage)

With this change to the code, now we will see the message printed to the console.

However, if that's all we want to do, then we can just put the function right inside of the print function, like this:

print(sayHiBill())

This will do the exact same thing, but with less work. Yay!

I want to make sure that you notice the parenthesis in the code above.  Every function always has its own set of parenthesis when it is called.  If no arguments are required, then they are empty, but they are still there.



Do Something and Return Something

As we learned in the section above, most functions either do something or return something, but it is completely possible to do both.

Check out the following code, which also takes two parameters:

# Define a custom function called addNumbers, with two parameters.
def addNumbers(intNum1, intNum2):
# DO something - Print a message that we are adding the numbers
print(f"Now adding {intNum1} and {intNum2}...")
# RETURN something - Send back the sum of the two numbers given.
return intNum1 + intNum2

# Call the function addNumbers with 3 and 4 as the arguments.
intResult = addNumbers(3, 4)
# Use an f string to print out the result in a nice sentence.
print(f"The answer is {intResult}.")

In the code above, the function prints out a message to the console that the numbers are being added, using an f-string to insert the numbers into the sentence from the arguments given for intNum1 and intNum2.  Then, the function returns the sum of the two numbers.

The function is called using the assignment operator (=) to set the value of the intResult variable to the return value of the function.  Finally, the result is printed into a sentence using another f-string.

Here is the result.

Now adding 3 and 4...
The answer is 7.

 So there you have it.  A function that both does something and returns something.  

You might be asking yourself – Couldn't you just have had the function print both lines?  Or, could we have just had the function just return the answer and do the printing outside the function.  The answer is “yes” to both questions, and that's actually a good thing. While this flexibility might be a little confusing at first, it also gives you a lot of power to create the kind of programs you want, in a way that makes sense to you.

Programming is actually a very creative activity – it just isn't often thought of that way.



Optional Parameters

In an earlier section, we learned that if a function has a parameter in the definition, then we are always required to give an argument to the function when we call it.  However, this isn't always true.  If we want to, we can make parameters optional when we define the function.

Here is a very simple example of what that would look like:

# Define a function called sayHi, with one optional parameter.
def sayHi(strName="Bill"):
# Print a greeting using an f-string to insert the value of the parameter.
print(f"Hello, {strName}!")

# Call the function with no argument.
sayHi()
# Call the function again, but with "George" as the argument.
sayHi("George")

In the code above, we define a function called sayHi. In the definition, we specify a parameter called strName, but there's something special about it.  When we specify the strName parameter, we also give it a default value of “Bill”, which means that if someone gives a value when they call sayHi, then it will use the given value, but if they do not, then it will use the default value of “Bill”.  

This makes the parameter optional.

Here is what we get when we run the code:

Hello, Bill!
Hello, George!

We can also have more than one optional parameter.  Check this out:

# Define the function sayIntro with optional parameters strName and intAge 
def sayIntro(strName = "Bill", intAge = 47):
# Print a sentence, inserting the values of the parameters using an f-string.
print(f"My name is {strName}, and I am {intAge} years old.")

# Call the function with no arguments
sayIntro()
# Call the function with two arguments.
sayIntro("George", 25)
# Call the function with one argument, unnamed.
sayIntro("Sam")
# Call the function using a named argument to specify the intAge parameter.
sayIntro(intAge=25)

In this scenario, we have two parameters in the function definition, strName and intAge.  Both have default values, meaning both are optional, independent of each other. This gives us a lot of options when it comes to calling the function.  Here is the result when the above code is run. See how the different ways of calling the function work to produce the output for each line?

My name is Bill, and I am 47 years old.
My name is George, and I am 25 years old.
My name is Sam, and I am 47 years old.
My name is Bill, and I am 25 years old.

Optional parameters give us a lot of power and flexibility.  Generally speaking, it is better to put required parameters (ones with no default) first in the definition, and then the optional parameters last.  However, you can have a mix of both types if you want to.

Videos for Module 6 - Custom Functions

6-1: Introducing Custom Functions (2:36)

6-2: The Benefits of Custom Functions (9:50)

6-3: Creating a Custom Function (2:44)

6-4: Using Parameters in Custom Functions (5:04)

6-5: Returning Values from Custom Functions (6:31)

6-6: S6 Explanation (11:26)

6-7: Reviewing Custom Functions (1:49)

6-8: Variable Scope (3:38)

6-9: The Global Keyword (3:46)

6-10: Optional Parameters and Default Arguments (6:17)

6-11: Keyword Arguments (4:13)

6-12: PyCharm Debugger (8:36)

6-13: A6 Explanation 1 of 4 (5:49)

6-14: A6 Explanation 2 of 4 (11:52)

6-15: A6 Explanation 3 of 4 (2:36)

6-16: A6 Explanation 4 of 4 (10:43)

Key Terms for Module 6 - Custom Functions

No terms have been published for this module.

Quiz Yourself - Module 6 - Custom Functions

Test your knowledge of this module by choosing options below. You can keep trying until you get the right answer.

Skip to the Next Question