:::note[TL;DR]
- Python’s core syntax fits in one file — data types, collections, control flow, functions, classes, and file I/O
- f-strings are the modern way to format strings — use them instead of
%or.format() - List/dict/set comprehensions replace most loops; generators do it without loading everything into memory
- Always use
with open(...)for file I/O — it handles closing automatically even when exceptions occur - The standard library covers most needs before reaching for external packages:
os,json,re,datetime:::
Data types
# Integers & floats
x = 10
y = 3.14
z = 10 / 3 # float division → 3.333...
z = 10 // 3 # floor division → 3
z = 10 % 3 # modulo → 1
z = 2 ** 8 # power → 256
# Strings
s = "hello"
s = 'hello'
s = """multi
line"""
# Booleans
t = True
f = False
# None
n = None
# Type check & cast
type(x) # <class 'int'>
int("42") # 42
float("3.14") # 3.14
str(100) # "100"
bool(0) # False
Strings
s = "Hello, World"
s.upper() # "HELLO, WORLD"
s.lower() # "hello, world"
s.strip() # remove whitespace
s.lstrip() / s.rstrip() # left/right strip
s.replace("Hello", "Hi")
s.split(", ") # ["Hello", "World"]
", ".join(["a", "b"]) # "a, b"
s.startswith("Hello") # True
s.endswith("World") # True
s.find("World") # 7
len(s) # 12
# f-strings (use these)
name = "Alice"
f"Hello, {name}!" # "Hello, Alice!"
f"{3.14:.2f}" # "3.14"
:::tip
f-strings are the fastest and most readable string formatting option in Python 3.6+. Prefer them over % formatting or .format() — they embed expressions directly and support format specs like :.2f for floats.
:::
# Slicing
s[0] # "H"
s[-1] # "d"
s[0:5] # "Hello"
s[::2] # every other character
s[::-1] # reversed
Lists
lst = [1, 2, 3, 4, 5]
lst.append(6) # add to end
lst.insert(0, 0) # insert at index
lst.extend([7, 8]) # add multiple
lst.remove(3) # remove first match
lst.pop() # remove & return last
lst.pop(0) # remove & return index
lst.index(4) # find index
lst.count(2) # count occurrences
lst.sort() # sort in place
lst.sort(reverse=True)
lst.reverse() # reverse in place
sorted(lst) # returns new sorted list
len(lst) # length
# Slicing
lst[1:3] # [2, 3]
lst[::-1] # reversed
# List comprehension
squares = [x**2 for x in range(10)]
evens = [x for x in range(20) if x % 2 == 0]
Tuples
t = (1, 2, 3) # immutable
t = 1, 2, 3 # parentheses optional
a, b, c = t # unpacking
a, *rest = t # a=1, rest=[2, 3]
t[0] # 1
len(t) # 3
Dictionaries
d = {"name": "Alice", "age": 30}
d["name"] # "Alice"
d.get("name") # "Alice" (no KeyError if missing)
d.get("missing", "default") # "default"
d["city"] = "Mumbai" # add/update
del d["age"] # delete key
"name" in d # True
d.keys() # dict_keys(["name", "city"])
d.values() # dict_values(["Alice", "Mumbai"])
d.items() # dict_items([...])
d.update({"age": 31}) # merge/update
d.pop("city") # remove & return
len(d) # key count
# Dict comprehension
squares = {x: x**2 for x in range(5)}
Sets
s = {1, 2, 3, 3} # {1, 2, 3} — no duplicates
s.add(4)
s.remove(2) # KeyError if missing
s.discard(99) # no error if missing
a = {1, 2, 3}
b = {2, 3, 4}
a | b # union {1,2,3,4}
a & b # intersection {2,3}
a - b # difference {1}
a ^ b # symmetric difference {1,4}
Control flow
# if / elif / else
if x > 0:
print("positive")
elif x == 0:
print("zero")
else:
print("negative")
# Ternary
label = "pos" if x > 0 else "neg"
# for loop
for i in range(5): # 0 1 2 3 4
print(i)
for i in range(2, 10, 2): # 2 4 6 8
print(i)
for item in lst:
print(item)
for i, item in enumerate(lst):
print(i, item)
for k, v in d.items():
print(k, v)
# while loop
while x > 0:
x -= 1
# break / continue / pass
for i in range(10):
if i == 5: break # stop loop
if i % 2 == 0: continue # skip iteration
pass # do nothing (placeholder)
Functions
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
greet("Alice") # "Hello, Alice!"
greet("Alice", "Hi") # "Hi, Alice!"
greet(name="Alice") # keyword arg
# *args and **kwargs
def variadic(*args, **kwargs):
print(args) # tuple of positional args
print(kwargs) # dict of keyword args
# Lambda
square = lambda x: x ** 2
square(5) # 25
# Type hints (optional but helpful)
def add(a: int, b: int) -> int:
return a + b
Classes
class Dog:
species = "Canis familiaris" # class variable
def __init__(self, name, age):
self.name = name # instance variable
self.age = age
def bark(self):
return f"{self.name} says woof!"
def __str__(self):
return f"Dog({self.name}, {self.age})"
def __repr__(self):
return f"Dog(name={self.name!r}, age={self.age!r})"
class Puppy(Dog): # inheritance
def bark(self): # override
return f"{self.name} says yip!"
fido = Dog("Fido", 3)
fido.bark() # "Fido says woof!"
fido.name # "Fido"
isinstance(fido, Dog) # True
File I/O
:::warning
Always use with open(...) instead of manually calling f.open() and f.close(). The with block guarantees the file is closed even if an exception is raised mid-read — skipping it leaks file handles.
:::
# Read
with open("file.txt", "r") as f:
content = f.read() # whole file as string
lines = f.readlines() # list of lines
# Write
with open("file.txt", "w") as f:
f.write("hello\n")
# Append
with open("file.txt", "a") as f:
f.write("more\n")
# Read line by line (memory efficient)
with open("big.txt") as f:
for line in f:
print(line.strip())
Exceptions
try:
result = 10 / 0
except ZeroDivisionError:
print("Can't divide by zero")
except (TypeError, ValueError) as e:
print(f"Error: {e}")
else:
print("No error") # runs if no exception
finally:
print("Always runs") # cleanup
# Raise
def validate(n):
if n < 0:
raise ValueError(f"Expected positive, got {n}")
# Custom exception
class AppError(Exception):
pass
Comprehensions
# List
[x**2 for x in range(10)]
[x for x in range(20) if x % 2 == 0]
# Dict
{k: v for k, v in pairs}
{k: v for k, v in d.items() if v > 0}
# Set
{x**2 for x in range(10)}
# Generator (lazy — doesn't build list in memory)
gen = (x**2 for x in range(1_000_000))
next(gen) # 0
:::note
Generators don’t allocate memory upfront. sum(x**2 for x in range(1_000_000)) uses constant memory; sum([x**2 for x in range(1_000_000)]) allocates a million-element list first. Use generators whenever you only need to iterate once.
:::
Common built-ins
len(x) # length
range(n) # 0..n-1
range(a, b) # a..b-1
range(a, b, step)
enumerate(lst) # (index, value) pairs
zip(a, b) # pair elements
map(fn, lst) # apply function lazily
filter(fn, lst) # filter lazily
sum(lst) # sum numbers
min(lst) # minimum
max(lst) # maximum
abs(x) # absolute value
round(x, 2) # round to 2 decimal places
sorted(lst) # new sorted list
reversed(lst) # iterator reversed
any(lst) # True if any element is truthy
all(lst) # True if all elements are truthy
isinstance(x, int) # type check
Useful standard library
import os
os.getcwd() # current directory
os.listdir(".") # list files
os.path.exists("file.txt") # check existence
os.path.join("a", "b") # path join
import sys
sys.argv # command-line args
sys.exit(0) # exit with code
import json
json.dumps({"a": 1}) # dict → JSON string
json.loads('{"a": 1}') # JSON string → dict
import re
re.match(r"\d+", "123abc") # match at start
re.search(r"\d+", "abc123") # search anywhere
re.findall(r"\d+", "1a2b3") # ["1", "2", "3"]
re.sub(r"\d+", "X", "1a2") # "XaX"
from datetime import datetime
datetime.now()
datetime.now().strftime("%Y-%m-%d")
New to Python and want to understand one feature in depth? Check out Python List Comprehensions Explained.
Summary
- Python’s core data types are int, float, str, bool, None — everything else builds on these
- Lists are ordered and mutable; tuples are ordered and immutable; dicts are key-value; sets are unique unordered values
- List comprehensions, dict comprehensions, and generator expressions replace most loops in idiomatic Python
- Functions use
defwith optional type hints; classes use__init__for instance setup and inheritance for extension - The standard library covers most needs:
os,sys,json,re,datetime— reach for these before installing packages
Frequently Asked Questions
What’s the difference between a list and a tuple in Python?
Both are ordered sequences. The key difference: lists are mutable (you can add, remove, change elements), tuples are immutable (you can’t). Use tuples for fixed data like coordinates or settings that shouldn’t change; use lists for collections that grow or change.
When should I use a generator instead of a list comprehension?
When you’re processing large data and only need to iterate once. A list comprehension builds the entire list in memory immediately. A generator computes values one at a time on demand. Use sum(x**2 for x in range(1_000_000)) over sum([x**2 for x in range(1_000_000)]) to avoid allocating a million-item list.
What does if __name__ == "__main__": mean?
It checks whether the file is being run directly (python script.py) or imported as a module. Code inside that block only runs on direct execution, not on import. It’s how you write scripts that are also importable as modules without running side effects on import.
What to Read Next
- Python List Comprehensions Explained — deep dive into one of Python’s most useful features
- Python Virtual Environments — set up your project environment before you start installing packages