Seven layers between your Python code and reality. Click each layer to see the code at that level of abstraction.
This is the beautiful abstraction Python gives you. One function call. One string. A child could read it and understand what it does. But Python doesn't execute this directly. Your computer doesn't understand English, or Python, or anything resembling human language.
print("Hello, World!")
This one line is a request. Everything below is what actually happens when you make it.
Before Python can do anything, it reads your text and builds a tree representing the structure of your code. Every keyword, every function, every value becomes a node. This is like diagramming a sentence in grade school. Subject, verb, object. Except here it is function, argument, value.
Module(body=[ Expr(value= Call( func=Name(id='print'), args=[ Constant(value='Hello, World!') ], keywords=[] ) ) ])
You can see this yourself in Python by running import ast; print(ast.dump(ast.parse('print("Hello, World!")'))). The tree is how Python understands structure. But it still cannot run anything.
Python compiles your code into bytecode: simple instructions for Python's virtual machine. Bytecode is like sheet music. It tells you what notes to play, but it is not sound. Someone still has to perform it.
1 0 PUSH_NULL 2 LOAD_NAME 0 (print) 4 LOAD_CONST 0 ('Hello, World!') 6 CALL 1 8 POP_TOP 10 RETURN_CONST 1 (None)
Try it: import dis; dis.dis('print("Hello, World!")'). Load the function. Load the string. Call it. That is the entire program in bytecode. But these instructions need an interpreter to execute them.
Python itself is written in C. When you run Python, you are running a C program that reads bytecode instructions in a loop and dispatches to C functions. That CALL instruction triggers something like this:
PyObject * PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs) { ternaryfunc call; call = Py_TYPE(callable)->tp_call; if (call == NULL) { /* Error: object is not callable */ PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable", Py_TYPE(callable)->tp_name); return NULL; } return (*call)(callable, args, kwargs); }
Notice the error handling. if (call == NULL). What happens if you try to call something that is not a function? The code catches it. Every layer has error handling. Every layer has rules.
The C compiler transforms that code into assembly. Assembly maps almost directly to what the CPU can do. Each line is one simple operation: move data, add numbers, jump to another location. It is like giving directions one step at a time.
PyObject_Call: push rbp ; Save base pointer mov rbp, rsp ; Set up stack frame sub rsp, 32 ; Reserve 32 bytes mov [rbp-24], rdi ; Store callable mov [rbp-16], rsi ; Store args mov [rbp-8], rdx ; Store kwargs ; ... continues for hundreds of instructions
These instructions operate on registers, tiny pieces of memory inside the CPU itself. We are close to the metal now.
Assembly becomes machine code. Raw bytes. This is what the CPU actually reads. The number 55 means "push rbp." The bytes 48 89 e5 mean "mov rbp, rsp." This is as low as software goes.
55 ; push rbp 48 89 e5 ; mov rbp, rsp 48 83 ec 20 ; sub rsp, 32 48 89 7d e8 ; mov [rbp-24], rdi 48 89 75 f0 ; mov [rbp-16], rsi 48 89 55 f8 ; mov [rbp-8], rdx e8 xx xx xx xx ; call function
Each byte is a specific instruction. Each instruction triggers transistors to switch. From here, we leave the world of software entirely.
The CPU decodes those bytes and executes them. Each instruction triggers a cascade of events. A single mov instruction involves:
1. Instruction Fetch Read bytes from memory into CPU 2. Decode Identify opcode, determine operands 3. Execute Read source register (64 flip-flops) Route data through internal bus 4. Memory Access Calculate address Check L1 cache ~4 cycles Check L2 cache ~12 cycles Check L3 cache ~40 cycles Access main RAM ~200 cycles 5. Write Back Store result, update flags
Each of these involves transistors switching states. Millions of transistors. And each transistor works by controlling the flow of electrons. Electrons that are, at a fundamental level, probabilistic.
That is seven layers. Twelve thousand lines of code. Billions of transistor state changes. And we have not even talked about the history yet.
Next: History →