Jesse Lawson

buy me a coffee ☕ / home / blog / tutorials / portfolio / contact

Feb 14, 2020 - Tutorials Python

Python Lecture Notes: Functions

What if we had some code that we wanted to perform a whole bunch of times?

For example, let’s say I had a log file, and each entry in the log file looked like this:

001|LOGIN_SUCCESS|lawsonje@nsa.gov|20200201|1344
002|LOGIN_SUCCESS|smithda@nsa.gov|20200202|1344
003|LOGIN_FAIL|trevamo@nsa.gov|20200203|0850
004|LOGIN_SUCCESS|trevamo@nsa.gov|20200203|0851
005|LOGIN_FAIL|cartersh@nsa.gov|20200203|0904

Let’s also say that I wanted to determine whether each entry was a successful login or a failed login. I could do that really easy as long as each line was stored as a string variable. For example, the first two lines might be:

1
2
line1 = "001|LOGIN_SUCCESS|lawsonje@nsa.gov|20200201|1344"
line2 = "002|LOGIN_SUCCESS|smithda@nsa.gov|20200202|1344"

You may recall that we can get a substring of a string by using the [ and ] characters. For example, I can pull out the first three characters of a string like this:

1
line1[:3] # Equals "001"

So if we wanted to pull out the string that indicates whether the login is successful or it failed, we just need to know the position of each character in the array:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
For a LOGIN_SUCCESS, we have character positions 4 through 17:
                                                 |         |
    +--------------------------------------------+         |                                    
    |                                                      |
    |           +------------------------------------------+
    |           |
    v           v
002|LOGIN_SUCCESS|smithda@nsa.gov|20200202|1344

For a LOGIN_FAIL, we have character positions 4 through 13:
                                              |         |
    +-----------------------------------------+         |                                    
    |                                                   |
    |        +------------------------------------------+
    |        |
    v        v
003|LOGIN_FAIL|trevamo@nsa.gov|20200203|0850

(Remember: the first position of a string is [0], not [1]).

We can determine if each entry in the log file is a LOGIN_SUCCESS or LOGIN_FAILED by checking the value of those string slices:

1
2
3
line1 = "001|LOGIN_SUCCESS|lawsonje@nsa.gov|20200201|1344"
print(line1[4:17])
# "LOGIN_SUCCESS"

Of course, if we want to DO something with the knowledge that the line contained LOGIN_SUCCESS or LOGIN_FAILED, we would want to store that knowledge in a variable, like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
line1 = "001|LOGIN_SUCCESS|lawsonje@nsa.gov|20200201|1344"
line1_failed = (line1[4:17] == "LOGIN_FAIL") # This is a fast way to set the 
                                             # value of a variable to be the 
                                             # result of a boolean expression. 
                                             # The result of <expr> == <expr> 
                                             # will be True if they are equal, 
                                             # and False if they are not.  
print(line1_failed)
# "False"

This is fine for one or two lines, but what happens if we want to check a file that contains hundreds of lines? Surely there must be something easier than:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
line1="001|LOGIN_SUCCESS|lawsonje@nsa.gov|20200201|1344"
line2="002|LOGIN_SUCCESS|smithda@nsa.gov|20200202|1344"
line3="003|LOGIN_FAIL|trevamo@nsa.gov|20200203|0850"
line4="004|LOGIN_SUCCESS|trevamo@nsa.gov|20200203|0851"
line5="005|LOGIN_FAIL|cartersh@nsa.gov|20200203|0904"
# ... 
line100="100|LOGIN_SUCCESS|reeveswa@nsa.gov|20200214|1115"

line1_failed = (line1[4:17] == "LOGIN_FAIL")
line2_failed = (line2[4:17] == "LOGIN_FAIL")
line3_failed = (line3[4:17] == "LOGIN_FAIL")
line4_failed = (line4[4:17] == "LOGIN_FAIL")
line5_failed = (line5[4:17] == "LOGIN_FAIL")
# ...
line100_failed = (line100[4:17] == "LOGIN_FAIL")

Can you imagine having to exhaustively go through each and every line like this? There are a number of problems with this approach:

  • It is nearly impossible to update. What happens when the log format changes? Because format and syntax changes to log output is common, especially if some developer pushes new updates and decides to switch an application into full logging mode and now we have NEW types of lines in the file. Are you supposed to account for every type and variant of log entry in the file? Yikes!

  • It does not account for variable length log files. In the real world, log files don’t have a set number of lines and that’s it. Some log files might be 100 lines long, others might be 1,000,000 lines long. Are we going to make a single variable for each possible line that could exist in a log file? Talk about exhausting!

Thankfully we don’t have to process a file like this. Instead, we can think about what we want to do to each line and then package up that work into a group of statements called a function.

We’re not going to be parsing a log file in this module, but I want you to be familiar with what we just talked about and the examples I provided. Our final project–the Network Intelligence Project–will make heavy use of log file parsing, so the problems we ran into earlier here are the same exact problems you’ll be facing in the last month of this course.

What is a function?

A function in programming is exactly the same as a function in mathematics. For example:

  • You can substitute a function for a variable.
  • Functions can have arguments, which are the inputs to the function.
  • Functions can return a value.
  • Functions can perform a task, return a value, or both.

How to declare a function

A function is declared by defining it using the def keyword, and will have, at a minimum, two empty parentheses after it’s name:

1
2
def say_hello():
    print("Hello, world!")

The scope of a function can be seen as anything that is indented below it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
def say_hello():
    print("Hello, world!")
#   ^
#   |
#   +--------------------------------------------------+
#                                                      |
# This is inside the scope of the say_hello() function +
#
# This is NOT inside the scope of the say_hello() function +
#                                                          |
# +--------------------------------------------------------+
# |
# v
print("Guess what?")

How to return a value from a function

A function can return a value, turning its call into an expression, or it can return a value into a variable:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# Here's an example of a function 
def get_name():
    return "Jesse"

print(f"My name is {get_name()}!")
# "My name is Jesse!"

# You can also store the return value of a function in a variable:
my_name = get_name()

print(my_name)
# "Jesse"

print(name_name == get_name())
# "True"

Functions can be used in place of an expression. For example, if I have a function that evaluates to True, I can store it in a variable like this:

def get_python_fun_factor():
    return True

is_python_fun = get_python_fun_factor()

# is_python_fun == True

Functions can return many different types. For example, here is a function that returns a string:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
def get_favorite_color(name):
    if name == "Jesse":
        return "Blue"
    elif name == "Samantha":
        return "Seafoam"
    elif name == "Emma":
        return "Brown"
    else:
        return "Black"

print(f"Jesse's favorite color is {get_favorite_color('Jesse')}")
# Prints "Jesse's favorite color is Blue"

print(f"Samantha's favorite color is {get_favorite_color('Samantha')}")
# Prints "Samantha's favorite color is Seafoam"

print(f"Jane's favorite color is {get_favorite_color('Jane')}")
# Prints "Jane's favorite color is Black"

You can see an interactive example here.

How to call a function

In addition to calling a function to return its value to a variable or as an expression, you can simply call a function by itself to make it process all the statements it contains. Calling a function just means activating it.

You do this by just writing the function by itself:

1
2
3
4
def say_hello():
    print("Hello, world!")

say_hello() # This will print "Hello, world!"

Again, you can also return the value of a function, which treats the function as an expression:

1
2
3
4
5
6
7
8
def is_silly():
    return True

if is_silly():
    print("Yes, it's silly!")

# The above would print "Yes, it's silly!" since the if statement would 
# evaluate to True. It evaluates to True because is_silly() returns True.

How to pass arguments to a function

Just like in math, a function can receive an argument. Do do this, it must be declared with placeholders for each argument:

1
2
3
4
5
def say_hello_to(name):
    print(f"Hello, {name}!")

say_hello("Ada Lovelace")
# This would print "Hello, Ada Lovelace!"

Further study