Python for people who already think in JavaScript

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.

JAVASCRIPT
let name = "Ada";
let age = 30;

if (age >= 18) {
  console.log(`${name} is an adult`);
}
PYTHON
name = "Ada"
age = 30

if age >= 18:
    print(f"{name} is an adult")
ConceptJavaScriptPython
Blocks{ }indentation
String interpolation`${x}`f"{x}"
Null valuenull / undefinedNone
Equality=== vs ==== (is checks identity)
Comments// and /* */# only
Watch out: empty containers ([], {}, set()) are falsy in Python — they aren't in JS.
TRY IT — edit and run
loading interpreter…

      

Quick check

1. What defines a code block in Python?
2. Which of these is falsy in Python but truthy in JS?

2. Data structures & comprehensions

Where Python's style diverges most from chained array methods.

JAVASCRIPT
const nums = [1,2,3,4,5];
const squares = nums
  .filter(n => n % 2 === 0)
  .map(n => n * n);
PYTHON
nums = [1, 2, 3, 4, 5]
squares = [n * n for n in nums if n % 2 == 0]
ConceptJavaScriptPython
ArrayArraylist
Object / MapObject, Mapdict
Destructure[a,b] = arra, b = tup
Immutable listObject.freeze()tuple (real immutability)
TRY IT — build a dict comprehension
loading interpreter…

      

Quick check

1. What is the closest Python equivalent to a chained .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).

JAVASCRIPT
function* chunks(arr, size) {
  for (let i = 0; i < arr.length; i += size) {
    yield arr.slice(i, i + size);
  }
}
PYTHON
def chunks(items, size):
    for i in range(0, len(items), size):
        yield items[i:i + size]
TRY IT — a lazy chunking generator
loading interpreter…

      

3. Functions

Default params, rest args, and lambdas — mostly familiar, with a few gotchas.

JAVASCRIPT
function greet(name, greeting = "Hi") {
  return `${greeting}, ${name}!`;
}

const shout = (...words) => words.join(" ").toUpperCase();
PYTHON
def greet(name, greeting="Hi"):
    return f"{greeting}, {name}!"

def shout(*words):
    return " ".join(words).upper()
Gotcha: mutable default arguments (like def f(x=[])) are created once and reused across every call — a classic Python trap with no JS equivalent.
TRY IT
loading interpreter…

      

Quick check

1. Python's version of JS's rest parameter ...args is:

Decorators

No direct JS equivalent — closest cousin is a higher-order function wrapping another function, but Python gives it dedicated syntax.

JAVASCRIPT (closest analog)
function withTimer(fn) {
  return (...args) => {
    const start = Date.now();
    const result = fn(...args);
    console.log(`${fn.name} took ${Date.now()-start}ms`);
    return result;
  };
}
PYTHON
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
TRY IT — a custom @timer decorator
loading interpreter…

      

Context managers

Roughly like using in C#; JS has nothing native (closest is a manual try/finally).

JAVASCRIPT
let f;
try {
  f = openFile("data.txt");
  process(f);
} finally {
  f?.close();
}
PYTHON
with open("data.txt") as f:
    contents = f.read()
# file is guaranteed closed here, even on exception
Anything that needs guaranteed setup/teardown (files, connections, timers) is a candidate for a 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.

TYPESCRIPT
class Circle {
  constructor(private radius: number) {}
  area(): number {
    return Math.PI * this.radius ** 2;
  }
}
PYTHON
class Circle:
    def __init__(self, radius: float):
        self.radius = radius

    def area(self) -> float:
        return 3.14159 * self.radius ** 2
ConceptJS / TSPython
Instance refthis (implicit)self (explicit param)
Inheritanceextends, single-parentclass B(A):, multiple allowed
Type checkingtsc — enforced at compile timemypy — advisory only, not enforced at runtime
TRY IT
loading interpreter…

      

Quick check

1. If you pass the wrong type to a type-hinted Python function, what happens?

Structural typing with Protocol

TS types by shape ("structural typing"). Python's default is nominal (based on class hierarchy) — Protocol restores shape-based typing.

TYPESCRIPT
interface HasArea {
  area(): number;
}

function printArea(shape: HasArea) {
  console.log(shape.area());
}
// any object with .area() matches — no inheritance needed
PYTHON
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.

JAVASCRIPT
async function main() {
  const [a, b] = await Promise.all([
    fetch(url1), fetch(url2)
  ]);
}
main(); // event loop already running
PYTHON
import asyncio

async def main():
    a, b = await asyncio.gather(
        fetch(url1), fetch(url2)
    )

asyncio.run(main())  # you start the loop yourself
JS has exactly one concurrency model — the event loop is always running. Python has three, chosen based on the bottleneck: 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.
ConceptJavaScriptPython
Await many at oncePromise.all()asyncio.gather()
Start the loopautomaticasyncio.run(main())
CPU parallelismWorker 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

JAVASCRIPT
try {
  risky();
} catch (e) {
  console.log("failed:", e.message);
} finally {
  cleanup();
}
PYTHON
try:
    risky()
except ValueError as e:
    print("failed:", e)
else:
    print("succeeded, no exception")
finally:
    cleanup()
Python's 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

ConceptJavaScriptPython
Regex/pattern/g literalre.findall(pattern, text) — always a string, no literal syntax
Number formattingx.toFixed(2)f"{x:.2f}" — format spec lives in the f-string
Pad stringstr.padStart()str.rjust() / zfill()

Files & paths

ConceptJS/NodePython
Join pathspath.join(a, b)Path(a) / b — paths are objects, not strings
Check existencefs.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.

TRY IT — Counter, defaultdict, datetime
loading interpreter…

      

Quick check

1. When does Python's 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.

TYPESCRIPT + ZOD
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
PYTHON + PYDANTIC
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
Pydantic underpins most of the modern Python API/AI ecosystem — FastAPI request/response models, and the tool-call schemas used by the OpenAI and Anthropic SDKs, are all Pydantic models under the hood.

What's next

This covers the syntax and mental-model gaps. To go further:

  • Set up uv or venv + pip, and ruff + pytest for 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.