The authoritative, jargon-light intro. Read sections 3–4 alongside this lesson. It's short.
You already know how to program. Python is not a new paradigm — it's a different dialect. Here's what actually changes:
Indentation IS syntax. In Python, there are no {} braces for blocks. Indentation (4 spaces, by convention) defines scope. Get this wrong and your code either errors or silently does the wrong thing.
function greet(name) {
if (name) {
return `Hello, ${name}!`;
} else {
return "Hello, stranger!";
}
}
def greet(name):
if name:
return f"Hello, {name}!"
else:
return "Hello, stranger!"
Notice: def instead of function, a colon : to open a block, f"..." for template literals, and no braces anywhere.
Your JS intuition maps almost directly. Here's the cheat sheet:
// Comment
let x = 5;
const PI = 3.14;
// String template
`Hello, ${name}`
// Null / undefined check
if (val === null || val === undefined)
// Strict equality
a === b
// Array
const arr = [1, 2, 3];
arr.push(4);
arr.length;
// Object
const obj = { key: "value" };
obj.key;
obj["key"];
// for loop
for (let i = 0; i < 5; i++) { }
// for-of
for (const item of arr) { }
// Arrow function
const add = (a, b) => a + b;
// typeof
typeof x === "string"
// Ternary
x > 0 ? "pos" : "neg"
// Falsy: false, 0, "", null, undefined, NaN
# Comment
x = 5
PI = 3.14 # no const — convention: UPPER_CASE
# f-string (Python 3.6+)
f"Hello, {name}"
# None check
if val is None:
# Equality (always "strict")
a == b # no === needed
# List (like JS Array)
arr = [1, 2, 3]
arr.append(4)
len(arr)
# Dict (like JS Object)
obj = {"key": "value"}
obj["key"] # dot access doesn't exist
obj.get("key") # safe access, returns None
# for loop (range)
for i in range(5):
# for-in (iterates values, not indices!)
for item in arr:
# Lambda (limited — use def for anything complex)
add = lambda a, b: a + b
# isinstance
isinstance(x, str)
# Ternary
"pos" if x > 0 else "neg"
# Falsy: False, 0, "", None, [], {}, ()
var, let, constPython has no declaration keywords. You just assign. Scope is function-level (like var in JS), with global and nonlocal keywords for edge cases.
None is not null or undefined — it's bothPython has one "nothing" value: None. Use is None / is not None to check it (not ==).
// Falsy
false, 0, "", null, undefined, NaN
// Truthy — surprises:
[] // truthy!
{} // truthy!
# Falsy
False, 0, "", None, [], {}, (), set()
# Truthy — no surprises:
[1] # truthy (non-empty)
{"a":1} # truthy (non-empty)
Python has a 4th core sequence type: tuple. It's like an array but immutable. Used heavily for returning multiple values from functions.
point = (10, 20) # tuple
x, y = point # unpacking — like JS destructuring
name, *rest = ["a", "b", "c"] # spread equivalent
for loop iterates values, not indicesPython's for item in collection is equivalent to JS's for...of, not for...in (which in JS gives you keys). If you need the index, use enumerate():
for i, item in enumerate(arr):
print(i, item)
Strings work similarly. Key differences:
# Single or double quotes — interchangeable
name = 'Alice'
name = "Alice"
# f-strings (Python 3.6+) — your new best friend
greeting = f"Hello, {name}! You are {2026 - 1990} years old."
# Multi-line strings
poem = """
Line one
Line two
"""
# Common string methods
"hello".upper() # "HELLO"
" hello ".strip() # "hello" (like .trim())
"a,b,c".split(",") # ["a","b","c"]
",".join(["a","b","c"]) # "a,b,c" (note: reversed from JS!)
"hello".replace("l","r") # "herro"
In JS: ["a","b"].join(",") — separator is the argument.
In Python: ",".join(["a","b"]) — separator is the object you call on. Takes some getting used to.
fruits = ["apple", "banana", "cherry"]
# Slicing — Python superpower
fruits[1:] # ["banana", "cherry"]
fruits[:2] # ["apple", "banana"]
fruits[-1] # "cherry" (last element)
fruits[::-1] # reversed list
# Common operations
fruits.append("date") # push to end
fruits.insert(0, "avocado") # insert at index
fruits.pop() # remove last (returns it)
fruits.remove("banana") # remove by value
len(fruits) # length
user = {"name": "Alice", "age": 30}
# Access
user["name"] # "Alice" (KeyError if missing!)
user.get("email") # None (safe access)
user.get("email", "") # "" (with default)
# Check key
"name" in user # True
# Iterate
for key in user: # keys
for key, val in user.items(): # key-value pairs
list(user.keys()) # ["name", "age"]
list(user.values()) # ["Alice", 30]
# Merge (Python 3.9+)
merged = {**user, "city": "NY"} # spread equivalent
merged = user | {"city": "NY"} # pipe operator (3.9+)
This is where Python diverges from JS most visibly. List comprehensions replace .map() and .filter() with a single, readable expression.
const nums = [1,2,3,4,5,6];
// map
const doubled = nums.map(n => n * 2);
// filter
const evens = nums.filter(n => n % 2 === 0);
// filter + map
const doubledEvens = nums
.filter(n => n % 2 === 0)
.map(n => n * 2);
nums = [1,2,3,4,5,6]
# map equivalent
doubled = [n * 2 for n in nums]
# filter equivalent
evens = [n for n in nums if n % 2 == 0]
# filter + map (one expression!)
doubled_evens = [n * 2 for n in nums
if n % 2 == 0]
Read it left-to-right: "give me n * 2 for each n in nums where n % 2 == 0". Once it clicks, you'll use it constantly.
Don't look back. Try to recall from memory — this builds retention, not just recognition.
1. In Python, what defines a code block instead of { }?
2. What is the Python equivalent of this JS: arr.filter(x => x > 2)?
3. How do you safely access a dict key that might not exist?
4. What does for item in ["a","b","c"] iterate over?
5. In Python, [] (an empty list) is…
Questions answered correctly. Come back tomorrow to try again — spaced repetition builds long-term retention.