Understanding Python Function Execution with the dis Module

Posted by Afsal on 04-Oct-2024

Hi Pythonistas!,

In the previous posts, we looked at how Python compiles conditionals and loops into bytecode. Now, let’s dive into function calls and see how Python manages function execution with bytecode.

Disassembling a Function with Arguments

Let’s start with a simple example of a function that takes arguments and returns a value

import dis

def multiply(a, b):
    return a * b

dis.dis(multiply)

Output

  2       0 LOAD_FAST            0 (a)

           2 LOAD_FAST            1 (b)

           4 BINARY_MULTIPLY

           6 RETURN_VALUE

Here’s what the bytecode instructions mean:

LOAD_FAST 0 (a): Load the local variable a onto the stack.

LOAD_FAST 1 (b): Load the local variable b onto the stack.

BINARY_MULTIPLY: Multiply a and b.

RETURN_VALUE: Return the result of a * b.

Disassembling a Function with a Default Argument

Next, let’s examine a function with default arguments:

code

def power(x, exp=2):
    return x ** exp

dis.dis(power)

Output

  2       0 LOAD_FAST            0 (x)

           2 LOAD_FAST            1 (exp)

           4 BINARY_POWER

           6 RETURN_VALUE

In this case, Python disassembles the function in the same way as before, but it internally handles the default argument (exp=2) when the function is called with fewer arguments than required.

In the upcoming post we will learn more about nested functions