Python for JS Devs · Fundamentals
Where Python quietly disagrees with JS — and the bugs that come from assuming it doesn't.
Python's variables feel familiar — dynamic typing, reassignable — but three things differ enough to bite you: declaration, equality, and truthiness. This lesson is about the deltas, not the sameness.
const maxRetries = 3;
let userName = "kd";
let result = null;MAX_RETRIES = 3 # UPPER = "constant" (convention only)
user_name = "kd" # snake_case, not camelCase
result = None # None ≈ nullconst/let/var. You just assign. There is no real block scope — a variable set inside an if is visible after it (scope is function-level, like var).snake_case for variables/functions, PascalCase for classes, UPPER_SNAKE for constants. Ruff will nag you.None — no null and undefined. Reading a name that was never assigned raises NameError, it doesn't give you undefined.== vs isPython's == compares values (roughly JS's ===, with no type coercion surprises). Python's is compares identity — same object in memory, like JS === on reference types.
a === b // value/ref, no coercion
a == b // coercing — avoid
x === nulla == b # value equality (use this)
a is b # same object identity
x is None # ALWAYS use `is` for None== for values, is only for None/True/False (singletons). Writing if x is 3: may work for small ints by luck and fail for others — never use is to compare values.
This is where JS instincts betray you. In JS, [] and {} are truthy. In Python, empty collections are falsy.
if ([]) { ... } // runs! [] is truthy
if ({}) { ... } // runs! {} is truthy
// falsy: 0 "" null undefined NaN falseif []: ... # skipped! empty list is falsy
if {}: ... # skipped! empty dict is falsy
# falsy: 0 0.0 "" [] {} () set() None Falseif items: — not if len(items) > 0:. And "did I get a value?" is if value is not None: when 0/"" are legal values you must not treat as missing.
7 / 2 # → 3.5 (true division, always float)
7 // 2 # → 3 (floor division)
7 % 2 # → 1 (modulo)
2 ** 10 # → 1024 (power, not Math.pow)
name = "kd"
f"hi {name.upper()}" # f-string; strings are immutable
type(name) # <class 'str'>
isinstance(name, str) # True — prefer this over type() ==
Before running, write down what each prints. Then paste into uv run python:
print(bool([]), bool([0]), bool(""), bool("0"))
x = None
print(x is None, x == None)
print(7 / 2, 7 // 2)
Any surprise (especially bool("0")) is a JS instinct getting corrected — exactly the point.
if []: do in Python?[] is truthy).None is a singleton — identity check with is is the idiom. null doesn't exist in Python.7 / 2 in Python 3?/ is true division and always returns a float. Use // for floor division.is "accidentally" works on integers? That's a great question — bring it.