Hi Pythonistas!,
In the previous post, we covered the basics of Python’s bytecode and how to use the dis module. Now, we’ll take it a step further and see how conditionals (if-else statements) and loops (for loops) are compiled into bytecode.
Disassembling an if-else Statement
Let’s start by disassembling a simple function that uses an if-else statement:
import dis
def check_even(x):
if x % 2 == 0:
return "Even"
else:
return "Odd"
dis.dis(check_even)
Output:
Here’s a breakdown of the bytecode:
LOAD_FAST 0 (x): Load the value of x onto the stack.
LOAD_CONST 1 (2): Load the constant 2 onto the stack.
BINARY_MODULO: Perform the modulo operation (x % 2).
LOAD_CONST 2 (0): Load the constant 0 onto the stack.
COMPARE_OP 2 (==): Compare if x % 2 equals 0.
POP_JUMP_IF_FALSE 18: If the comparison is false, jump to instruction 18 (the else block).
LOAD_CONST 3 ('Even'): Load the string "Even" for the if block.
RETURN_VALUE: Return "Even" if the condition was true.
LOAD_CONST 4 ('Odd'): Load the string "Odd" for the else block.
RETURN_VALUE: Return "Odd" if the condition was false.
Disassembling a Loop
Next, let’s look at how Python disassembles a simple for loop:
code
import dis
def loop_example():
for i in range(3):
print(i)
dis.dis(loop_example)
Output:
4 0 LOAD_GLOBAL 0 (range)
2 LOAD_CONST 1 (3)
4 CALL_FUNCTION 1
6 GET_ITER
>> 8 FOR_ITER 6 (to 22)
10 STORE_FAST 0 (i)
5 12 LOAD_GLOBAL 1 (print)
14 LOAD_FAST 0 (i)
16 CALL_FUNCTION 1
18 POP_TOP
20 JUMP_ABSOLUTE 4 (to 8)
4 >> 22 LOAD_CONST 0 (None)
24 RETURN_VALUE
Here’s how the bytecode works:
LOAD_GLOBAL 0 (range): Load the range() function.
LOAD_CONST 1 (3): Load the constant 3.
CALL_FUNCTION 1: Call the range(3) function.
GET_ITER: Get an iterator from the result of range(3).
FOR_ITER 12: Iterate over the items in the iterator, and if the iterator is exhausted, jump to instruction 22.
STORE_FAST 0 (i): Store the current value of i from the loop.
LOAD_GLOBAL 1 (print): Load the print() function.
LOAD_FAST 0 (i): Load the current value of i onto the stack.
CALL_FUNCTION 1: Call print(i).
POP_TOP: Remove the result of print() from the stack.
JUMP_ABSOLUTE 8: Jump back to instruction 8 to continue the loop.
LOAD_CONST 0 (None): Once the loop is done, load None.
RETURN_VALUE: Return None.
In the next post, we’ll explore how Python handles functions in more detail.