Python Functions
Functions are used to encapsulate a set of relatred instructions that you want to use within your program to carry out a specific task. This helps with the organization of your code as well as code reusability. Oftent times functions accept parameters and return values, but that’s not always the case.
There are three types of functions in Python:
- Built-in functions, such as help() to ask for help, min() to get the minimum value, print() to print an object to the terminal,… You can find an overview with more of these functions here.
- User-Defined Functions (UDFs), which are functions that users create to help them out; And
- Anonymous functions, which are also called lambda functions because they are not declared with the standard def keyword.
Functions vs. Methods
A method is a function that’s part of a class which we’ll discuss in another lecture. Keep in mind all methods are functions but not all functions are methods.
Parameters vs. Arguments
Parameters are the names used when defining a function or a method, and into which arguments will be mapped. In other words, arguments are the things which are supplied to any function or method call, while the function or method code refers to the arguments by their parameter names.
Consider the function
def sum(a, b):
return a + b
sum
has 2 parameters a and b. If you call the sum
function with values 2 and 3 then 2 and 3 are the arguments.
Defining User Functions
The four steps to defining a function in Python are the following:
- Use the keyword
def
to declare the function and follow this up with the function name. - Add parameters to the function: they should be within the parentheses of the function. End your line with a colon.
- Add statements that the functions should execute.
- End your function with a return statement if the function should output something. Without the return statement, your function will return an object None.
# takes no parameters, returns none
def say_hello():
print('hello')
say_hello()
hello
x = say_hello()
print(type(x), x)
hello
<class 'NoneType'> None
# takes parameters, returns none
def say_something(message):
print(message)
x = say_something('hello class')
print(type(x), x)
hello class
<class 'NoneType'> None
The return Statement
Sometimes it’s useful to reuturn values from functions. We’ll refactor our code to return values.
def get_message():
return 'hello class'
def say_something():
message = get_message()
print(message)
x = get_message()
print(type(x), x)
<class 'str'> hello class
say_something()
hello class
def ask_user_to_say_something():
message = input('say something')
print(message)
ask_user_to_say_something()
say something welcome to class
welcome to class
def say_anything(fn):
message = fn()
print(message)
fn = get_message
say_anything(fn)
hello class
fn = input
say_anything(fn)
python
python
print(type(say_something()))
hello class
<class 'NoneType'>
x = say_something()
print(type(x), x)
hello class
<class 'NoneType'> None
returning multiple values
In python you can return values in a variety of data types including primitive data structures such as integers, floats, strings, & booleans as well as non-primitive data structures such as arrays, lists, tuples, dictionaries, sets, and files.
# returning a list
def get_messages():
return ['hello class', 'python is great', 'here we\'re retuning a list']
messages = get_messages()
print(type(messages), messages)
for message in messages:
print(type(message), message)
<class 'list'> ['hello class', 'python is great', "here we're retuning a list"]
<class 'str'> hello class
<class 'str'> python is great
<class 'str'> here we're retuning a list
# returning a tuple... more on tuples later
def get_message():
return ('hello class', 3)
message = get_message()
print(type(message), message)
for i in range(0, message[1]):
print(message[0])
<class 'tuple'> ('hello class', 3)
hello class
hello class
hello class
def get_message():
return 'hello class', 3 # ('s are optional
message = get_message()
print(type(message), message)
for i in range(0, message[1]):
print(message[0])
<class 'tuple'> ('hello class', 3)
hello class
hello class
hello class
message, iterations = get_message()
print(type(message), message)
for i in range(0, iterations):
print(message)
<class 'str'> hello class
hello class
hello class
hello class
Function Arguments in Python
There are four types of arguments that Python functions can take:
- Default arguments
- Required arguments
- Keyword arguments
- Variable number of arguments
Default Arguments
Default arguments are those that take a default value if no argument value is passed during the function call. You can assign this default value by with the assignment operator =, just like in the following example:
import random
def get_random_numbers(n=1):
if n == 1:
return random.random()
elif n > 1:
numbers = []
for i in range(0, n):
numbers.append(random.random())
return numbers
w = get_random_numbers()
print('w:', type(w), w)
x = get_random_numbers(1)
print('x:', type(x), x)
y = get_random_numbers(n=3)
print('y:', type(y), y)
z = get_random_numbers(n=-1)
print('z:', type(z), z)
w: <class 'float'> 0.9910565700695637
x: <class 'float'> 0.9558550589791632
y: <class 'list'> [0.42626170162229604, 0.5088767993123806, 0.8323423336075707]
z: <class 'NoneType'> None
# note : this might be a better implementation
def get_random_numbers(n=1):
if n == 1:
return [random.random()]
elif n > 1:
numbers = []
for i in range(0, n):
numbers.append(random.random())
return numbers
else:
return []
w = get_random_numbers()
print('w:', type(w), w)
x = get_random_numbers(1)
print('x:', type(x), x)
y = get_random_numbers(n=3)
print('y:', type(y), y)
z = get_random_numbers(n=-1)
print('z:', type(z), z)
w: <class 'list'> [0.1019992349979667]
x: <class 'list'> [0.773928378946725]
y: <class 'list'> [0.6769995705574616, 0.5620051030258161, 0.6386635457018367]
z: <class 'list'> []
Required Arguments
Required arguments are mandatory and you will generate an error if they’re not present. Required arguments must be passed in precisely the right order, just like in the following example:
def say_something(message, number_of_times):
for i in range(0, number_of_times):
print(message)
# arguments passed in the proper order
say_something('hello', 3)
hello
hello
hello
# arguments passed incorrectly
say_something(3, 'hello')
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/var/folders/jd/pq0swyt521jb2424d6fvth840000gn/T/ipykernel_91274/301183919.py in <module>
1 # arguments passed incorrectly
----> 2 say_something(3, 'hello')
/var/folders/jd/pq0swyt521jb2424d6fvth840000gn/T/ipykernel_91274/2017276285.py in say_something(message, number_of_times)
1 def say_something(message, number_of_times):
----> 2 for i in range(0, number_of_times):
3 print(message)
4
5 # arguments passed in the proper order
TypeError: 'str' object cannot be interpreted as an integer
Keyword Arguments
You can use keyword arguments to make sure that you call all the parameters in the right order. You can do so by specifying their parameter name in the function call.
say_something(message='hello', number_of_times=3)
say_something(number_of_times=3, message='hello')
hello
hello
hello
hello
hello
hello
Variable Number of Arguments
In cases where you don’t know the exact number of arguments that you want to pass to a function, you can use the following syntax with *args:
def add(*x):
print(type(x), x)
total = 0
for i in x:
total += i
return total
total = add(1)
print(total)
total = add(1, 1)
print(total)
total = add(1, 2, 3, 4, 5)
print(total)
<class 'tuple'> (1,)
1
<class 'tuple'> (1, 1)
2
<class 'tuple'> (1, 2, 3, 4, 5)
15
The asterisk *
is placed before the variable name that holds the values of all nonkeyword variable arguments. Note here that you might as well have passed *varint
, *var_int_args
or any other name to the plus()
function.
# You can spedify any combination of required, keyword, and variable arguments.
def add(a, b, *args):
total = a + b
for arg in args:
total += arg
return total
total = add(1, 1)
print(total)
total = add(1, 1, 2)
print(total)
total = add(1, 2, 3, 4, 5)
print(total)
2
4
15
Global vs Local Variables
In general, variables that are defined inside a function body have a local scope, and those defined outside have a global scope. That means that local variables are defined within a function block and can only be accessed inside that function, while global variables can be obtained by all functions that might be in your script:
# global variable
score = 0
def player_hit():
global score
hit_points = -10 # local variable
score += hit_points
def enemy_hit():
global score
hit_points = 5 # local variable
score += hit_points
enemy_hit()
enemy_hit()
enemy_hit()
enemy_hit()
player_hit()
enemy_hit()
player_hit()
print(score)
5
Anonymous Functions in Python
Anonymous functions are also called lambda functions in Python because instead of declaring them with the standard def keyword, you use the lambda
keyword.
def double(x):
return x * 2
y = double(3)
print(y)
6
d = lambda x: x * 2
y = d(3)
print(y)
6
sdlfjsdk = lambda x, n: x if n < 5 else 0
result = sdlfjsdk(4, 6)
print(result)
0
a = lambda x: x ** 2 if x < 0 else x
print(a(-1))
print(a(-2))
print(a(3))
1
4
3
add = lambda x, y: x + y
x = add(2, 3)
print(x)
5
You use anonymous functions when you require a nameless function for a short period of time, and that is created at runtime. Specific contexts in which this would be relevant is when you’re working with filter()
, map()
and reduce()
:
filter()
function filters the original input list on the basis of a criterion > 10.map()
applies a function to all items of the listreduce()
is part of the functools library. You use this function cumulatively to the items of the my_list list, from left to right and reduce the sequence to a single value.
from functools import reduce
my_list = [1,2,3,4,5,6,7,8,9,10]
# Use lambda function with `filter()`
filtered_list = list(filter(lambda x: (x * 2 > 10), my_list))
# Use lambda function with `map()`
mapped_list = list(map(lambda x: x * 2, my_list))
# Use lambda function with `reduce()`
reduced_list = reduce(lambda x, y: x + y, my_list)
print(filtered_list)
print(mapped_list)
print(reduced_list)
[6, 7, 8, 9, 10]
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
55
Using main() as a Function
You can easily define a main() function and call it just like you have done with all of the other functions above:
# Define `main()` function
def main():
print("This is a main function")
main()
This is a main function
However, as it stands now, the code of your main()
function will be called when you import it as a module. To make sure that this doesn’t happen, you call the main()
function when __name__ == '__main__'
.
# Define `main()` function
def start_here():
print("This is a main function")
# Execute `main()` function
if __name__ == '__main__':
start_here()
This is a main function