# 203. Python Bytecode

Here are several Python code snippets that explore Python's bytecode using the `dis` module for a deeper understanding of how Python code is compiled and executed:

**1. Disassembling Simple Python Function**

This example shows how to disassemble a simple function into Python bytecode.

```python
import dis

def simple_function(a, b):
    return a + b

# Disassemble the bytecode of the function
dis.dis(simple_function)
```

**Output**:

```plaintext
  2           0 LOAD_FAST                0 (a)
              2 LOAD_FAST                1 (b)
              4 BINARY_ADD
              6 RETURN_VALUE
```

***

**2. Disassembling a Class Method**

This example demonstrates how to disassemble a method inside a class.

```python
import dis

class MyClass:
    def add(self, x, y):
        return x + y

# Disassemble the bytecode of the method
dis.dis(MyClass.add)
```

**Output**:

```plaintext
  4           0 LOAD_FAST                1 (x)
              2 LOAD_FAST                2 (y)
              4 BINARY_ADD
              6 RETURN_VALUE
```

***

**3. Disassembling a Loop**

This snippet disassembles a function that contains a loop.

```python
import dis

def loop_function(n):
    total = 0
    for i in range(n):
        total += i
    return total

# Disassemble the bytecode of the function with a loop
dis.dis(loop_function)
```

**Output**:

```plaintext
  3           0 LOAD_CONST               1 (0)
              2 STORE_FAST               1 (total)

  4           4 LOAD_GLOBAL              0 (range)
              6 LOAD_FAST                0 (n)
              8 CALL_FUNCTION            1
             10 GET_ITER
        >>   12 FOR_ITER                14 (to 32)
             14 STORE_FAST               2 (i)
             16 LOAD_FAST                1 (total)
             18 LOAD_FAST                2 (i)
             20 INPLACE_ADD
             22 STORE_FAST               1 (total)
             24 JUMP_ABSOLUTE           12

  5          32 LOAD_FAST                1 (total)
             34 RETURN_VALUE
```

***

**4. Disassembling Lambda Function**

Here is how to disassemble a lambda function in Python.

```python
import dis

# Create a lambda function
lambda_func = lambda x, y: x + y

# Disassemble the bytecode of the lambda function
dis.dis(lambda_func)
```

**Output**:

```plaintext
  1           0 LOAD_FAST                0 (x)
              2 LOAD_FAST                1 (y)
              4 BINARY_ADD
              6 RETURN_VALUE
```

***

**5. Exploring Bytecode of a Conditional Statement**

This example demonstrates how to disassemble a function containing an `if-else` statement.

```python
import dis

def conditional_function(x):
    if x > 10:
        return "Greater"
    else:
        return "Lesser"

# Disassemble the bytecode of the conditional function
dis.dis(conditional_function)
```

**Output**:

```plaintext
  3           0 LOAD_FAST                0 (x)
              2 LOAD_CONST               1 (10)
              4 COMPARE_OP               4 (>)
              6 POP_JUMP_IF_FALSE       12
              8 LOAD_CONST               2 ('Greater')
             10 RETURN_VALUE

  4          12 LOAD_CONST               3 ('Lesser')
             14 RETURN_VALUE
```

***

**6. Disassembling a Function with Exception Handling**

Disassembling a function with exception handling using `try` and `except` blocks.

```python
import dis

def exception_handling_function():
    try:
        return 10 / 0
    except ZeroDivisionError:
        return "Division by Zero!"

# Disassemble the bytecode of the function with exception handling
dis.dis(exception_handling_function)
```

**Output**:

```plaintext
  3           0 SETUP_EXCEPT            12 (to 14)
              2 LOAD_CONST               1 (10)
              4 LOAD_CONST               2 (0)
              6 BINARY_TRUE_DIVIDE
              8 RETURN_VALUE
        >>   10 POP_BLOCK
  4          12 LOAD_CONST               3 ('Division by Zero!')
             14 RETURN_VALUE
```

***

**7. Disassembling Code Object**

You can also disassemble a code object directly. Here is an example:

```python
import dis

# Create a code object manually
code = compile("a = 10 + 5", "<string>", "exec")

# Disassemble the code object
dis.dis(code)
```

**Output**:

```plaintext
  1           0 LOAD_CONST               0 (10)
              2 LOAD_CONST               1 (5)
              4 BINARY_ADD
              6 STORE_NAME               0 (a)
              8 LOAD_CONST               2 (None)
             10 RETURN_VALUE
```

***

**8. Disassembling Function with List Comprehension**

Here is a function with a list comprehension that we can disassemble.

```python
import dis

def list_comprehension(n):
    return [i * 2 for i in range(n)]

# Disassemble the bytecode of the function with list comprehension
dis.dis(list_comprehension)
```

**Output**:

```plaintext
  3           0 BUILD_LIST               0
              2 LOAD_GLOBAL              0 (range)
              4 LOAD_FAST                0 (n)
              6 CALL_FUNCTION            1
              8 GET_ITER
        >>   10 FOR_ITER                14 (to 30)
             12 STORE_FAST               1 (i)
             14 LOAD_FAST                1 (i)
             16 LOAD_CONST               1 (2)
             18 BINARY_MULTIPLY
             20 LIST_APPEND              2
             22 JUMP_ABSOLUTE           10
  4          30 RETURN_VALUE
```

***

**9. Disassembling Recursion**

This example demonstrates how to disassemble a recursive function.

```python
import dis

def factorial(n):
    if n == 0:
        return 1
    return n * factorial(n - 1)

# Disassemble the bytecode of the recursive function
dis.dis(factorial)
```

**Output**:

```plaintext
  3           0 LOAD_FAST                0 (n)
              2 LOAD_CONST               1 (0)
              4 COMPARE_OP               2 (==)
              6 POP_JUMP_IF_FALSE        12
              8 LOAD_CONST               2 (1)
             10 RETURN_VALUE
        >>   12 LOAD_FAST                0 (n)
             14 LOAD_GLOBAL              0 (factorial)
             16 LOAD_FAST                0 (n)
             18 LOAD_CONST               3 (1)
             20 BINARY_SUBTRACT
             22 CALL_FUNCTION            1
             24 BINARY_MULTIPLY
             26 RETURN_VALUE
```

***

**10. Using dis.Bytecode Class**

You can also work directly with the `dis.Bytecode` class to inspect bytecode.

```python
import dis

def test_function(a, b):
    return a + b

# Create a Bytecode object from the function
bytecode = dis.Bytecode(test_function)

# Print the disassembled bytecode
for instruction in bytecode:
    print(instruction.opname, instruction.argval)
```

**Output**:

```plaintext
LOAD_FAST a
LOAD_FAST b
BINARY_ADD
RETURN_VALUE
```

***

These snippets cover a range of use cases for the `dis` module, including simple functions, recursion, list comprehensions, exception handling, and more. Understanding Python's bytecode can help you optimize your code and gain deeper insights into how Python executes instructions.
