In this Article, We will learn what is disassembler in python. A disassembler for Python bytecode is like a translator. When you write Python code, before it gets run, it’s changed into a special language that the computer understands, called “bytecode”. If you want to see and understand this special language, the disassembler helps you translate it back into something closer to regular Python code. It’s like turning computer talk back into human talk!
dis() function
the dis()
function in Python stands for “disassemble.” It’s a tool to look under the hood of Python code and see a more low-level representation of what’s happening. More precisely, it shows the Python bytecode, which is the intermediate step between your high-level Python code and the machine code that the computer understands.
Think of Python bytecode as a set of instructions that the Python interpreter uses to execute your code. By examining this bytecode, we can better understand how Python translates our human-readable code into steps it can follow.
Let’s use a simple example to illustrate this:
Suppose you have this function:
def add(a, b):
return a + b
If you use the dis()
function from the dis
module to inspect the add
function, you’ll get a disassembled view of its bytecode.
import dis
dis.dis(add)
The output might look something like this:
# Output of above code
1 0 RESUME 0
2 2 LOAD_FAST 0 (a)
4 LOAD_FAST 1 (b)
6 BINARY_OP 0 (+)
10 RETURN_VALUE
Let’s break down the output:
- RESUME is A no-op. Performs internal tracing, debugging and optimization checks.
LOAD_FAST 0 (a)
andLOAD_FAST 1 (b)
are loading the values ofa
andb
respectively onto a stack.BINARY_ADD
pops two values from the stack, adds them, and pushes the result back onto the stack.RETURN_VALUE
returns the value on top of the stack.
So, in simple terms, the bytecode is showing the step-by-step instructions for the Python interpreter to execute the add
function.
Using dis.dis()
, you can dissect any Python code to understand how it’s being executed at the bytecode level!
Bytecode()
dis.Bytecode
provides an iterable representation of the bytecode operations for the given source, and each iteration yields a named tuple with fields such as opcode (operation code), arg (argument for the operation), and more.
Let’s use a basic function as our code:
import dis
def hello(name):
return "Hello, " + name
bytecode = dis.Bytecode(hello)
for instruction in bytecode:
print(instruction)
This will produce an output similar to:
# Ouput
Instruction(opname='RESUME', opcode=151, arg=0, argval=0, argrepr='', offset=0, starts_line=3, is_jump_target=False, positions=Positions(lineno=3, end_lineno=3, col_offset=0, end_col_offset=0))
Instruction(opname='LOAD_CONST', opcode=100, arg=1, argval='Hello, ', argrepr="'Hello, '", offset=2, starts_line=4, is_jump_target=False, positions=Positions(lineno=4, end_lineno=4, col_offset=11, end_col_offset=20))
Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='name', argrepr='name', offset=4, starts_line=None, is_jump_target=False, positions=Positions(lineno=4, end_lineno=4, col_offset=23, end_col_offset=27))
Instruction(opname='BINARY_OP', opcode=122, arg=0, argval=0, argrepr='+', offset=6, starts_line=None, is_jump_target=False, positions=Positions(lineno=4, end_lineno=4, col_offset=11, end_col_offset=27))
Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=10, starts_line=None, is_jump_target=False, positions=Positions(lineno=4, end_lineno=4, col_offset=4, end_col_offset=27))
show_code()
The show_code()
function in the dis
(disassembler) module of Python displays a summary of important details about a compiled Python object (like a function). It doesn’t show you the bytecode instructions like dis()
does, but instead, it provides metadata about the compiled code, such as the number of local variables, constants used, and more.
Let’s use a basic function as our demonstration:
import dis
def demo_function(a, b):
message = "Hello"
return message + " " + a + b
# To see the metadata of this function using show_code(), you would do:
dis.show_code(demo_function)
The output might look something like this:
Name: demo_function
Filename: <ipython-input-3-ec5dd3916158>
Argument count: 2
Positional-only arguments: 0
Kw-only arguments: 0
Number of locals: 3
Stack size: 2
Flags: OPTIMIZED, NEWLOCALS
Constants:
0: None
1: 'Hello'
2: ' '
Variable names:
0: a
1: b
2: message
Explanation:
Name
: The name of the function.Filename
: Where the function is defined (often a file path, but in environments like IPython/Jupyter, it might be a special reference).Argument count
: Number of arguments the function accepts.Positional-only arguments
: Number of arguments that are only positional.Kw-only arguments
: Number of arguments that are only keyword.Number of locals
: Total local variables in the function.Stack size
: Maximum stack depth required.Flags
: Specific flags set for the compiled object.OPTIMIZED
indicates that the function uses fast locals.NEWLOCALS
indicates that a new dictionary for local variables should be created.NOFREE
indicates that there are no free or cell variables.
Constants
: The constants used in the function.Variable names
: Local variable names.
This function gives you a nice summary of the compiled Python object without diving into the actual bytecode instructions. It’s useful for quickly understanding the structure and components of a function or any compiled code object.
Conclusion:
Diving into the intricacies of Python, it’s evident that every high-level code we write undergoes a transformation, becoming a set of machine-friendly instructions known as “bytecode”. The dis
module in Python provides us with powerful tools like dis()
, Bytecode()
, and show_code()
to unravel and inspect this hidden layer. These tools not only offer insights into how our beloved Python language communicates with computers but also bridge the gap between high-level programming and the intricate dance of bits and bytes. For any Python enthusiast, understanding bytecode and the utilities to dissect it offers a richer perspective on the language’s efficiency and operation. Whether you’re debugging, optimizing, or just satisfying curiosity, the disassembler is your magnifying glass into the world of Python’s bytecode.