Mental Model — Thinking in Python
The biggest shift: the compiler is gone
Section titled “The biggest shift: the compiler is gone”In TypeScript, the compiler (tsc) is your first line of defence. It catches type mismatches before any code runs. When you move to Python, that safety net disappears at runtime — the interpreter does not check types. Optional type hints exist (and you should use them), but enforcement requires a separate tool like mypy or pyright.
Mental model: TypeScript says “prove it is safe before running.” Python says “run it, and if you pass the wrong type, you will get a runtime error.”
Dynamic typing
Section titled “Dynamic typing”Python variables are labels that can point to any object. The type of the value lives on the object itself, not on the variable name. This is fundamentally different from TypeScript where the variable has a type at compile time.
// TypeScript: variable has a compile-time typelet count: number = 0;count = "five"; // Error: Type 'string' is not assignable to type 'number'
function double(n: number): number { return n * 2;}# Python: variable is just a labelcount = 0count = "five" # perfectly legal at runtime
def double(n: int) -> int: # hints are documentation only return n * 2
print(double(3)) # 6print(double("x")) # TypeError at RUNTIME, not beforeThe interpreter and the REPL
Section titled “The interpreter and the REPL”TypeScript compiles to JavaScript before running. Python is interpreted — you run source files directly with python3. There is no build step for development.
# TypeScript workflow# tsc index.ts → compiles to index.js# node index.js → runs the compiled output# ts-node index.ts → shortcut for development# Python workflow# python3 script.py → runs directly, no compile step# python3 → opens the interactive REPL# python3 -c "print(1)" → run a one-liner inlineSignificant indentation — no curly braces
Section titled “Significant indentation — no curly braces”This is the change TypeScript developers notice first. Python uses indentation (4 spaces by convention) to delimit blocks. There are no { and }. A colon (:) always opens a new block.
// TypeScriptfunction classify(n: number): string { if (n > 0) { return "positive"; } else if (n < 0) { return "negative"; } else { return "zero"; }}# Python — indentation IS the blockdef classify(n: int) -> str: if n > 0: return "positive" elif n < 0: # note: elif, not else if return "negative" else: return "zero"
print(classify(5)) # positiveprint(classify(-3)) # negativeprint(classify(0)) # zeroA misindented line is a syntax error. Most Python editors enforce 4-space indentation automatically.
One obvious way — “Pythonic” code
Section titled “One obvious way — “Pythonic” code”TypeScript is flexible: you can use classes, functional composition, prototype chains, or plain objects interchangeably. Python encourages a single idiomatic style. The community has a name for it: Pythonic. Pythonic code is concise, readable, and uses language features as intended.
// TypeScript: multiple ways to filterconst evens1 = nums.filter(n => n % 2 === 0);const evens2 = nums.reduce((acc, n) => n % 2 === 0 ? [...acc, n] : acc, []);// Both work; TS community accepts both# Python: list comprehension is the Pythonic waynums = [1, 2, 3, 4, 5, 6]evens = [n for n in nums if n % 2 == 0]print(evens) # [2, 4, 6]
# Works but less Pythonic:# evens = list(filter(lambda n: n % 2 == 0, nums))Explicit self in methods
Section titled “Explicit self in methods”TypeScript class methods implicitly receive this. Python class methods require an explicit self parameter as the first argument — the interpreter fills it in when you call the method, but you must declare it.
// TypeScript: implicit thisclass Counter { private count: number = 0;
increment(): void { this.count++; // 'this' is implicit }
value(): number { return this.count; }}# Python: explicit selfclass Counter: def __init__(self): # called on Counter() self.count = 0 # instance attribute
def increment(self): # self is the instance self.count += 1
def value(self) -> int: return self.count
c = Counter()c.increment()c.increment()print(c.value()) # 2Annotations are lazy now (Python 3.14)
Section titled “Annotations are lazy now (Python 3.14)”TypeScript types are erased: tsc deletes them and the JavaScript that runs has no idea they existed. Python keeps its annotations as real objects — which used to mean they were evaluated eagerly, at import time, so a type that did not exist yet had to be quoted as a string.
Python 3.14 (PEP 649) changed that: annotations are stored as a lazily evaluated function and only computed when something actually asks for them.
// TypeScript — types are erased before runtimeinterface Node { parent: Node | null; } // self-reference is free
function walk(n: Node): void { /* ... */ }// Compiled JS has no trace of Node at all# Python 3.14 — annotations are evaluated on demandclass Node: parent: Node | None # no quotes needed — not evaluated yet def add(self, child: Node) -> Node: # forward reference just works return child
# Read them when you actually want them:from annotationlib import get_annotationsprint(get_annotations(Node.add))Two practical consequences: you can drop the quotes around forward references, and from __future__ import annotations is no longer something you add to every file. Libraries that inspect annotations at runtime — Pydantic, FastAPI, dataclasses — read them through annotationlib instead of poking at __annotations__ directly.
# Mental model: types live on objects, not variablesx = 42print(type(x).__name__) # int
x = "hello"print(type(x).__name__) # str
# Significant indentationdef classify(n: int) -> str: if n > 0: return "positive" elif n < 0: return "negative" else: return "zero"
for num in [5, -3, 0]: print(f"{num} is {classify(num)}")
# Pythonic list comprehensionevens = [n for n in range(1, 11) if n % 2 == 0]print("Evens:", evens)
# Explicit selfclass Counter: def __init__(self): self.count = 0
def increment(self): self.count += 1
def value(self) -> int: return self.count
c = Counter()c.increment()c.increment()print("Counter:", c.value())Loading Python runtime (first run only)…