Python Functions

Functions are a set of instructions grouped together to perform a specific task. Python comes with many built-in functions such as len() to get the length of a collection and sorted() to sort the elements of a collection. However, you may want to do more than what the default functions offer. Python lets you define your own functions to perform all the tasks you want. Creating your own functions also makes your code more organized and reusable.

Use the keyword def to define new functions.

def greet():
	print('Hello, World!')
	
greet()

Remember that Python executes instruction from top to bottom. You cannot reference variables or call functions before you define them. Organizing all your function definitions at the top of the file is a best practice.

shout() # produces a NameError; the function has not yet been defined

def shout():
	print('Yeeee HAAAAWWWWW!!!')

Functions can receive arguments and return values. The return keyword sends the value back to the function call where you can assign it to a variable.

If a function does not have a return statement, the return value is None.

def add_numbers(x,y):
	return x + y

def get_height():
	x = 10
	# this function doesn't return anything

height = get_height()
	
print(add_numbers(3,4))  # prints 7
print(type(height))      # prints <class 'NoneType'>

Give your functions descriptive names. In the code sample below, the only appropriate function name is check_email().

def a():
	pass
	
def my_function():
	pass

def check():
	pass

def check_email()  # the only good function name
	pass

Recursion

Recursion is a programming technique where a function is used to call itself. Certain problems can be solved more easily by using recursion. For example, the factorial of 5 is denoted as 5! and is calculated by multiplying n*(n-1) until n equals 1. In the case of 5!: 5 * 4 * 3 * 2 * 1 equals 120.

The Python code for a recursive function to calculate factorials would look something like:

def calculate_factorial(n):
	if n == 1:
		return 1
	else:
		return n * calculate_factorial(n-1)

print(calculate_factorial(5))

Be careful when creating recursion in your programs. It’s possible to create an infinite loop. Infinite loops are those that have no terminating statement where the function no longer calls itself. An if-statement is typically used to stop the recursion and return a base value.

In the example above, the if statement and return 1 returns a base value instead of calling the function again. In the example below, there is no terminating statement.

def create_infinite_loop(n):
	n += 1
	create_infinite_loop(n)  # creates a RecursionError

print(create_infinite_loop(1))

If a functions calls itself more than 1,000 times in a row, a RecursionError is raised in Python.

Parameters vs. Arguments

Functions can receive multiple arguments when they’re called. The order of arguments is important. These are called positional arguments and are set in the order they are received, except when you explicitly define them in your function call.

If you pass in more positional arguments than expected, Python throws an error.

def divide(x, y):
	return x/y

print(divide(4,2))      # prints 2.0
print(divide(2,4))      # prints 0.5
print(divide(x=4, y=2)) # prints 2.0
print(divide(2,4,8))    # throws an error; the function can only take 2 arguments

You can define default parameters in your function definition. However, they must be placed after non-default arguments or Python will throw an error.

def say_hi(greeting, name='buddy'):
	return greeting + ', ' + name + '!'
	
print(say_hi('Hi')) # prints Hi, buddy!

def say_hello(name='buddy', greeting):    # throws an error,
	return greeting + ', ' + name + '!'   # non-default argument follows 
										  # default argument
print(say_hello('Hi'))