Same brain, new syntax.
You already know how to program — loops, functions, objects, async. This runs each idea through both languages side by side, with a real Python interpreter running in your browser so you can test things as you go.
1. Syntax speed-run
The surface-level differences you'll hit in the first five minutes.
let name = "Ada";
let age = 30;
if (age >= 18) {
console.log(`${name} is an adult`);
}
name = "Ada"
age = 30
if age >= 18:
print(f"{name} is an adult")
| Concept | JavaScript | Python |
|---|---|---|
| Blocks | { } | indentation |
| String interpolation | `${x}` | f"{x}" |
| Null value | null / undefined | None |
| Equality | === vs == | == (is checks identity) |
| Comments | // and /* */ | # only |
[], {}, set()) are falsy in Python — they aren't in JS.Quick check
2. Data structures & comprehensions
Where Python's style diverges most from chained array methods.
const nums = [1,2,3,4,5]; const squares = nums .filter(n => n % 2 === 0) .map(n => n * n);
nums = [1, 2, 3, 4, 5] squares = [n * n for n in nums if n % 2 == 0]
| Concept | JavaScript | Python |
|---|---|---|
| Array | Array | list |
| Object / Map | Object, Map | dict |
| Destructure | [a,b] = arr | a, b = tup |
| Immutable list | Object.freeze() | tuple (real immutability) |
Quick check
.filter().map()?Generators & iterators
Lazy sequences — same idea as a JS generator function, but used far more pervasively in idiomatic Python (file reading, pagination, pipelines).
function* chunks(arr, size) {
for (let i = 0; i < arr.length; i += size) {
yield arr.slice(i, i + size);
}
}
def chunks(items, size):
for i in range(0, len(items), size):
yield items[i:i + size]
3. Functions
Default params, rest args, and lambdas — mostly familiar, with a few gotchas.
function greet(name, greeting = "Hi") {
return `${greeting}, ${name}!`;
}
const shout = (...words) => words.join(" ").toUpperCase();
def greet(name, greeting="Hi"):
return f"{greeting}, {name}!"
def shout(*words):
return " ".join(words).upper()
def f(x=[])) are created once and reused across every call — a classic Python trap with no JS equivalent.Quick check
...args is:Decorators
No direct JS equivalent — closest cousin is a higher-order function wrapping another function, but Python gives it dedicated syntax.
function withTimer(fn) {
return (...args) => {
const start = Date.now();
const result = fn(...args);
console.log(`${fn.name} took ${Date.now()-start}ms`);
return result;
};
}
import time
def timer(fn):
def wrapper(*args, **kwargs):
start = time.time()
result = fn(*args, **kwargs)
print(f"{fn.__name__} took {time.time()-start:.4f}s")
return result
return wrapper
@timer
def slow_task():
total = sum(range(10**6))
return total
Context managers
Roughly like using in C#; JS has nothing native (closest is a manual try/finally).
let f;
try {
f = openFile("data.txt");
process(f);
} finally {
f?.close();
}
with open("data.txt") as f:
contents = f.read()
# file is guaranteed closed here, even on exception
with block — write your own with contextlib.contextmanager or a class implementing __enter__/__exit__.4. Classes & typing
Structurally similar to JS/TS classes, with an explicit self and optional, unenforced type hints.
class Circle {
constructor(private radius: number) {}
area(): number {
return Math.PI * this.radius ** 2;
}
}
class Circle:
def __init__(self, radius: float):
self.radius = radius
def area(self) -> float:
return 3.14159 * self.radius ** 2
| Concept | JS / TS | Python |
|---|---|---|
| Instance ref | this (implicit) | self (explicit param) |
| Inheritance | extends, single-parent | class B(A):, multiple allowed |
| Type checking | tsc — enforced at compile time | mypy — advisory only, not enforced at runtime |
Quick check
Structural typing with Protocol
TS types by shape ("structural typing"). Python's default is nominal (based on class hierarchy) — Protocol restores shape-based typing.
interface HasArea {
area(): number;
}
function printArea(shape: HasArea) {
console.log(shape.area());
}
// any object with .area() matches — no inheritance needed
from typing import Protocol
class HasArea(Protocol):
def area(self) -> float: ...
def print_area(shape: HasArea) -> None:
print(shape.area())
# any object with an .area() method satisfies this
5. The async model
Syntactically close to JS. Conceptually, this is the biggest gap between the two languages.
async function main() {
const [a, b] = await Promise.all([
fetch(url1), fetch(url2)
]);
}
main(); // event loop already running
import asyncio
async def main():
a, b = await asyncio.gather(
fetch(url1), fetch(url2)
)
asyncio.run(main()) # you start the loop yourself
asyncio for I/O,
threading for blocking I/O libraries, and multiprocessing for CPU-bound work —
because Python's Global Interpreter Lock (GIL) stops threads from running Python code in true parallel.
There's no JS equivalent to that split.
| Concept | JavaScript | Python |
|---|---|---|
| Await many at once | Promise.all() | asyncio.gather() |
| Start the loop | automatic | asyncio.run(main()) |
| CPU parallelism | Worker threads (separate heaps) | multiprocessing (GIL blocks real thread parallelism) |
6. Errors, strings & files
The everyday idioms that don't fit neatly into "syntax" or "OOP" but come up constantly.
Error handling
try {
risky();
} catch (e) {
console.log("failed:", e.message);
} finally {
cleanup();
}
try:
risky()
except ValueError as e:
print("failed:", e)
else:
print("succeeded, no exception")
finally:
cleanup()
else clause runs only if no exception was raised — JS's try/catch/finally has no equivalent slot. Idiomatic Python also catches specific exception types (ValueError, KeyError) rather than checking a generic error's message string.Strings & regex
| Concept | JavaScript | Python |
|---|---|---|
| Regex | /pattern/g literal | re.findall(pattern, text) — always a string, no literal syntax |
| Number formatting | x.toFixed(2) | f"{x:.2f}" — format spec lives in the f-string |
| Pad string | str.padStart() | str.rjust() / zfill() |
Files & paths
| Concept | JS/Node | Python |
|---|---|---|
| Join paths | path.join(a, b) | Path(a) / b — paths are objects, not strings |
| Check existence | fs.existsSync() | Path.exists() |
Standard library tour
Python's stdlib is noticeably richer than JS's — idiomatic Python reaches for it before reaching for a package, unlike npm culture.
Quick check
try/except/else block run its else clause?7. Data validation
TS types disappear at runtime — and so do Python type hints, by default. Pydantic is the practical Python analog to a zod schema: validation that actually runs.
import { z } from "zod";
const User = z.object({
name: z.string(),
age: z.number(),
active: z.boolean().default(true),
});
const user = User.parse(requestBody);
// throws if shape is wrong
from pydantic import BaseModel
class User(BaseModel):
name: str
age: int
active: bool = True
user = User(**request_json)
# raises a clear validation error if shape is wrong
What's next
This covers the syntax and mental-model gaps. To go further:
- Set up
uvorvenv+pip, andruff+pytestfor linting and testing. - Build something real: a small CLI with
argparse/click, or a tiny FastAPI service if you know Express. - Go deeper on decorators, context managers (
with), and multiple inheritance — the three Python features with no direct JS analog.