Functions#

Midjourney: Functional Lines, ref. C.E.B. Reas

A function of good software is to make the complex appear simple.

— Grady Booch

Slides/PDF#

Function Definition and Function Call#

All higher-level programming languages allow the definition of functions (or procedures) to avoid having to write repetitive code and to structure complex programs.

Functions in Python are defined with the keyword def. They have a function name and can take multiple arguments as input in parentheses. The function declaration is terminated with :. The body of the function, i.e., the part that should be executed when the function is called, must always be indented.

def function_name(argument1, argument2):
    # Function body
    print("Der Datentyp von Argument 1 ist "+str(type(argument1)))
    print("Der Datentyp von Argument 2 ist "+str(type(argument2)))

All parts of the function must be indented to the same level. The following code is, for instance, syntactically incorrect.

def function_name(argument1, argument2):
    # Function body
    print("Der Datentyp von Argument 1 ist "+str(type(argument1)))
    print("Der Datentyp von Argument 2 ist "+str(type(argument2)))

When calling the function with funktionsname(wert1, wert2), the arguments must be provided with input values. The arguments in Python are dynamically typed, like other variables (So we don’t necessarily know which data type they will have later. This is a common source of errors).

function_name("wert1", 2)
Der Datentyp von Argument 1 ist <class 'str'>
Der Datentyp von Argument 2 ist <class 'int'>

The crucial thing is that the arguments can change their values.

function_name("anderer wert ", "noch ein anderer")
Der Datentyp von Argument 1 ist <class 'str'>
Der Datentyp von Argument 2 ist <class 'str'>

Return of Results#

The return statement is used in functions to terminate the execution of a function and return the assigned value(s) as the function’s result.

def function_with_one_output():
    return "Ausgabewert"
    print("Dieser Teil wird nicht ausgeführt")
    return "Anderer Ausgabewert"
result = function_with_one_output()
result
'Ausgabewert'

We can see that the result is Ausgabewert and the print() function has not been called.

Multiple return values can be returned using return.

def function_with_two_outputs():
    return "Ausgabewert1", "Ausgabewert2"
result = function_with_two_outputs()
result
('Ausgabewert1', 'Ausgabewert2')

The result of functions that return multiple values is a tuple.

type(result)
tuple

You can access the individual values in a tuple by their index. For reference, the index of a list or tuple starts at 0.

result[0]
'Ausgabewert1'

The tuple can, however, also be prevented by multiple assignment. In a multiple assignment, you list several variables separated by commas on the left-hand side of the assignment.

result1, result2 = function_with_two_outputs()
print(result1)
print(result2)
print(type(result1))
print(type(result2))
Ausgabewert1
Ausgabewert2
<class 'str'>
<class 'str'>

Variable Validity#

Variables inside functions are not globally valid. The variables for the arguments are valid only within the function. New variables defined inside the function are also not valid outside the function. This is how it works in the following function:

def my_function(argument):
    internal_variable = "geheim"
    print("Der Wert von argument innerhalb der Funktion ist "+str(argument))
    print("Der Wert von internal_variable innerhalb der Funktion ist "+str(internal_variable))

The values of argument and intern are defined inside the function and are printed by the print() statement.

my_function("argument_wert")
Der Wert von argument innerhalb der Funktion ist argument_wert
Der Wert von internal_variable innerhalb der Funktion ist geheim

However, the variables argument and interne_variable are not globally available after the function has been executed.

print(argument)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[14], line 1
----> 1 print(argument)

NameError: name 'argument' is not defined
print(internal_variable)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[15], line 1
----> 1 print(internal_variable)

NameError: name 'internal_variable' is not defined

It also allows defining variable names outside the function that can have a different value inside the function. This keeps the value of argument unchanged.

argument = 6  # original value
print("Der Wert von argument vor der Funktion ist "+str(argument))
my_function(4)
print("Der Wert von argument nach der Funktion ist immer noch "+str(argument))
Der Wert von argument vor der Funktion ist 6
Der Wert von argument innerhalb der Funktion ist 4
Der Wert von internal_variable innerhalb der Funktion ist geheim
Der Wert von argument nach der Funktion ist immer noch 6

Modified and Unmodified Arguments#

As a rule, modifications to primitive-type arguments inside a function are not carried over (pass-by-value). Thus, inside the function you can also assign new values to the arguments.

def my_function(argument):
    print("Der Wert von argument am Anfang der Funktion ist "+str(argument))
    argument = 3
    print("Der Wert von argument am Ende der Funktion ist "+str(argument))

argument = 6  # original value
print("Der Wert von argument vor der Funktion ist "+str(argument))
my_function(argument)
print("Der Wert von argument nach der Funktion ist immer noch "+str(argument))
Der Wert von argument vor der Funktion ist 6
Der Wert von argument am Anfang der Funktion ist 6
Der Wert von argument am Ende der Funktion ist 3
Der Wert von argument nach der Funktion ist immer noch 6

This also works with complex data types, such as list, set, and dict, as long as they are completely reassigned.

def my_function(argument):
    print("Der Wert von argument am Anfang der Funktion ist "+str(argument))
    argument = [3]
    print("Der Wert von argument am Ende der Funktion ist "+str(argument))

argument = [6] # original value
print("Der Wert von argument vor der Funktion ist "+str(argument))
my_function(argument)
print("Der Wert von argument nach der Funktion ist immer noch "+str(argument))
Der Wert von argument vor der Funktion ist [6]
Der Wert von argument am Anfang der Funktion ist [6]
Der Wert von argument am Ende der Funktion ist [3]
Der Wert von argument nach der Funktion ist immer noch [6]

However, if they are modified only by pass-by-reference, these changes are global as well.

def my_function(argument):
    print("Der Wert von argument am Anfang der Funktion ist "+str(argument))
    argument.append(3)  # We append 3 to the list
    print("Der Wert von argument am Ende der Funktion ist "+str(argument))

argument = [6]  # original value
print("Der Wert von argument vor der Funktion ist "+str(argument))
my_function(argument)
print("Der Wert von argument nach der Funktion ist auf einmal "+str(argument))
Der Wert von argument vor der Funktion ist [6]
Der Wert von argument am Anfang der Funktion ist [6]
Der Wert von argument am Ende der Funktion ist [6, 3]
Der Wert von argument nach der Funktion ist auf einmal [6, 3]
⚠️ Warning: This can quickly lead to errors if you unintentionally modify global data structures.

Quiz#

shuffleQuestions: true shuffleAnswers: true ### What are functions used for in Python? - [x] To structure reusable code - [x] To make programs clearer - [ ] To make the code faster - [ ] To create databases ### How does a function definition begin in Python? - [x] With the keyword `def` - [ ] With `function` - [ ] With `define()` - [ ] With `func` ### What happens if the function body is not properly indented? - [x] There is a syntax error - [ ] The function is ignored - [ ] The function is stored as a global function - [ ] Python automatically indents the code ### What does `return` do in a function? - [x] Ends the function and returns a value - [ ] Automatically prints all arguments - [ ] Repeats the function - [ ] Skips the rest of the code ### What happens when multiple values are returned with `return`? - [x] A tuple is returned - [ ] Only the first value is returned - [ ] An error occurs - [ ] All values are automatically printed ### Where are variables valid that were defined inside a function? - [x] Only inside the function - [ ] In the entire program - [ ] Inside loops - [ ] In the entire module ### What happens to a variable `x` that is defined inside a function? - [x] It exists only during the function execution - [ ] It automatically overwrites global variables - [ ] It becomes a global constant - [ ] It stays in memory until the program ends ### What happens to primitive data types when they are passed to a function and modified there? - [x] The change remains local to the function - [ ] The original variable is always overwritten - [ ] Python stores the change automatically globally - [ ] The function automatically returns a copy ### What happens if you modify a list in a function **modified**, but do not reassign it? - [x] The change also affects outside the function - [ ] The list is automatically copied - [ ] The function creates a new list - [ ] An error occurs ### How does a list behave if it is **reassigned** in a function? - [x] The global list remains unchanged - [ ] The global list is overwritten - [ ] It cannot be used inside a function - [ ] Python blocks this assignment ### How does a Python program generally execute? - [x] Line by line, from top to bottom - [ ] Randomly, depending on the interpreter - [ ] Only at the end, then backwards - [ ] From functions to the main program ### What happens if a function is defined but not called? - [x] The code in the function is skipped - [ ] The function is executed automatically anyway - [ ] The code is only run on the next restart - [ ] Python shows a warning ### What happens on function call? - [x] The function code is executed at this point - [ ] Only the return value is computed - [ ] Only the last line of the function is executed - [ ] Python pauses the main program ### What is a recursive function? - [x] A function that calls itself - [ ] A function that may be called only once - [ ] A function without parameters - [ ] A special looping function in Python ### What error is in this example? ```python def rekursive_funktion(): rekursive_funktion() ``` - [x] It lacks a termination condition, causing an infinite loop - [ ] The function name is invalid - [ ] Python does not allow self-calls - [ ] The function must have a parameter ### Sort the following lines to correctly implement a simple recursive function: ```python n = 3 ``` 1. `def count_down(n):` 2. ` if n <= 0:` 3. ` print("Fertig!")` 4. ` else:` 5. ` print(n)` 6. ` count_down(n - 1)`