Python from First PrinciplesPost 1 of 20 · 20 of 20 written

1. What Python actually is

Python · 1 of 202 min readOct 2026

Python is a language specification; CPython is the program that actually runs it. When people say “Python is slow” or “Python has a GIL”, they mean CPython, the reference implementation written in C. Other implementations exist (PyPy has a JIT compiler), but the AI ecosystem is built on CPython because of its C extension API.

What happens when you run a file

  1. Read the source text, decoded as UTF-8.
  2. Parse it into an abstract syntax tree (AST): the program’s structure as a tree of nodes.
  3. Compile the AST to bytecode: instructions for a virtual stack machine, cached in __pycache__/*.pyc.
  4. Execute: the interpreter loop runs bytecode one instruction at a time, creating and manipulating objects on the heap.

You can look at each stage yourself:

import ast, dis

print(ast.dump(ast.parse("y = x * 2 + 1"), indent=2))

def f(x):
    return x * 2 + 1

dis.dis(f)

The dis output looks roughly like this (opcode names vary by version):

LOAD_FAST    x
LOAD_CONST   2
BINARY_OP    *
LOAD_CONST   1
BINARY_OP    +
RETURN_VALUE

This is why pure Python is slow. BINARY_OP * does not know the types in advance: it inspects x, finds its type’s multiplication function, and calls it, every single time. Since 3.11 CPython specializes hot instructions (an int-only fast path, for example), but the model is the same.

Dynamic, strong, duck-typed

  • Dynamic: types belong to objects, not to variables. A name can point at an int, then at a string.
  • Strong: Python does not silently convert unrelated types. "3" + 4 raises TypeError, where JavaScript would give "34".
  • Duck typing: code cares about what an object can do, not what it is. Anything with __len__ and __getitem__ behaves like a sequence, which is exactly why PyTorch’s DataLoader accepts your own dataset class.

Everything is an object

Integers, functions, classes, modules and even types are objects. Each has a type, an identity and a value.

import math

def g(): pass

type(3)      # <class 'int'>
type(g)      # <class 'function'>
type(math)   # <class 'module'>
type(int)    # <class 'type'>
type(type)   # <class 'type'>  (types are objects too)

The practical consequence: you can pass functions as arguments, store classes in dicts and attach attributes to anything. A framework’s registry that maps "adamw" to an optimizer class is just a dict.

Compiled or interpreted? Both - and mostly neither

Python is compiled to bytecode, then interpreted. NumPy and PyTorch are mostly C, C++ and CUDA called from Python. In AI work, treat Python as the control plane: it decides what to compute, and compiled kernels do the computing.

Questions to ask:

  • Is this line running Python bytecode in a loop, or making one call into compiled code?
  • Which interpreter and version am I on? Check with import sys; print(sys.version, sys.executable).