Functions help extract out common code blocks.
Let's define a function print_greeting().
def print_greeting():
    print("Hi there, how are you?")
    print("Long time no see.")
And call it:
print_greeting()
That's a bit impersonal.
def print_greeting(name):
    print("Hi there, {0}, how are you?".format(name))
    print("Long time no see.")
print_greeting("Andreas")
But we might not know their name.
(And we just changed the interface of print_greeting!)
def print_greeting(name="my friend"):
    print("Hi there, {0}, how are you?".format(name))
    print("Long time no see.")
print_greeting("Andreas")
print_greeting()
Function parameters work like variables.
So what does this do?
def my_func(my_list):
    my_list.append(5)
    
l = [1,2,3]
my_func(l)
print(l)
Can be very surprising!
Define a better function my_func_2:
def my_func_2(my_list):
    return my_list + [5]
l = [1,2,3]
l2 = my_func_2(l)
print(l)
print(l2)