Beginner Python tutorial – 08 – Functions

Today we are going to learn about Python functions. Functions are an important concept in Python and any language, as we use them to break our program into small pieces of re-usable code, making our code more organized and tidier.

As they say, a bunch of code is worth a thousand words.

# How to calculate taxes

item_price = int(input("How much does it cost?\n"))
item_with_taxes = item_price * 1.21 # Increate the cost by 21%
print(f'Your item costs {item_with_taxes} after taxes')

Here is a small code to calculate taxes after asking the user via input for the price of an item. It is just 3 lines of code. But now, imagine we need to use this sample code 4 times in our program, at different places.

You could easily copy it 4 times, and it will of course work. But imagine that the taxes change and no longer it is 21%, but 17%. Now you have to search in your program for that code and replace it in every instance. And, of course, hope that we didn’t forget to change it in any place.

Or we can use functions.

Basic Function

If we wrap around that code around a function and we name it, we can call it anytime by its name, and the code inside the function will execute as if it was written there:

# Here define the function. Nothing is executed.
def calculate_item_taxes():
  item_price = int(input("How much does it cost?\n"))
  item_with_taxes = item_price * 1.21 # Increate the cost by 21%
  print(f'Your item costs {item_with_taxes} after taxes')

# Now, we want to ask the user for an item's price
calculate_item_taxes()
print('Code executed for the first time')
calculate_item_taxes()
print('Code executed for the second time')
calculate_item_taxes()
print('Code executed for the third time')
calculate_item_taxes()
print('Code executed for the fourth time')

Read and run the code. You’ll see it is easy to understand.

Now, if we want to change the taxes from 21% to 17% we only need to change it once: At the function. A good alternative to looking for hundreds or thousands of lines of code to replace it.

A good analogy: You tell a friend how to cook an omelette. The next time you want your friend to cook an omelette, you don’t explain all over again how to cook it, you just say “Cook an omelette”. Here, the “Cook an omelette” is a function that will “execute” the “code” to cook it.

Remember the ‘print(‘Hello, world!’)’? Print is also a function. You have been using functions from the start!

Functions with parameters

But functions are more than just a trick to repeat static code. We can pass values via parameters to be used in the code inside a function.

Different items have different taxes. In our program, were are calling the calculate_item_taxes function 4 times. Should we create 4 functions, each one with a different tax rate? We could, but that defeats the purpose of using functions.

Let’s use parameters:

def calculate_item_taxes(tax_rate):
  item_price = int(input("How much does it cost?\n"))
  item_with_taxes = item_price * (1 + tax_rate/100)
  print(f'Your item costs {item_with_taxes} after a {tax_rate}% tax rate')

calculate_item_taxes(21)
calculate_item_taxes(17)
calculate_item_taxes(5)
calculate_item_taxes(0)

Output:

How much does it cost?
100
Your item costs 121.0 after a 21% tax rate
How much does it cost?
42
Your item costs 49.14 after a 17% tax rate
How much does it cost?
20
Your item costs 21.0 after a 5% tax rate
How much does it cost?
5
Your item costs 5.0 after a 0% tax rate

Easy, right? We pass a value as a function argument, and we can use that value inside the code. That gives us more flexibility with functions. But that’s not all.

Functions with default parameters

Now we can pass values to functions, but what if we want to use the same value most of the time? We can set a default value for our parameter. If an argument is passed, it will use that value. If no argument is passed, it will use the default one:

def calculate_item_taxes(tax_rate=17):
  item_price = int(input("How much does it cost?\n"))
  item_with_taxes = item_price * (1 + tax_rate/100)
  print(f'Your item costs {item_with_taxes} after a {tax_rate}% tax rate')

calculate_item_taxes(21) # Argument = Uses 21
calculate_item_taxes() # No argument = Default 17
calculate_item_taxes(10) 
calculate_item_taxes(17)

Output:

How much does it cost?
150
Your item costs 181.5 after a 21% tax rate
How much does it cost?
150
Your item costs 175.5 after a 17% tax rate
How much does it cost?
150
Your item costs 165.0 after a 10% tax rate
How much does it cost?
150
Your item costs 175.5 after a 17% tax rate

Flexible and easy, right?

But that’s not all we can do with Functions.

Returning values

Until now we have used functions to repeat some code, so our programs are tidier. If we want to change something, we only need to address to the function, make the change and that’s all.

But we can also use functions to process calculations and return some value (an int, a list, some value…) to our code. To do so, we just need to ‘return’ the value we want to receive in our code:

def calculate_bigger_number(a, b):
  if a > b:
    return 'A is bigger than B'
  elif a < b:
    return 'B is bigger than A'
  else:
    return 'A and B is the same number'

print(calculate_bigger_number(5, 1))
print(calculate_bigger_number(1, 5))
print(calculate_bigger_number(5, 5))

The ‘print(calculate_bigger_number(5, 1))’ is equivalent to ‘print(WHATEVER THE FUNCTION RETURNS)’. As the function returns a simple string, we can print that, just like this:

A is bigger than B
B is bigger than A
A and B is the same number

Passing lists

Of course, we are not limited to numbers or strings: We can pass everything to functions. For example, lists:

def print_grocery_list(my_list):
  print('Grocery list:\n')
  for element in my_list:
    print(f'- {element}')

print_grocery_list(['apples', 'oranges', 'cherries', 'more apples'])

Output:

Grocery list:

- apples
- oranges
- cherries
- more apples

Seems like functions are easy, right?

It is your normal Python code but wrapped around a function with a name. Nothing less, nothing more.

Scope

Well, I said it was easy, and still is, but you have to mind the scope of your variables.

The scope of a variable is where they ‘live’. If a variable is inside a function (or local), its use is limited to the function. If it’s outside, the scope is global, as the function is inside the file and has access to every value (but the local ones from other functions!)

Too many words, let’s see some code:

def print_something():
  a = 10 # Scope -> Local
  print(a)

print_something()

b = 12 # Scope -> Global
def print_something_else():
  print(b)

print_something_else()

‘print_something’ can reach ‘a’ variable, as it is inside the function. ‘print_something_else’ can reach the ‘b’ variable, as the function is just a piece of the file.

If you can enter a bedroom’s house, you can turn the switch on. If you can enter a house, you can turn the bedroom’s switch on.

Exercise time

Let’s ingrain this part in your mind. Read the code before executing it and think what’s going to happen. Then, run it. If you failed to predict the result, think what really happened.

# First exercise

first = 10
def first_print():
  print(first)

first_print()

# Second exercise

def print_second():
  second = 123
  print(second)

print_second()

# Third exercise

third = 'Hi'
def print_third():
  third = 'Hello'
  print(third)

print_third()

# Fourth exercise

fourth = 'Outside value'
def print_fourth(val):
  fourth = val
  print(fourth)

print_fourth('Function calling value')

# Fifth exercise

fifth = 12
def print_fifth(val):
  fifth = 'Hello again'
  print(fifth)

Copy each exercise where do you code (IDE, Terminal, Repl.it…) and think what would be the output. Then run the code to see if you were right. If you don’t, try to understand what Python is printing that value. Do that for the 5 exercises.

If you don’t understand some result, here’s the explanation (please, read this only after you did the exercises):

1.- ‘first’ is a global variable , so we have access from any place inside the file

2.- ‘second’ is a local variable, so we have access from this function, and only inside this function.

3.- The function searches for a ‘third’ variable. We have one inside the function, so it prints its value. Granted, we have another ‘third’ variable outside, but local variables have preference over global ones.

4.- We have a global and a local variable. This local variable value is passed down from the argument used on calling for this function, so that’s its value. Remember: Local over global variables. The value comes from outside the function, but the creation of the variable is local to its function.

5.- Ah, the trick question. We are not seeing anything as we are only declaring the function, not calling for it. We tell Python how handle that function, but we never told to execute the code, so nothing is displayed here.

Recursion

We can call a function, but can the function call itself?

Of course. At the end of the function, we can call itself. Beware! This could lead to an endless loop that you can only stop by stopping the program. Or throwing the laptop by the window, but my boss disagrees.

def final_countdown(n):
  if n <= 0:
    print('Boom!')
    return
  else:
    print(f'{n}!')
    n -= 1
    final_countdown(n)

final_countdown(10)

Let’s see what happens before running the code.

We have one function that accepts one argument. We call that function passing it 10. If that number is equal or less than 0, we print ‘Boom’! and we return nothing. That escapes the function, ending it. We used ‘return’ to return a value before. Now we don’t need to return anything, just end the function.

Anything else (if the number is more than 0), we print the number, subtract one and then call it again and repeat the same process.

def final_countdown(n):
  if n == 0:
    print('Boom!')
    return
  else:
    print(f'{n}!')
    n -= 1
    final_countdown(n)

final_countdown(10)

When you are using recursion, don’t forget to write a wait out of the function or it will loop forever.

Variable-length arguments

Until now, we always passed the same number of arguments as the function creation had. On the last one, final_countdown(n), we had to pass one argument.

But sometimes we want to pass two arguments. Or 5. Or 100. Why not?

Do we have to write one function for each case? If you want, you can’t. But I’m a lazy programmer. I suggest you to be one too. Code smarter, not harder.

We have a way to pass any number of arguments to the same function, just create a function with an argument with an asterisk before the name:

def multivariable_function(*args):
  print(*args)

multivariable_function(5, 6, 2)
multivariable_function([1, 2, 3, 4], 'Cat', 12.4, 'Plane', 'Mr Boombastic', 42)

Output:

5 6 2
[1, 2, 3, 4] Cat 12.4 Plane Mr Boombastic 42

As you can see, it’s simple. But seems like we are just passing a big string of string to print it, right? Let’s see a more practical approach.

def odd_or_even(*args):
  for number in args:
    if number % 2 == 0:
      print(f'{number} is even')
    else:
      print(f'{number} is odd')

odd_or_even(3, 5, 2, 5)
odd_or_even(1, 2, 54, 3, 56, 23, 3, 8, 3, 7, 34, 5, 9)

Output:

3 is odd
5 is odd
2 is even
5 is odd
1 is odd
2 is even
54 is even
3 is odd
56 is even
23 is odd
3 is odd
8 is even
3 is odd
7 is odd
34 is even
5 is odd
9 is odd

As you can see, argument works like a list (It is a tuple, a list-like data structure. More on that on future lessons) where we can pass any number of values.

Keyword arguments

We can also pass arguments that look like dictionaries! Dictionaries are simple data structure (More on that on future lessons) that each element has a key and a value. If you’re an experienced programmer, they are like Maps or a json:

myself = {
  'name': 'David',
  'age': 35,
  'bank_account_number': 'Do not be cheeky'
}

If we want to access to my age, we just use the variable with the key ‘age’:

print(myself['age']) # Prints '35' as an int

As we did before, we can call a function and pass any number of elements using a pair of key-value arguments:

def print_my_info(**person):
  for key, value in person.items():
    print(f'My {key} is {value}')


print_my_info(name='David', age=35, bank_account_number='Do not be cheeky')

Output:

My name is David
My age is 35
My bank_account_number is Do not be cheeky

Every iteration of the for loop we access to a pair of key-value (for example, {‘name’: ‘David’}). With .items() we return both values, so we store the key in the ‘key’ variable and the value in the ‘value’ variable. We have access to them now so we print them.

Final thoughts

Wow, this lesson was long. And with a reason: Functions are the bread and butter of Python (or any) programming. We use functions everywhere.

We wrap a functionality inside a function and we call it whenever we please. If we need to tweak it a bit, we just go to the function and fix it: No need to loop over dozens of files with hundreds of lines.

It is surprising how much time and headaches functions solve.

There is no challenge after this lesson as I find it is too long to add one. But is had to be long as I need to cover so much ground and I didn’t want to leave nothing outside.

So after you have finished this one, play around with functions. Open your IDE or terminal and create functions. Add them parameters, make them return things, use default values, *arguments, **keyword arguments. Create what you want, as having fun is the best way to learn.

I hope you had fun doing lesson.

Next lesson: 09 – Function challenges

Link to the code: https://repl.it/@DavidMM1707/08-Functions

Follow me on Twitter: https://twitter.com/DavidMM1707

My GitHub: https://github.com/david1707

Learn more about:
– Built-in functions in Python: https://docs.python.org/3/library/functions.html