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()) # 2# 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)…