Skip to content

Mental Model — Thinking in Python

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.”

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
// TypeScript: variable has a compile-time type
let count: number = 0;
count = "five"; // Error: Type 'string' is not assignable to type 'number'
function double(n: number): number {
return n * 2;
}
Python
# Python: variable is just a label
count = 0
count = "five" # perfectly legal at runtime
def double(n: int) -> int: # hints are documentation only
return n * 2
print(double(3)) # 6
print(double("x")) # TypeError at RUNTIME, not before

TypeScript compiles to JavaScript before running. Python is interpreted — you run source files directly with python3. There is no build step for development.

TypeScript
# TypeScript workflow
# tsc index.ts → compiles to index.js
# node index.js → runs the compiled output
# ts-node index.ts → shortcut for development
Python
# Python workflow
# python3 script.py → runs directly, no compile step
# python3 → opens the interactive REPL
# python3 -c "print(1)" → run a one-liner inline

Significant 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.

TypeScript
// TypeScript
function classify(n: number): string {
if (n > 0) {
return "positive";
} else if (n < 0) {
return "negative";
} else {
return "zero";
}
}
Python
# Python — indentation IS the block
def 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)) # positive
print(classify(-3)) # negative
print(classify(0)) # zero

A misindented line is a syntax error. Most Python editors enforce 4-space indentation automatically.

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
// TypeScript: multiple ways to filter
const 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
# Python: list comprehension is the Pythonic way
nums = [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))

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
// TypeScript: implicit this
class Counter {
private count: number = 0;
increment(): void {
this.count++; // 'this' is implicit
}
value(): number {
return this.count;
}
}
Python
# Python: explicit self
class 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 variables
x = 42
print(type(x).__name__) # int
x = "hello"
print(type(x).__name__) # str
# Significant indentation
def 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 comprehension
evens = [n for n in range(1, 11) if n % 2 == 0]
print("Evens:", evens)
# Explicit self
class 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())
In Python, where is the type of a value stored?
What opens a new indented block in Python?
Why must Python class methods declare `self` as their first parameter?
What is the Pythonic way to filter even numbers from a list?