Variables, functions, lists, and conditionals — the fundamentals of Python.
Python variables need no declaration keyword — just assign a value: name = "Ada". Unlike JavaScript's let/const, there's no way to lock a Python variable from reassignment at the language level (though naming a constant ALL_CAPS is a common convention signaling "please don't reassign this").
Every value has a type, though Python infers it rather than requiring you to state it: str (text), int (whole numbers), float (decimals), bool (True/False, capitalized unlike JS), and None (Python's version of "no value", equivalent to JS's null). Collections — list, tuple, dict, set — are covered in their own sections.
The type() function returns a value's type, handy for debugging: type(42) is <class 'int'>. Python variable names conventionally use snake_case (count_of_items), not JavaScript's camelCase (countOfItems).
name = "Ada"
age = 30
age = "thirty" # legal -- Python is dynamically typedtype(42) # <class 'int'>
type("hi") # <class 'str'>
type(True) # <class 'bool'>Arithmetic operators (+ - * /) work as expected, plus two Python-specific ones: // (floor division, rounds down to the nearest whole number) and ** (exponentiation, 2 ** 3 is 8). Regular division / always returns a float in Python 3, even 4 / 2 gives 2.0.
Comparison operators (== != > < >= <=) compare values directly — Python has no separate strict-equality operator like JavaScript's ===, because == doesn't silently convert types the way JavaScript's does: "5" == 5 is False in Python, already the "safe" behavior by default.
Logical operators are spelled-out words, not symbols: and, or, and not instead of &&, ||, and !. Like JavaScript, and and or short-circuit — they stop evaluating as soon as the result is known.
7 / 2 # 3.5
7 // 2 # 3
2 ** 3 # 8"5" == 5 # False -- different types, no conversionA function is defined with def, and everything indented underneath its colon is the function body — Python uses indentation instead of curly braces to mark blocks, so consistent indentation isn't just style, it's required syntax.
Parameters can have default values (def greet(name="friend")), just like JavaScript. *args collects any number of extra positional arguments into a tuple, and **kwargs collects extra keyword arguments into a dict — together they're Python's equivalent of JavaScript's rest parameter.
A function with no return statement returns None automatically, same as JavaScript implicitly returning undefined. return immediately exits the function, same as JS.
def greet(name="friend"):
return f"Hello, {name}!"def total(*nums):
return sum(nums)
total(1, 2, 3) # 6A list is an ordered, mutable collection, created with square brackets: fruits = ["apple", "banana"]. Items are accessed by zero-based index — fruits[0] is "apple" — and len(fruits) gives the current item count (Python's equivalent of JavaScript's .length property, but as a function, not a property).
Slicing pulls out a sub-list with fruits[start:end] — the start index is included, the end index is not, matching JavaScript's .slice(). Negative indexes count from the end: fruits[-1] is the last item.
append() adds an item to the end (like JS's push), and pop() removes and returns the last item — both mutate the list in place, the same trade-off as JavaScript's mutating array methods.
fruits = ["apple", "banana"]
fruits.append("cherry")
len(fruits) # 3fruits[0:2] # ["apple", "banana"]
fruits[-1] # "cherry"A list comprehension builds a new list in one line: [n * 2 for n in nums] reads almost like English — "for each n in nums, put n * 2 in the new list." It's Python's equivalent of JavaScript's .map(), condensed into its own syntax rather than a method call.
Add a condition at the end to filter while building: [n for n in nums if n > 2] only keeps items where the condition is true — no separate .filter() call needed, both steps happen in the same expression.
Comprehensions aren't limited to lists — the same syntax with {} instead of [] builds a dict or set comprehension. For truly complex transformation logic, a regular for loop with .append() is sometimes clearer; comprehensions shine for straightforward transform-and-filter cases.
nums = [1, 2, 3, 4, 5]
even_doubled = [n * 2 for n in nums if n % 2 == 0]
# [4, 8]squares = {n: n * n for n in range(4)}
# {0: 0, 1: 1, 2: 4, 3: 9}A dictionary groups related data as key-value pairs: user = {"name": "Ada", "age": 30} — Python's equivalent of a JavaScript object, though dictionary keys are far more commonly written as explicit strings in quotes rather than bare identifiers.
Values are read with square-bracket key access: user["name"] — Python has no dot-notation shortcut like JavaScript's user.name for dictionaries specifically (dot notation is reserved for actual object attributes and methods).
.keys(), .values(), and .items() return the dictionary's keys, values, or (key, value) pairs respectively — .items() is what you loop over to get both at once, Python's equivalent of JavaScript's Object.entries().
user = {"name": "Ada", "age": 30}
for key, value in user.items():
print(key, value)user.get("email", "no email set")if, elif, and else branch on a condition, the same three-way structure as JavaScript's if/else if/else — just spelled elif instead of "else if", and using a colon + indentation instead of curly braces.
Python has its own set of "falsy" values that act as False in a condition: 0, 0.0, "" (empty string), [] (empty list), {} (empty dict), and None. Every other value is truthy — including the string "False", which is a common beginner trap since it's a non-empty string.
A conditional expression (Python's ternary) reads as value_if_true if condition else value_if_false — the condition sits in the middle, unlike JavaScript's condition ? a : b.
if age >= 18:
status = "adult"
elif age >= 13:
status = "teen"
else:
status = "child"label = "adult" if age >= 18 else "minor"Python's for loop iterates directly over an iterable's items — for fruit in fruits: — rather than JavaScript's classic index-counting for (let i = 0; ...) loop. Use range(n) when you specifically need a sequence of numbers: for i in range(5) counts 0 through 4.
enumerate() gives you both the index and the value while looping, when you need both: for i, fruit in enumerate(fruits) — Python's cleaner alternative to manually tracking an index counter.
while loops work like JavaScript's — looping as long as a condition holds. break exits a loop immediately and continue skips to the next iteration, both spelled the same as in JavaScript.
for fruit in fruits:
print(fruit)
for i, fruit in enumerate(fruits):
print(i, fruit)for i in range(5):
print(i) # 0 1 2 3 4Strings can use single or double quotes interchangeably in Python — unlike JavaScript, where single/double quotes are equivalent but backticks specifically enable template literals. Python's equivalent of a template literal is an f-string: prefix the string with f and embed expressions in curly braces.
f-strings (f"Hello, {name}!") evaluate any expression inside the braces, not just variable names — f"{price * 1.1:.2f}" formats a computed value to two decimal places in the same breath.
Common string methods: .upper()/.lower() change case, .strip() removes surrounding whitespace, .split(",") breaks a string into a list, and ",".join(list) does the reverse — gluing a list of strings back together with a separator.
name = "Ada"
print(f"Hello, {name}!")"a,b,c".split(",") # ["a", "b", "c"]
"-".join(["a", "b"]) # "a-b"A tuple is an ordered, immutable collection — written with parentheses (though the parentheses are often optional): point = (3, 4). "Immutable" means once created, a tuple's items can't be reassigned, unlike a list.
Unpacking assigns multiple variables from a tuple or list in one line: x, y = point — Python's equivalent of JavaScript's array destructuring (const [x, y] = point), and it works the same way for swapping two variables without a temp: a, b = b, a.
A * before a name during unpacking collects any remaining items into a list: first, *rest = [1, 2, 3, 4] gives first = 1 and rest = [2, 3, 4] — the same idea as JavaScript's rest-in-destructuring ([first, ...rest]).
point = (3, 4)
x, y = point
print(x, y) # 3 4a, b = 1, 2
a, b = b, a
print(a, b) # 2 1try/except lets you run code that might fail without crashing the whole program — Python's equivalent of JavaScript's try/catch, just spelled except instead of catch. Code that raises inside the try block jumps straight to except, where you get the exception object.
raise ValueError("message") creates and immediately raises an exception — Python's equivalent of JavaScript's throw new Error("message"). Python has many specific built-in exception types (ValueError, TypeError, KeyError, and more) rather than one generic Error, so except can target a specific kind of failure.
finally runs a block of cleanup code whether or not an exception occurred, same as JavaScript — commonly used to close a file or release a resource no matter how the try block turned out.
try:
int("not a number")
except ValueError as err:
print(err)if age < 0:
raise ValueError("age cannot be negative")A variable created inside a function is local to that function — it doesn't exist outside it, the same rule as JavaScript. Python looks up a name using the LEGB rule: Local scope first, then Enclosing (an outer function), then Global (module-level), then Built-in.
Unlike JavaScript, assigning to a variable inside a function always creates a new local variable by default — even if a global variable shares the same name, the function can't modify it without an explicit global keyword declaring the intent first.
A closure is a function that remembers variables from the scope it was created in, even after that outer function has finished running — the same concept as JavaScript closures, just without needing let/const block-scoping to make it work cleanly.
count = 0
def increment():
global count
count += 1def make_counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment