Problem Formulation
Given a function definition in Python, starting with the keyword def
:
def f(x, y): p = x * y return p res = f(2, 3) print(res) # 6
How to know when a “def
” of a function ends? For example, in Java and C++, functions are enclosed with opening and closing parentheses {...}
, so the ending of a function is not ambiguous.
Ending a Function Syntactically
In Python, whitespace indentation carries meaning. The inner function body is indented compared to the environment it is defined in, per default by four empty spaces. Once the indentation level falls back to the level the function is defined, the function syntactically ends. Formally, the function definition ends when it encounters a non-empty line indented at most at the same level with the function definition. This non empty line is not part of that block.
Have a look at this example where we define three lines apart from the function definition—the first two lines are part of the function body, the third is not.
def f(): x = 1 # first line x = 2 # second line x = 3 # third line, not part of f()
So, if you run this code snippet, only the third line is executed as it’s not part of the not-executed function:
def f(): x = 1 # first line x = 2 # second line x = 3 # third line, not part of f() print(x) # 3
Theoretically, you can also write a one-liner function body right after the colon like so:
def f(): pass
If you need to run multiple expressions in a one-line function definition, you can do so with the semicolon. However, this is not recommended because it hurts readability:
def f(): pass; pass
Ending a Function Semantically
While the syntactical ending of a function definition is given by the indentation level, your program may leave the function early for multiple reasons:
- The
return
statement gives back the result of the function execution to the caller of the function. - The
yield
statement is used for generator functions that yield a series of dynamically-generated values. - After executing the last line of a function definition, Python implicitly adds a
return None
statement that will be automatically executed (see option 1). - Python may raise an error in which case the execution flow will leave the function body and the error is propagated to the caller and upwards until it is caught or the error terminates the program.
Here are four functions that will end prematurely due to those four reasons:
def f_1(): if 2 + 2 == 4: return True # Function will end here else: return False def f_2(): yield 2 # Function will end here print('hi') def f_3(): print('hi') print('python') # Function will end here def f_4(): x = 3 / 0 # Function will end here (error) print(x)
Here’s an example execution:
>>> f_1() True >>> for i in f_2(): print(i) 2 hi >>> f_3() hi python >>> f_4() Traceback (most recent call last): File "<pyshell#20>", line 1, in <module> f_4() File "C:\Users\xcent\Desktop\code.py", line 19, in f_4 x = 3 / 0 # Function will end here (error) ZeroDivisionError: division by zero
Note that the generator expression will still execute the second line in the function definition f_3()
when checking for the next value to yield in the second loop trial. In case you need a quick refresher on generator expression, feel free to check out our tutorial on the Finxter blog.