Python Inner Working

1. Python is an Interpreted, Bytecode-Compiled Language
When you write Python code:
print("Hello, World!")
It does not run directly as text.
Instead, Python goes through three major steps:
Parsing → Converts your
.py
file into an Abstract Syntax Tree (AST).Bytecode Compilation → The AST is compiled into bytecode (low-level, platform-independent instructions).
Execution by CPython’s Virtual Machine → The bytecode is executed by the Python Virtual Machine (PVM).
Example: View Python Bytecode
import dis
def add(a, b):
return a + b
dis.dis(add)
Output:
css 2 0 LOAD_FAST 0 (a)
2 LOAD_FAST 1 (b)
4 BINARY_ADD
6 RETURN_VALUE
Explanation:
LOAD_FAST
→ Loads local variables from the stackBINARY_ADD
→ Adds two valuesRETURN_VALUE
→ Returns result
📌 Why important?
Understanding bytecode helps in optimization — unnecessary operations = slower execution.
2. Python Execution Flow
When you run:
bash script.py
Here’s what happens under the hood (CPython version):
Lexing (Tokenizer) → Breaks source code into tokens:
bash print ( "Hello, World!" )
Parsing → Creates an AST (tree-like structure).
Compilation → Produces
.pyc
(bytecode) files in__pycache__
.PVM Execution → Runs bytecode inside the interpreter loop.
3. Python Memory Management
Python uses a private heap to store all objects, managed by the Python Memory Manager.
Key Points:
Reference Counting → Each object tracks how many variables point to it.
Garbage Collection → Removes unreachable objects.
Memory Pools → Objects of similar size are grouped for faster allocation.
Example: Reference Counting
import sys
x = [1, 2, 3]
print(sys.getrefcount(x)) # Count of references to x
y = x
print(sys.getrefcount(x)) # Increased by 1
del y
print(sys.getrefcount(x)) # Decreased
💡 Python deletes the object only when the refcount drops to 0.
4. The Global Interpreter Lock (GIL)
CPython allows only one thread to execute Python bytecode at a time.
GIL makes multi-threading not truly parallel for CPU-bound tasks.
Solution: Use
multiprocessing
for CPU-heavy work,threading
for I/O-bound work.
Example: GIL Limitation
import threading
def counter():
for _ in range(10_000_000):
pass
t1 = threading.Thread(target=counter)
t2 = threading.Thread(target=counter)
t1.start()
t2.start()
t1.join()
t2.join()
Even though you have two threads, due to GIL, they run one at a time.
5. Python Data Model (Dunder Methods)
Everything in Python is an object — numbers, functions, classes.
Example: Custom Behavior with Dunder Methods
pythonCopyEditclass Vector:
def __init__(self, x, y):
self.x, self.y = x, y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __repr__(self):
return f"Vector({self.x}, {self.y})"
v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2) # Vector(4, 6)
Here, +
works because we implemented __add__
.
6. Python Execution Speed & Optimizations
Interning small integers & strings for reuse.
Local variables are faster than globals (lookups are quicker in local stack).
List comprehensions are faster than loops for building lists.
Built-in functions are faster than custom Python loops (written in C).
Example: Local vs Global Lookup Speed
import timeit
x = 10
def global_access():
return x + 1
def local_access():
y = 10
return y + 1
print(timeit.timeit(global_access)) # Slower
print(timeit.timeit(local_access)) # Faster
7. Summary Mindmap
Code → Tokens → AST → Bytecode → PVM
Memory → Private heap, reference counting, garbage collector
Concurrency → GIL affects CPU-bound tasks
Everything is an object → Python data model
Optimizations → Interning, local variables, built-ins
Subscribe to my newsletter
Read articles from M Naeem BanGash directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
