Side-by-side, interactive cheatsheets for Python programmers
comparing Python to other languages. Every example runs live in your browser β no setup, no installation.
Choose your own path by reordering languages
Python's closest peer and philosophical foil. Both are readable, dynamic, and expressive, but Ruby's blocks, open classes, and "there's more than one way" ethos give you a richer object model β the language that gave the world Rails and proved convention-over-configuration could be beautiful.
doβ¦end syntax; more powerful than Python's single-expression lambdasnil all have methods; there's no primitive/object split:name vs "name" β a distinction Python's str type doesn't makePython's favorite upgrade path. When you've hit a performance wall or need concurrency without the GIL, Go delivers static types, native goroutines, and 10Γ throughput β with a learning curve much shorter than Rust or C++.
threading and asyncio with one unified modelThe language of the browser, JavaScript is the one runtime every Python web developer eventually meets on the front end.
[] and {} are truthy in JavaScript but falsy in Pythonnull and undefined β where Python has only None=== vs. ==: the strict equality you should reach for, and the coercing one you should avoidmap, filter, reduce) in place of list comprehensionsthis instead of an explicit selfasync/await with no explicit asyncio.run()The language CPython is written in. Every list, dict and str you use is a C struct with a refcount on the front β so C is less a foreign language than the floor underneath the one you already know.
& and * make explicit what a name binding already ismalloc/free instead of reference counting, with leaks and use-after-free as the pricelist, dict or str β a string is a pointer to bytes ending in zero, and strlen is O(n)errno instead of exceptions; nothing forces the caller to checkctypes actually do, including releasing the GIL for real parallelismRoughly what a Python developer wishes Java were β and then null becomes a type error. Inference, top-level functions, expressions instead of statements, data classes, and no ceremony to speak of. So this page is not about boilerplate: it is about what a type system buys once the boilerplate is gone.
String and String? are different types, and the compiler refuses the call your annotation only ever documenteddata class is @dataclass with copy(), destructuring and generated equality β and == is structural while === is identity, the reverse of Python's namingwhen over a sealed type is checked for exhaustiveness at compile time, which is the one thing match cannot doawait at the call site and no event loop to pass around, and structured concurrency cancels the children for youpip and an activated environmentPython's expressiveness, but with memory safety, concurrency without the GIL, and performance without a rewrite. Rust eliminates entire classes of bugs at compile time while keeping code readable.
Option and Result make None and errors explicit in the type system β no more AttributeError: 'NoneType' object has no attribute at runtimematch β more powerful than Python's match, and the compiler enforces you handle every caseA struct is a copy, and that is the whole first hour. Python has nothing like value semantics β every name is a reference, which is exactly why def f(items=[]) misbehaves. Add optionals as a type rather than a value that can turn up anywhere, and most of the surprise on this page is accounted for.
struct β and Array, Dictionary, Set, String β copies on assignment, so no aliasing, no defensive copy, and no mutable-default-argument bugString and String? are different types; if let, guard let and ?? are the ceremony that buys the guaranteesome against any is the distinction that trips people upweak and unowned are your jobasync/await is spelled the same and runs on a real thread pool: genuinely parallel, with actor isolation checked by the compiler"π¨βπ©βπ§".count is 1 where Python counts code points and says 5Python's familiar syntax meets compile-time type safety. TypeScript catches whole classes of bugs before they run β the same errors that only surface at runtime in Python.
Optional-equivalent T | null β the same expressiveness as Python's type hints, but verified by the compilerinterface and structural typing β like Python's Protocol, but built into the language and checked everywhere<T> β the same concept as Python's TypeVar, with cleaner syntax and broader tooling support`Hello, ${name}!` vs f"Hello, {name}!"The static language that meets you halfway. LINQ is the comprehension you already write, yield return is yield, and async/await is spelled exactly the same. What is genuinely new is that types are checked before the program exists, that a struct copies when you assign it, and that two threads really do run at once.
Where/Select/GroupBy/Aggregate, deferred exactly like a generator expression β but re-runnable, where a generator is spent after one passyield return compiles to the same kind of state machine yield does, so your generators translate almost line for linestruct, or reading one out of a list, hands you a copy β the correctness surprise that costs newcomers the most timevar means inferred, never anythingasync/await looks identical and promises more: no GIL, so Tasks and AsParallel() use every corestring? marks what may be null and the compiler warns when you forget to check β plus ?., ?? and ??= for the null dance@dataclass with value equality and with expressions built in, and switch expressions are a match the compiler checks for exhaustivenessEverything you defer to run time, decided before the program starts. A type hint is a comment Java turns into a contract: the wrong argument is a build failure rather than a TypeError at 3am, there is no top level to write a script in, and the compiler asks you to say what Python let you leave implicit β the element type, the exception, the interface.
List<String> changes what compiles, where list[str] changes nothing unless you separately run mypypublic static void main(String[] args)multiprocessing β and every piece of shared mutable state becomes yours to guardrecord is @dataclass(frozen=True) built into the language, and sealed interfaces plus switch patterns give match the exhaustiveness check it lacks== compares identity so strings need .equals, an int wraps silently at 2Β³ΒΉ where a Python integer just grows, a missing key returns null instead of raising KeyError, and there is no comprehension, no keyword argument and no REPL habitYou are almost certainly here because the fast part has to stop being Python. NumPy, PyTorch, Polars and pandas' new engine are C++ libraries with a thin Python face, so the common path is not "Python developer changes languages" but "Python developer has to touch the extension module". Nearly every surprise waiting for you comes from one sentence: in Python a variable is a name bound to an object, and in C++ the variable is the object.
second = first duplicates the whole container instead of aliasing it, and you ask for Python's behavior with a single &. Everything about lifetimes downstream follows from thiswith and finally β a destructor runs at the closing brace, even while an exception unwinds, so cleanup lives in the type instead of at every call sitestd::optional is your None as a type, std::expected (C++23) is the value-or-error return, and std::format borrowed Python's own mini-language, so {:.2f} means what you expecttyping.Protocol with the compiler actually enforcing itshared_ptr is CPython's reference count, made visible β the same mechanism you already live inside, opted into per object, with the same cycle problem and no cycle collector-7 / 2 is -3, not -4, and signed overflow is undefined behavior rather than a big numbergil_scoped_release is the one line that turns an extension from faster into parallelPython syntax you already know, plus the performance you've always wanted. Mojo extends Python's familiar syntax with strict typing, value semantics, and SIMD β letting you write Python-style code that compiles to metal.
def functions work exactly like Python's β mutable arguments, implicit typing, no ceremonyfn functions are strict and typed β declared argument mutability, explicit return types, zero runtime overheadvar declares a new variable; Mojo's type inference means you rarely need to spell the type outstruct types have value semantics β no garbage collector, no hidden allocations, fully predictable performancePython.import_module() β call any Python library directlyTwo dynamic languages that both grew optional typing, and only one of them means it. def greet(name: str) accepts 42 and fails later, somewhere else; function greet(string $name) throws at the call, and declare(strict_types=1) stops the engine coercing on the way in. This is the rare page where your own tooling is the looser of the two.
mypyarray does the work of list, dict and tuple β and it is copied on assignment, so the mutable-default-argument bug is unreachableuse (...) clause namesarray_map and array_filter with callbacks, in the opposite order from each other, and array_filter keeps the keysmatch, readonly, constructor promotion, named arguments and ?->; Composer is the part of the ecosystem a Python developer enviesThe scientific language that compiles to fast machine code, Julia gives Python developers NumPy-style array math and C-like speed without leaving a high-level, dynamic language.
1:3 is 1, 2, 3)sqrt.(values), numbers .* 2) instead of NumPy or loops^ for exponentiation, and div(a, b) for floor divisionend rather than indentationBool β no truthy/falsy valuesWhere Python's data-science world meets the language built by statisticians. R is vector-first and data-frame-native β much of what NumPy and pandas add to Python is simply how R already works.
42 is a length-1 vector and arithmetic is vectorized by default, so loops and comprehensions are rarely neededx[1] is the first element and 1:5 is 1, 2, 3, 4, 5<- for assignment and c() to build vectors, instead of = and list literalsdata.frame is part of base R β the original inspiration for pandas, available with no importNA for missing values, separate from NULL β first-class missingness rather than NaN/Noneapply family (sapply, lapply, vapply) in place of loops and method chainsEverything Python decides at run time, decided before the program starts. No None, no exceptions, no Any, no rebinding, no GIL and no garbage collector β and yet the parts you like survive: # comments, // floor division, match with guards and or-patterns, and interpolation that reads almost like an f-string.
None. A function that might not find something says so in its return type, as a tag union, and the compiler will not let the caller skip the missing case β so the AttributeError on NoneType has no way to happen.Try value naming what went wrong, and ? marks every point a function can exit early β the opposite of a bare int() that raises invisibly.Any, no cast() and no way to write "trust me" β and a match that forgets a case is a compile error rather than a silent None.KeyError in production. Keys that really are data still get Dict.Dec is an ordinary number type, so 0.1 + 0.2 really is 0.3 and you never reach for decimal.Decimal.Str.replace and no JSON. Read it for the ideas, not for next quarter's project.Everything Python gives you for free, taken away β and one thing Python can never give you, added. Forth has no named variables, no types, no heap and no string library, so the work Python hides is done in the open. In exchange, a program can add new syntax to its own compiler while it runs.
DUP, SWAP and OVER arrange the stack where Python would write a variable twiceTrue is β1, not 1, and AND is bitwise, so 1 2 AND is false even though both numbers are truthyDEFERIMMEDIATE word runs while the compiler runs, so UNLESS can be a real keyword rather than a function taking a lambda: SQUARE 4 0 DO 100 FORWARD 90 RIGHT LOOP ; draws on the page, the way Python's turtle module draws in a window