Every syntax, method, pattern, and idiom β organized for rapid lookup and deep understanding
site-packages to prevent conflicts.venv - built-in, lightweight, standard libraryvirtualenv - faster, supports older Python versionsconda - cross-language, binary packages (Anaconda)pipenv - official PyPA tool (Pipfile)poetry / pdm - modern pyproject.toml based workflowspyenv - manage multiple Python versions globallyDocker - full OS-level isolation# 1. Create environment python -m venv myenv # 2. Activate (Linux/macOS) source myenv/bin/activate # 2. Activate (Windows) myenv\Scripts\activate # CMD .\myenv\Scripts\Activate.ps1 # PowerShell # 3. Install packages (local to env) pip install requests # 4. Deactivate deactivate
# --- virtualenv --- (predecessor to venv) pip install virtualenv virtualenv -p /usr/bin/python3.11 myenv source myenv/bin/activate # (Faster creation, works offline, supports Python 2) # --- Conda (Anaconda / Miniconda) --- conda create -n myenv python=3.11 conda activate myenv conda install numpy pandas conda env export --no-builds > environment.yml conda deactivate
# --- Poetry --- poetry new myapp cd myapp poetry add flask # resolves dependencies & updates lock poetry add --group dev pytest poetry run python main.py poetry shell # spawn subshell with env # --- PDM --- pdm init pdm add requests pdm run python script.py # No activation needed
# --- pyenv (manage multiple Py versions) --- pyenv install 3.12.4 pyenv virtualenv 3.12.4 myproject pyenv activate myproject # Auto-switch: echo "myproject" > .python-version # --- Docker (OS-level isolation) --- FROM python:3.12-slim WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY . . CMD ["python", "app.py"]
python without activation uses global packages!sudo inside a venv. It breaks permissions or installs globally.requirements.txt or pyproject.toml in version control!brew install python3sudo apt install python3# Check version $ python --version $ python3 --version # Start interactive REPL $ python3 >>> exit() # or Ctrl+D
$ python script.py # run file $ python3 -i script.py # run, drop to REPL $ python -c 'print(42)' # one-liner $ python -m module_name # run as module $ python -m http.server # common example $ python -m pytest # run pytest # REPL special vars >>> _ # last result >>> help(str) # built-in help >>> dir(list) # attributes/methods
#!/usr/bin/env python3 # shebang (Unix) # -*- coding: utf-8 -*- # encoding def main(): print("Hello, World!") if __name__ == '__main__': main() # only when executed directly # NOT when imported
# Single-line comment x = 5 # Inline comment """Module-level docstring.""" def greet(name: str) -> str: """ Return greeting for name. Args: name (str): Person's name. Returns: str: Greeting string. Examples: >>> greet('Alice') 'Hello, Alice!' """ return f'Hello, {name}!' print(greet.__doc__) # access docstring
# --- Context Setup --- undef_var = None x = 0 # --------------------- # 1. Shadowing built-ins list = [1, 2] # BAD! overwrites built-in list() del list # Fix: unbind the name # 2. Indentation errors if True: print("bad") # IndentationError # 3. NameError (used before assigned) print(undef_var) # NameError # 4. = vs == # if x = 5: # SyntaxError (assignment in if) if x == 5: pass # Correct (comparison)
| Type | Example | Mutable |
|---|---|---|
| int | 42, -7, 0b1111 | No |
| float | 3.14, 1.5e-3 | No |
| complex | 3+4j | No |
| str | 'hello', "world" | No |
| bytes | b'data' | No |
| bool | True, False | No |
| NoneType | None | β |
| list | [1, 2, 3] | Yes |
| dict | {'a': 1} | Yes |
| set | {1, 2, 3} | Yes |
| bytearray | bytearray(b'hi') | Yes |
| tuple | (1, 2, 3) | No |
| frozenset | frozenset({1,2}) | No |
type(42) # <class 'int'> type(3.14) # <class 'float'> type('hi') # <class 'str'> type(True) # <class 'bool'> type(None) # <class 'NoneType'> isinstance(3, int) # True isinstance(True, int) # True (bool IS int!) isinstance(3, (int, float)) # True (tuple check) issubclass(bool, int) # True id(42) # unique memory address (int) hash(42) # hash value (for hashable types)
int('42') # 42 int(3.9) # 3 (truncates!) int('ff', 16) # 255 (base-16) int('0b1111', 0) # 15 (auto-detect) float('3.14') # 3.14 float('inf') # inf str(42) # '42' bool(0) # False bool('') # False bool([]) # False bool(None) # False bytes('hi', 'utf-8') # b'hi' list('abc') # ['a','b','c'] tuple([1,2,3]) # (1, 2, 3) set([1,1,2,3]) # {1, 2, 3} dict(a=1, b=2) # {'a':1,'b':2} frozenset({1,2}) # frozenset({1,2}) complex(3, 4) # (3+4j)
# --- Context Setup --- data = None import tempfile f = tempfile.TemporaryFile(mode="w+") def process(x): return x # --------------------- # Basic assignment x = 42 name, age = 'Alice', 30 # tuple unpack a = b = c = 0 # chained # Extended unpacking first, *rest = [1,2,3,4] # rest=[2,3,4] *init, last = [1,2,3,4] # init=[1,2,3] a, *mid, z = [1,2,3,4,5] # mid=[2,3,4] # Walrus operator := (3.8+) if (n := len(data)) > 10: print(f'{n} items') # Use in while loops while chunk := f.read(8192): process(chunk) # Augmented assignment x += 1; x -= 1; x *= 2; x /= 2 x //= 3; x %= 5; x **= 2 x &= 0b1111; x |= 0b0001; x ^= 1 x >>= 2; x <<= 1 del x # unbinds name from object
# LEGB: Local β Enclosing β Global β Built-in x = 'global' def outer(): x = 'enclosing' def inner(): x = 'local' print(x) # 'local' inner() print(x) # 'enclosing' count = 0 def inc(): global count # modify global var count += 1 def make_adder(n): def adder(x): nonlocal n # modify enclosing n += x; return n return adder
# Pitfall: Chained assignment with mutables a = b = [] a.append(1) print(b) # Output: [1] (both point to same list) # Pitfall: input() always returns str age = int(input("Age: ")) # MUST convert # ValueError: float string to int directly fails # x = int('4.5') # ValueError x = int(float('4.5')) # Correct # Truthy surprises bool([]) # False (empty) bool([0]) # True (non-empty) bool("0") # True (non-empty string)
255 # decimal 0b11111111 # binary β 255 0o377 # octal β 255 0xFF # hex β 255 1_000_000 # underscores (readability) bin(255) # '0b11111111' oct(255) # '0o377' hex(255) # '0xff' int('ff', 16) # 255 int('111', 2) # 7 # Bit operations 5 & 3 # 1 (AND) 5 | 3 # 7 (OR) 5 ^ 3 # 6 (XOR) ~5 # -6 (NOT, bitwise complement) 5 << 1 # 10 (left shift Γ 2) 5 >> 1 # 2 (right shift Γ· 2) (255).bit_length() # 8 (7).bit_count() # 3 (3.10+)
# Float literals 3.14; 1.5e10; 1.5e-3 float('inf') # infinity float('-inf') # negative infinity float('nan') # Not a Number import math math.isnan(float('nan')) # True math.isinf(float('inf')) # True math.isfinite(3.14) # True # Precision issues 0.1 + 0.2 # 0.30000000000000004 ! round(0.1+0.2, 10) # 0.3 from decimal import Decimal Decimal('0.1') + Decimal('0.2') # 0.3 exact # Complex numbers z = 3 + 4j z.real # 3.0 z.imag # 4.0 z.conjugate() # (3-4j) abs(z) # 5.0 (magnitude) complex(3, 4) # (3+4j)
10 + 3 # 13 addition 10 - 3 # 7 subtraction 10 * 3 # 30 multiplication 10 / 3 # 3.333 true division (always float) 10 // 3 # 3 floor division (int) 10 % 3 # 1 modulo 2 ** 10 # 1024 exponentiation divmod(17, 5) # (3, 2) pow(2, 10) # 1024 pow(2, 10, 1000) # 24 (modular pow) abs(-42) # 42 round(3.14159, 2) # 3.14
() parentheses** exponentiation (right-assoc)+x -x ~x unary* / // % multiply/divide+ - add/subtract<< >> bit shifts& ^ | bitwise (in order)== != < > <= >= comparisonsnot and or logical:= walrus (lowest)import math math.pi # 3.14159265β¦ math.e # 2.71828182β¦ math.tau # 6.28318530β¦ math.inf # float('inf') math.nan # float('nan') math.sqrt(16) # 4.0 math.ceil(3.2) # 4 math.floor(3.9) # 3 math.trunc(3.9) # 3 math.factorial(5) # 120 math.gcd(12, 8) # 4 math.lcm(4, 6) # 12 (3.9+) math.log(math.e) # 1.0 math.log(8, 2) # 3.0 math.log2(8) # 3.0 math.log10(1000) # 3.0 math.exp(1) # 2.71828β¦ math.sin(math.pi/2) # 1.0 math.cos(0) # 1.0 math.degrees(math.pi) # 180.0 math.radians(180) # 3.14159β¦ math.hypot(3, 4) # 5.0 math.comb(5, 2) # 10 (combinations) math.perm(5, 2) # 20 (permutations) math.prod([1,2,3,4]) # 24 (3.8+)
import random random.seed(42) # reproducible random.random() # 0.0 β 1.0 random.uniform(1.0, 5.0) # float in range random.randint(1, 6) # int inclusive random.randrange(0,10,2) # even 0β8 random.choice([1,2,3]) # pick one random.choices([1,2,3], k=5) # w/ replacement random.sample([1,2,3,4], 2) # no replacement lst = [1,2,3]; random.shuffle(lst) import statistics as st data = [1, 2, 3, 4, 5, 5] st.mean(data) # 3.333 st.median(data) # 3.5 st.mode(data) # 5 st.stdev(data) # 1.505 (sample) st.variance(data) # 2.266 (sample) st.pstdev(data) # population stdev st.pvariance(data) # population variance st.quantiles(data, n=4) # quartiles
# --- Context Setup --- name = "name" # --------------------- s1 = 'single'; s2 = "double" s3 = '''multi line''' # triple quotes raw = r'C:\Users\no\escape' # raw string byt = b'bytes literal' fs = f'Hello {name}' # f-string # Escape sequences # \n \t \\ \' \" \r \0 \xNN \uNNNN # Indexing & Slicing s = 'Python Basics' s[0] # 'P' (first) s[-1] # 's' (last) s[0:6] # 'Python' s[7:] # 'Basics' s[::2] # every 2nd char s[::-1] # reversed s[1:10:2] # step of 2 len(s) # 13 sl = slice(0,6); s[sl] # 'Python'
# Case 'hello'.upper() # 'HELLO' 'HELLO'.lower() # 'hello' 'hello world'.title() # 'Hello World' 'hello'.capitalize() # 'Hello' 'Hello'.swapcase() # 'hELLO' 'StraΓe'.casefold() # 'strasse' (unicode) # Search t = 'Python is great' t.find('is') # 7 (-1 if not found) t.rfind('is') # 7 (from right) t.index('is') # 7 (raises ValueError) t.count('t') # 2 t.startswith('Py') # True t.endswith('at') # True t.startswith(('Py','py')) # True (tuple) 'py' in t # False (case-sensitive)
# Strip & Replace ' hello '.strip() # 'hello' ' hello '.lstrip() # 'hello ' ' hello '.rstrip() # ' hello' '***hi***'.strip('*') # 'hi' 'hello'.replace('l','L') # 'heLLo' 'hello'.replace('l','L',1) # 'heLlo' '42'.zfill(6) # '000042' '-7'.zfill(6) # '-00007' '1\t2'.expandtabs(4) # '1 2' # Split & Join 'a,b,c'.split(',') # ['a','b','c'] 'a b c'.split() # ['a','b','c'] 'a b c'.split(' ', 1) # ['a','b c'] 'a b c'.rsplit(' ', 1) # ['a b','c'] 'a\nb\nc'.splitlines() # ['a','b','c'] ','.join(['a','b','c']) # 'a,b,c' 'key=val'.partition('=')# ('key','=','val')
# Alignment & Padding 'Hi'.center(10) # ' Hi ' 'Hi'.center(10, '*') # '****Hi****' 'Hi'.ljust(10, '-') # 'Hi--------' 'Hi'.rjust(10, '-') # '--------Hi' # Is* predicates 'abc'.isalpha() # True '123'.isdigit() # True 'abc123'.isalnum() # True ' '.isspace() # True 'Hello World'.istitle() # True 'HELLO'.isupper() # True 'hello'.islower() # True 'var_1'.isidentifier() # True 'hello'.isascii() # True 'Β²'.isnumeric() # True (unicode) 'Β²'.isdecimal() # False 'hello'.isprintable() # True # Encode / Decode 'Hello'.encode('utf-8') # b'Hello' b'Hello'.decode('utf-8') # 'Hello' # Translate tbl = str.maketrans('aeiou', '*****') 'hello'.translate(tbl) # 'h*ll*'
# --- Context Setup --- words = ["hello", "world"] # --------------------- # 1. Strings are immutable! Methods return NEW strings s = 'hello' s.upper() # s is still 'hello' s = s.upper() # Reassign to keep the change: 'HELLO' # 2. Inefficient concatenation in a loop for w in words: result += w # BAD! O(n^2) time complexity result = ''.join(words) # GOOD! O(n) # 3. find() vs index() 'hello'.find('z') # Returns -1 'hello'.index('z') # Raises ValueError!
name='Alice'; score=95.678; n=1234567 f'Hello {name}' # Hello Alice f'{score:.2f}' # 95.68 f'{score:8.2f}' # ' 95.68' f'{n:,}' # 1,234,567 f'{n:#010x}' # 0x0012d687 f'{name!r}' # "'Alice'" (repr) f'{name!s}' # 'Alice' (str) f'{name!a}' # 'Alice' (ascii) f'{score=}' # score=95.678 (3.8+) f'{2+2}' # any expression! # Alignment f'{name:<10}' # 'Alice ' (left) f'{name:>10}' # ' Alice' (right) f'{name:^10}' # ' Alice ' (center) f'{name:*^10}' # '**Alice***' (fill char) f'{n:010}' # '0001234567' (zero-pad) # Format types f'{0.5:%}' # '50.000000%' f'{65:c}' # 'A' (unicode char) f'{255:b}' # '11111111' (binary) f'{255:o}' # '377' (octal) f'{255:x}' # 'ff' (hex lower) f'{255:X}' # 'FF' (hex upper) f'{3.14:e}' # '3.140000e+00' f'{3.14:g}' # '3.14' (general)
| Spec | Meaning |
|---|---|
| < | Left align |
| > | Right align (default numbers) |
| ^ | Center |
| = | Pad after sign |
| + | Always show sign |
| - | Show minus only |
| space | Space for positive |
| # | Prefix: 0b/0o/0x |
| , | Thousands separator |
| _ | Underscore separator |
| .n | Precision digits |
| d b o x X | Integer formats |
| e E f F g G | Float formats |
| % | Percentage |
| s c n | String/char/locale |
# --- Context Setup --- d = {"a": 1, "b": 2} obj = {"key": "value"} # --------------------- # .format() method '{} {}'.format('a', 'b') # 'a b' '{0} {1} {0}'.format('a', 'b') # 'a b a' '{name}'.format(name='Alice') # 'Alice' '{:.2f}'.format(3.14159) # '3.14' '{:>10}'.format('hi') # ' hi' '{0.name}'.format(obj) # attr access '{0[key]}'.format(d) # item access # % operator (legacy) '%s is %d' % ('Alice', 30) # 'Alice is 30' '%.2f' % 3.14159 # '3.14' '%10s' % 'hi' # ' hi' '%r' % 'hello' # "'hello'" '%x' % 255 # 'ff' '%o' % 8 # '10' '%e' % 12345.6 # '1.234560e+04' # string.Template from string import Template t = Template('Hello $name!') t.substitute(name='Alice') # 'Hello Alice!' t.safe_substitute() # no KeyError
import string string.ascii_letters # 'abcdef...ABCDEF...' string.ascii_lowercase # 'abcdefghijklmnopqrstuvwxyz' string.ascii_uppercase # 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' string.digits # '0123456789' string.hexdigits # '0123456789abcdefABCDEF' string.octdigits # '01234567' string.punctuation # all punctuation chars string.whitespace # space, tab, newline... string.printable # all printable chars
| Pattern | Meaning |
|---|---|
| . | Any char except newline |
| ^ | Start of string (or line w/ MULTILINE) |
| $ | End of string |
| * | 0 or more (greedy) |
| + | 1 or more |
| ? | 0 or 1 (optional) |
| {m} | Exactly m |
| {m,n} | m to n repetitions |
| [] | Character class [a-z] [^0-9] |
| | | Alternation (OR) |
| () | Capture group |
| \d / \D | Digit / non-digit |
| \w / \W | Word char / non-word |
| \s / \S | Whitespace / non-ws |
| \b / \B | Word boundary / non |
| \A / \Z | Absolute start / end |
| *? +? ?? | Non-greedy (lazy) |
import re re.match(r'\d+', '42abc') # matches at start re.search(r'\d+', 'abc42') # anywhere in string re.findall(r'\d+', 'a1b2') # ['1','2'] re.finditer(r'\d+', 'a1b2') # iterator of matches re.sub(r'\d+', '#', 'a1b2') # 'a#b#' re.subn(r'\d+','#','a1b2') # ('a#b#', 2) re.split(r'[,;]', 'a,b;c') # ['a','b','c'] re.fullmatch(r'\d+', '123') # match entire str re.escape('a.b') # 'a\.b' # Compiled pattern (reuse) p = re.compile(r'(\w+)=(\w+)') m = p.search('key=value') m.group(0) # 'key=value' (full match) m.group(1) # 'key' m.group(2) # 'value' m.groups() # ('key', 'value') m.start() # 0 m.end() # 9 m.span() # (0, 9) # Flags re.IGNORECASE # re.I β case-insensitive re.MULTILINE # re.M β ^ $ match lines re.DOTALL # re.S β . matches \n re.VERBOSE # re.X β allow comments re.ASCII # re.A β ASCII-only \w etc
# --- Context Setup --- import re # --------------------- # Named groups p = re.compile(r'(?P<year>\d{4})-(?P<month>\d{2})') m = p.search('2024-01') m.groupdict() # {'year':'2024','month':'01'} m.group('year') # '2024' # Non-capturing group r'(?:abc)+' # group without capturing # Lookahead / Lookbehind r'\d+(?=px)' # digits followed by 'px' r'\d+(?!px)' # digits NOT followed by 'px' r'(?<=@)\w+' # word after '@' r'(?<!@)\w+' # word NOT after '@' # Backreference r'(\w+)\s+\1' # match repeated word # Verbose (readable patterns) p = re.compile(r""" (\d{4}) # year -(\d{2}) # month -(\d{2}) # day """, re.VERBOSE)
lst = [] lst = [1, 2, 3] lst = list(range(5)) # [0,1,2,3,4] lst = list('abc') # ['a','b','c'] nested = [[1,2],[3,4]] lst[0] # first element lst[-1] # last element lst[1:3] # slice [1, 2] lst[::2] # every 2nd lst[::-1] # reversed copy lst[1:4] = [10, 20] # slice assignment
# --- Context Setup --- lst = [1, 2, 3] x = 0 # --------------------- lst.append(4) # add to end lst.extend([5,6]) # add multiple lst.insert(0, 99) # insert at index lst.remove(99) # remove first match lst.pop() # remove & return last lst.pop(0) # remove & return idx 0 lst.clear() # remove all elements lst.copy() # shallow copy lst.index(3) # first index of 3 lst.count(3) # count occurrences lst.sort() # in-place sort lst.sort(reverse=True) # descending lst.sort(key=lambda x: x[1]) lst.reverse() # in-place reverse # Built-ins with lists sorted(lst) # new sorted list sorted(lst, key=len) # sort by length reversed(lst) # iterator (lazy) min(lst) / max(lst) sum(lst) len(lst) 3 in lst # membership test
# --- Context Setup --- lst = [1, 2, 3] lst1 = [1, 2] lst2 = [3, 4] nested = [] objs = [] x = 0 # --------------------- from operator import attrgetter, itemgetter people = [{'name':'Bob','age':30}, {'name':'Ann','age':25}] # Sort by dict key sorted(people, key=itemgetter('age')) # Sort by multiple keys sorted(people, key=itemgetter('age','name')) # Sort objects by attribute sorted(objs, key=attrgetter('name')) # Reverse sort sorted(lst, reverse=True) # Stable sort (preserves equal order) lst.sort(key=lambda x: x.lower()) # Copy strategies import copy shallow = lst.copy() # or lst[:] deep = copy.deepcopy(lst)# fully independent # Useful tricks flat = [x for sub in nested for x in sub] unique = list(dict.fromkeys(lst)) # preserve order lst1 + lst2 # concatenate lst * 3 # repeat
# --- Context Setup --- lst = [1, 2, 3] matrix = [[1,2], [3,4]] val = 0 x = 0 # --------------------- # Advanced Slicing [start:stop:step] lst[::-1] # Reverse list lst[-4:-1] # Negative slicing lst[::2] = [1, 2] # Strided slice assignment # List Comprehensions [expr for item in iter if cond] [x**2 for x in lst] # Basic [x for x in lst if x%2==0] # Filtered [x if x>0 else 0 for x in lst] # Ternary (if/else) # Nested List Comprehension (Matrix flattening) [val for row in matrix for val in row]
# --- Context Setup --- import copy lst = [1, 2, 3] # --------------------- # 1. Assignment vs Shallow vs Deep Copy a = [1, [2]] b = a # Aliasing (same list) c = a[:] # Shallow copy (inner list shared) d = copy.deepcopy(a) # Deep copy (fully independent) # 2. Mutating during iteration for item in lst: pass # BAD! Skips elements for item in lst[:]: pass # GOOD! Iterate over a copy # 3. Default mutable arguments def append_to(x, lst=[]): pass # BAD! lst is shared
t = () # empty tuple t = (1,) # single (comma required!) t = (1, 2, 3) t = 1, 2, 3 # packing (no parens) t = tuple([1,2,3]) t = tuple('abc') # ('a','b','c') t = tuple(range(5)) # (0,1,2,3,4) # Only 2 methods! t.count(2) # number of occurrences t.index(2) # index of first occurrence # Cannot change elements t[0] = 99 # TypeError!
# Unpacking x, y = (1, 2) first, *rest = (1,2,3,4) # rest is LIST *init, last = (1,2,3,4) a, *mid, z = (1,2,3,4,5) # Swap without temp variable a, b = b, a # Tuple as dict key (hashable!) d = {(0,0): 'origin', (1,2): 'point'} # Named tuples from collections import namedtuple Point = namedtuple('Point', ['x', 'y']) p = Point(3, 4) p.x # 3 p[0] # 3 (index still works) p._asdict() # {'x':3,'y':4} p._replace(x=10) # Point(x=10,y=4) # Typed NamedTuple from typing import NamedTuple class Point(NamedTuple): x: int y: int = 0 # default value
# --- Context Setup --- x = 0 # --------------------- # Slicing (same as lists, returns NEW tuple) coords = (10, 20, 30, 40, 50) coords[::-1] # (50, 40, 30, 20, 10) coords[1:4] # (20, 30, 40) # Tuple Comprehension (Generator) # Using () creates a generator, wrap in tuple() t = tuple(x**2 for x in range(5)) # (0, 1, 4, 9, 16) # Membership Testing (O(N) for tuples) 30 in coords # True
# 1. Single element tuple syntax t1 = (42) # Type: int! t2 = (42,) # Type: tuple (comma required) # 2. The Paradox: Mutating inside an immutable tuple t = (1, [2, 3]) t[1].append(99) # ALLOWED (list mutated in place) # t[1] = [2, 3] # TypeError (cannot reassign index) # 3. Shared nested objects with * t = ([0],) * 3 # ([0], [0], [0]) - ALL point to same list!
s = {1, 2, 3} # set literal
s = set([1, 1, 2, 3]) # from iterable
s = set() # empty (NOT {} β that's dict)
fs = frozenset({1,2,3}) # immutable set
s.add(4) # add element
s.remove(4) # raises KeyError if missing
s.discard(4) # no error if missing
s.pop() # remove arbitrary element
s.clear() # remove all
s.copy() # shallow copy
a = {1,2,3}; b = {2,3,4}
# Operators
a | b # {1,2,3,4} union
a & b # {2,3} intersection
a - b # {1} difference
a ^ b # {1,4} symmetric difference
a <= b # False subset
a >= b # False superset
a < b # proper subset
# Methods (same as above)
a.union(b)
a.intersection(b)
a.difference(b)
a.symmetric_difference(b)
a.issubset(b)
a.issuperset(b)
a.isdisjoint(b) # True if no common
# In-place operations
a.update(b) # a |= b
a.intersection_update(b) # a &= b
a.difference_update(b) # a -= b
a.symmetric_difference_update(b) # a ^= b
# frozenset: hashable, usable as dict key
fs = frozenset({1,2,3})
d = {fs: 'value'} # works!
# --- Context Setup --- x = 0 # --------------------- # Set Comprehensions (removes duplicates) {x**2 for x in [1, -1, 2, -2]} # {1, 4} {x for x in range(10) if x % 2 == 0} # Element Hashability Requirement # Elements MUST be hashable (int, str, tuple) # s = {1, [2, 3]} # TypeError (list is unhashable) # Fast Membership Testing (O(1) average) 'apple' in {'apple', 'banana'} # True (Extremely fast)
# --- Context Setup --- condition = True s = "string" # --------------------- # 1. Empty set vs Empty dict not_a_set = {} # Creates an empty dict! is_a_set = set() # Creates an empty set # 2. Mutating set during iteration for x in s: if condition: s.remove(x) # RuntimeError: Set changed size during iteration # 3. Sets of sets # s = {{1, 2}, {3, 4}} # TypeError: set is unhashable s = {frozenset({1, 2}), frozenset({3, 4})} # Valid
d = {'a':1, 'b':2}
d = dict(a=1, b=2)
d = dict([('a',1),('b',2)])
d = dict.fromkeys(['a','b'], 0)
d = {} # empty dict (not set!)
# Access
d['a'] # 1 (KeyError if missing)
d.get('a') # 1
d.get('z', 0) # 0 (default)
d.setdefault('c', 3) # insert if missing, return
# --- Context Setup --- cond = True d = {"a": 1, "b": 2} d1 = {"a": 1} d2 = {"b": 2} items = [1] x = 0 # --------------------- d['c'] = 3 # add/update d.update({'c':30}) # merge del d['c'] # delete d.pop('c') # remove & return d.pop('c', None) # safe pop d.popitem() # remove last inserted d.clear() # remove all # Iteration d.keys() # dict_keys view d.values() # dict_values view d.items() # dict_items (k,v) view for k, v in d.items(): ... # Merge (Python 3.9+) {**d1, **d2} # merge (d2 wins) d1 | d2 # merge operator d1 |= d2 # in-place merge # Dict comprehension {k:v for k,v in items if cond} {x: x**2 for x in range(5)}
# --- Context Setup --- c2 = c2 = [] dict1 = {} dict2 = {} # --------------------- from collections import ( OrderedDict, defaultdict, Counter, ChainMap, deque ) # defaultdict β no KeyError on missing key dd = defaultdict(list) dd['key'].append(1) # auto-creates [] dd = defaultdict(int) # for counters # Counter β count frequencies c = Counter('aabbbcc') # {'b':3,'a':2,'c':2} c = Counter([1,2,2,3]) c.most_common(2) # top 2 items c + c2 / c - c2 # arithmetic ops c.elements() # iterator of elements c.subtract('abc') # subtract counts # deque β double-ended queue dq = deque([1,2,3], maxlen=5) dq.appendleft(0) # add to left dq.popleft() # remove from left dq.rotate(2) # rotate right dq.extendleft([9]) # extend left # ChainMap β lookup chain cm = ChainMap(dict1, dict2) cm['key'] # searches dict1 then dict2
# --- Context Setup --- d = {"a": 1, "b": 2} k = "k" v = 0 x = 0 # --------------------- # Dict Comprehensions {x: x**2 for x in range(5)} # Basic {k: v for k, v in d.items() if v>0} # Filtered # Inverting a Dictionary (swap keys/values) inverted = {v: k for k, v in d.items()} # Sorting a dictionary dict(sorted(d.items())) # By keys dict(sorted(d.items(), key=lambda x: x[1])) # By values
# --- Context Setup --- k = "k" # --------------------- # 1. dict.fromkeys() with mutable default d = dict.fromkeys(['a', 'b'], []) d['a'].append(1) # BOTH 'a' and 'b' get [1]! d2 = {k: [] for k in ['a', 'b']} # Correct # 2. .keys(), .values(), .items() return VIEWS keys = d.keys() d['c'] = 3 print(keys) # View dynamically updates to show 'c' # 3. Unhashable keys # d = {[1, 2]: 'val'} # TypeError! d = {(1, 2): 'val'} # Correct
# --- Context Setup --- a = 0 d = {"a": 1, "b": 2} import tempfile f = tempfile.TemporaryFile(mode="w+") inner = [1,2] k = "k" lst = [1, 2, 3] matrix = [[1,2], [3,4]] outer = [[1,2]] v = 0 w = "w" words = ["hello", "world"] x = 0 # --------------------- # List comprehension [x**2 for x in range(10)] [x for x in range(20) if x % 2 == 0] [a if a>0 else -a for a in lst] # if-else [f(x) for x in outer for y in inner] # nested [x for row in matrix for x in row] # flatten # Walrus in comprehension (3.8+) results = [y := f(x), y**2, y**3] # Dict comprehension {w: len(w) for w in ['hi', 'hello']} {k:v for k,v in d.items() if v > 0} # Set comprehension {len(w) for w in words} {x*2 for x in range(5) if x > 1}
# --- Context Setup --- x = 0 # --------------------- # Generator expression (lazy!) gen = (x**2 for x in range(10)) sum(x**2 for x in range(10)) # no brackets needed next(gen) # 0 (first value) # Generator function def count_up(n): for i in range(n): yield i # pause & return def two_way(): val = yield 'first' # receive via send() yield f'got {val}' g = count_up(3) next(g) # 0 next(g) # 1 list(g) # [2] # yield from def chain(*iters): for it in iters: yield from it # send / throw / close g = two_way() next(g) # 'first' g.send('hi') # 'got hi' g.throw(ValueError) g.close() # stop generator
import itertools as it # Infinite iterators it.count(10, 2) # 10, 12, 14, ... it.cycle('ABC') # A, B, C, A, B, ... it.repeat(5, 3) # 5, 5, 5 (3 times)
# --- Context Setup --- import itertools as it x = 0 # --------------------- it.accumulate([1,2,3,4]) # 1,3,6,10 it.chain([1,2],[3,4]) # 1,2,3,4 it.chain.from_iterable([[1],[2]]) # 1,2 it.compress('ABCD',[1,0,1,1]) # A,C,D it.dropwhile(lambda x:x<3,[1,2,3,4])# 3,4 it.takewhile(lambda x:x<3,[1,2,3]) # 1,2 it.filterfalse(lambda x:x%2,range(5))# 0,2,4 it.islice(range(10), 2, 8, 2) # 2,4,6 it.starmap(pow, [(2,3),(3,2)]) # 8,9 it.zip_longest([1,2],[3], fillvalue=0) it.pairwise([1,2,3,4]) # (1,2),(2,3) 3.10+ it.groupby('AAABBBCC', key=lambda x:x)
# --- Context Setup --- import itertools as it # --------------------- it.product('AB', repeat=2) # AA AB BA BB it.permutations('ABC', 2) # AB AC BA BC CA CB it.combinations('ABC', 2) # AB AC BC it.combinations_with_replacement('ABC', 2) # AA AB AC BB BC CC
# --- Context Setup --- a = 0 b = 0 func = lambda *args: None # --------------------- from functools import ( reduce, partial, lru_cache, cache, wraps, total_ordering, cached_property, singledispatch ) reduce(lambda a,b: a+b, [1,2,3,4]) # 10 double = partial(pow, exp=2) double(base=5) # 25 # Memoization @lru_cache(maxsize=128) def fib(n): return n if n<2 else fib(n-1)+fib(n-2) @cache # unlimited (3.9+) def fib(n): ... # Preserve metadata in decorators @wraps(func) def wrapper(*a, **kw): ... # Type-based dispatch @singledispatch def process(arg): ... @process.register(int) def _(arg): ... # for int
# --- Context Setup --- val = 0 x = 0 y = 0 # --------------------- if x > 0: print('positive') elif x < 0: print('negative') else: print('zero') # Ternary expression result = 'yes' if x > 0 else 'no' # Chained comparisons 1 < x < 10 # True if x in (1,10) 0 <= y <= 100 # Truthy / Falsy # FALSY: 0, 0.0, '', [], {}, (), set(), None, False, b'' # TRUTHY: everything else # Short-circuit evaluation 0 and 1/0 # 0 (1/0 never evaluated) 5 or 1/0 # 5 (1/0 never evaluated) x = val or 'default' # common pattern any([0, '', 3]) # True (any truthy) all([1, 'a', 3]) # True (all truthy)
# --- Context Setup --- class Point: def __init__(self, x, y): self.x = x self.y = y command = None p = {"age": 30} point = (0, 0) x = 0 y = 0 # --------------------- match command: case 'quit': ... case 'start' | 'run': ... case {'action': a, 'item': i}: ... # mapping case [x, y]: ... # sequence case Point(x=x, y=y): ... # class case _ if x > 0: ... # guard case _: ... # wildcard match point: case (0, 0): print('origin') case (x, 0): print(f'x-axis at {x}') case (0, y): print(f'y-axis at {y}') case (x, y) as p: print(f'point {p}')
# --- Context Setup --- condition = True d = {"a": 1, "b": 2} done = False lst = [1, 2, 3] lst1 = [1, 2] lst2 = [3, 4] target = 0 # --------------------- for i in range(5): ... # 0,1,2,3,4 for i in range(2, 10, 2): ... # 2,4,6,8 for i, v in enumerate(lst): ... # with index for i, v in enumerate(lst, start=1): ... for a, b in zip(lst1, lst2): ... # parallel for k, v in d.items(): ... # dict for ch in 'hello': ... # string for x in reversed(lst): ... # reverse for x in sorted(lst): ... # sorted # Loop control break # exit loop continue # skip to next iteration pass # no-op placeholder # for...else (rarely known!) for x in lst: if x == target: break else: # runs ONLY if loop completed w/o break print('not found') # while loop while condition: ... while True: if done: break # Unzip with zip pairs = [(1,2),(3,4)] a, b = zip(*pairs) # unzip!
# --- Context Setup --- condition = True lst = [1, 2, 3] # --------------------- # 1. Mutating list while iterating (BAD) for item in lst: if condition: lst.remove(item) # skips elements! # FIX: iterate over a copy for item in lst[:]: ... # 2. Infinite while loop count = 0 while count < 5: print(count) # count += 1 # Forgetting this -> infinite loop # 3. range() off-by-one list(range(5)) # [0, 1, 2, 3, 4] (5 is EXCLUDED) # 4. Variable leakage for i in range(3): pass print(i) # 2 (loop variables NOT block scoped)
def greet(name: str = 'World') -> str: '''Return greeting.''' return f'Hello, {name}!' # Mutable default arg pitfall! def append(item, lst=None): # CORRECT if lst is None: lst = [] lst.append(item) return lst # Positional-only / keyword-only def f(pos1, pos2, /, both, *, kw_only): pass # pos1, pos2 MUST be positional (before /) # kw_only MUST be keyword (after *) # *args and **kwargs def func(*args, **kwargs): print(args) # tuple print(kwargs) # dict # Full order: pos β *args β kw-only β **kwargs def full(a, b, *args, c=10, **kw): ... # Unpacking in calls f(*[1,2,3]) # unpack list f(**{'a':1,'b':2}) # unpack dict # Multiple return values def minmax(lst): return min(lst), max(lst) # returns tuple lo, hi = minmax([3,1,4])
# --- Context Setup --- i = 0 n = 0 p = {"age": 30} people = [{'name':'Bob','age':30}] s = "string" x = 0 y = 0 # --------------------- # Lambda: lambda args: expression (No return!) square = lambda x: x**2 add = lambda x, y: x + y grade = lambda s: 'A' if s>=90 else 'B' # Passing as arguments sorted(people, key=lambda p: p['age']) # Factory (Lambda returning lambda) multiplier = lambda n: lambda x: x * n double = multiplier(2) # IIFE (Immediately invoked) (lambda x: x*2)(5) # 10 # Walrus in Lambda f = lambda s: (n := len(s)) ** 2 # Loop capture pitfall! (Late Binding) # BAD: fns = [lambda: i for i in range(3)] # all 2! fns = [lambda i=i: i for i in range(3)] # GOOD
# --- Context Setup --- a = 0 b = 0 x = 0 # --------------------- # map(func, iterable) m = map(lambda x: x**2, [1, 2, 3]) print(list(m)) # [1, 4, 9] # filter(func, iterable) evens = filter(lambda x: x%2==0, [1,2,3,4]) print(list(evens)) # [2, 4] # reduce(func, iterable) from functools import reduce prod = reduce(lambda a,b: a*b, [1,2,3,4]) print(prod) # 24 # Note: map/filter return lazy iterators! print(list(m)) # [] (Exhausted)
# LEGB Scope Rule: # Local β Enclosing β Global β Built-in x = 'global' def outer(): x = 'enclosing' def inner(): nonlocal x # Modifies 'enclosing' x x = 'local' # Modifying global scope count = 0 def increment(): global count # Required to modify global count += 1 # Implicit return def no_return(): pass print(no_return()) # Output: None
# First-Class Function (assign to variable) f = print f('Hello') # Recursion (function calls itself) def factorial(n: int) -> int: if n <= 1: return 1 # Base case return n * factorial(n - 1) # Recursion limit (default ~1000) import sys sys.setrecursionlimit(2000)
# 1. Calling function before definition # Function name must be bound at call time. # Fails if called during module top-level import. # 2. Assigning print() result result = print('hi') # result is None!
# --- Context Setup --- abstractmethod = None dec1 = None dec2 = None name = "name" # --------------------- from functools import wraps def my_decorator(func): @wraps(func) # preserve __name__, __doc__ def wrapper(*args, **kwargs): print('before') result = func(*args, **kwargs) print('after') return result return wrapper @my_decorator def say_hello(): print('hello') # Decorator with arguments (factory) def repeat(n): def decorator(func): @wraps(func) def wrapper(*a, **kw): for _ in range(n): func(*a, **kw) return wrapper return decorator @repeat(3) def hi(): print('hi') # Stacking (applied bottom-up) @dec1 @dec2 # dec1(dec2(f)) def f(): ... # Built-in decorators @staticmethod # no self/cls @classmethod # cls as first arg @property # getter @name.setter # setter @name.deleter # deleter @abstractmethod # from abc def _dummy(): pass
# Docstrings (__doc__) def area(r): """Return the area of a circle. (PEP 257)""" pass from typing import Optional, Union, Any from typing import Callable, TypeVar, Generic # Basic type hints def greet(name: str) -> str: ... def f(x: int, y: Optional[str] = None) -> Union[int,str]: ... # Modern syntax (3.10+) def f(x: int | None = None) -> list[int]: ... # Generic TypeVar T = TypeVar('T') def first(lst: list[T]) -> T: return lst[0] # TypedDict from typing import TypedDict class Point(TypedDict): x: float y: float # Callable[[arg_types], return_type] def apply(fn: Callable[[int], str], x: int): ... # Literal from typing import Literal def mode(m: Literal['r','w','a']): ...
def factorial(n: int) -> int: """Return n! using recursion. n >= 0.""" if n <= 1: return 1 return n * factorial(n - 1)
def binary_search(arr: list, target: int) -> int: """Return index of target in sorted arr, or -1.""" low, high = 0, len(arr) - 1 while low <= high: mid = (low + high) // 2 if arr[mid] == target: return mid elif arr[mid] < target: low = mid + 1 else: high = mid - 1 return -1
def merge_dicts(d1: dict, d2: dict) -> dict: """Combine d1 & d2; d2 overrides d1.""" return {**d1, **d2} # Python 3.5+ # Python 3.9+: d1 | d2
def remove_duplicates(lst: list) -> list: """Return unique elements preserving order.""" seen = set() result = [] for item in lst: if item not in seen: seen.add(item) result.append(item) return result
# --- Context Setup --- c = "c" # --------------------- def is_palindrome(s: str) -> bool: """True if string is palindrome (ignores symbols).""" cleaned = [c.lower() for c in s if c.isalnum()] return cleaned == cleaned[::-1]
def caesar(text: str, shift: int) -> str: res = [] for ch in text: if ch.isalpha(): base = ord('A') if ch.isupper() else ord('a') offset = (ord(ch) - base + shift) % 26 res.append(chr(base + offset)) else: res.append(ch) return ''.join(res)
def word_freq(sentence: str) -> dict: import re words = re.findall(r'\b\w+\b', sentence.lower()) freq = {} for w in words: freq[w] = freq.get(w, 0) + 1 return freq
def flatten(nested: list) -> list: flat = [] for item in nested: if isinstance(item, list): flat.extend(item) else: flat.append(item) return flat
def is_valid(s: str) -> bool: stack = [] mapping = {')':'(', ']':'[', '}':'{'} for ch in s: if ch in mapping.values(): stack.append(ch) elif ch in mapping: if not stack or stack.pop() != mapping[ch]: return False return not stack
def first_unique(s: str) -> str | None: from collections import Counter counts = Counter(s) for ch in s: if counts[ch] == 1: return ch return None
def abbrev_name(name: str) -> str: """Robert Downey Jr. -> R.D.Jr.""" parts = name.split() res = [] for i, p in enumerate(parts): if i == len(parts)-1 and not p.endswith('.'): res.append(p) else: res.append(p[0].upper() + '.') return ''.join(res)
# --- Context Setup --- c = "c" s = "string" # --------------------- def are_anagrams(s1: str, s2: str) -> bool: # Uses lambda for clean string normalisation clean = lambda s: sorted(c.lower() for c in s if c.isalnum()) return clean(s1) == clean(s2)
class Animal: species = 'Unknown' # class attribute def __init__(self, name, age): self.name = name # instance attribute self.age = age def __str__(self): # for print() return f'Animal({self.name})' def __repr__(self): # unambiguous repr return f'Animal({self.name!r},{self.age!r})' @classmethod def from_dict(cls, d): # alternative constructor return cls(d['name'], d['age']) @staticmethod def is_vertebrate(): return True @property def info(self): return f'{self.name},{self.age}' @info.setter def info(self, val): self.name, self.age = val.split(',') class Dog(Animal): # single inheritance def __init__(self, name, age, breed): super().__init__(name, age) # call parent self.breed = breed isinstance(Dog(), Animal) # True issubclass(Dog, Animal) # True Dog.__mro__ # Method Resolution Order
| Category | Methods |
|---|---|
| Repr | __str__ __repr__ __bytes__ __format__ |
| Math | __add__ __sub__ __mul__ __truediv__ __floordiv__ __mod__ __pow__ |
| Math right | __radd__ __rsub__ etc. |
| Math in-place | __iadd__ __isub__ etc. |
| Comparison | __eq__ __ne__ __lt__ __le__ __gt__ __ge__ __hash__ |
| Container | __len__ __getitem__ __setitem__ __delitem__ __contains__ __iter__ __next__ |
| Context mgr | __enter__ __exit__ |
| Lifecycle | __new__ __init__ __del__ |
| Callable | __call__(*args, **kwargs) |
| Attributes | __getattr__ __setattr__ __delattr__ __getattribute__ |
| Numeric | __bool__ __int__ __float__ __abs__ __round__ __index__ |
# --- Context Setup --- p = {"age": 30} # --------------------- from dataclasses import dataclass, field from dataclasses import asdict, astuple, replace @dataclass class Point: x: float y: float = 0.0 tags: list = field(default_factory=list) @dataclass(frozen=True) class FrozenPoint: pass class FrozenPoint: pass # immutable @dataclass(order=True) class _Dummy1: pass # comparison methods @dataclass(slots=True) class _Dummy2: pass # __slots__ (3.10+) @dataclass(kw_only=True) class _Dummy3: pass # keyword-only (3.10+) asdict(p) # {'x':1.0,'y':0.0,'tags':[]} astuple(p) # (1.0, 0.0, []) replace(p, x=5.0) # new instance changed x
# --- Context Setup --- import math # --------------------- from abc import ABC, abstractmethod class Shape(ABC): @abstractmethod def area(self) -> float: ... class Circle(Shape): def area(self) -> float: return math.pi * self.r ** 2 from enum import Enum, IntEnum, auto, unique @unique class Color(Enum): RED = auto() # 1 GREEN = auto() # 2 BLUE = auto() # 3 Color.RED # Color.RED Color.RED.value # 1 Color.RED.name # 'RED' list(Color) # all members Color['RED'] # by name Color(1) # by value
SystemExit β sys.exit()KeyboardInterrupt β Ctrl+CGeneratorExitArithmeticError: ZeroDivisionError, OverflowErrorLookupError: IndexError, KeyErrorValueError, TypeError, AttributeErrorNameError, UnboundLocalErrorImportError, ModuleNotFoundErrorOSError: FileNotFoundError, PermissionError, TimeoutErrorRuntimeError: RecursionError, NotImplementedErrorStopIteration, MemoryErrorUnicodeError: UnicodeDecodeError, UnicodeEncodeErrorSyntaxError: IndentationError, TabError# --- Context Setup --- e = Exception("e") eg = Exception("eg") x = 0 # --------------------- try: result = 10 / x except ZeroDivisionError: print('div by zero') except (ValueError, TypeError) as e: print(f'Error: {e}') except Exception as e: print(e.args, str(e)) raise # re-raise else: print('success:', result) # no exception finally: print('always runs') # cleanup # Exception chaining try: open('missing.txt') except FileNotFoundError as e: raise RuntimeError('failed') from e raise RuntimeError('failed') from None # suppress # Exception groups (3.11+) try: raise ExceptionGroup('eg', [ValueError(1), TypeError(2)]) except* ValueError as eg: print(eg.exceptions)
# --- Context Setup --- import tempfile f = tempfile.TemporaryFile(mode="w+") fnames = ["file.txt"] import os # --------------------- # Custom exceptions class ValidationError(ValueError): def __init__(self, field, msg): self.field = field super().__init__(f'{field}: {msg}') raise ValidationError('email', 'invalid') # Context manager class class Timer: def __enter__(self): import time self.start = time.time() return self def __exit__(self, *args): self.elapsed = time.time()-self.start return False # don't suppress exc # @contextmanager from contextlib import contextmanager @contextmanager def timer(): import time start = time.time() yield # 'with' block runs here print(time.time()-start) from contextlib import suppress with suppress(FileNotFoundError): os.remove('maybe_missing.txt') # Multiple context managers with open('a') as f1, open('b') as f2: ... from contextlib import ExitStack with ExitStack() as stack: files = [stack.enter_context(open(f)) for f in fnames]
| Mode | Description |
|---|---|
| 'r' | Read text (default) |
| 'w' | Write text (truncate) |
| 'a' | Append text |
| 'x' | Create (fail if exists) |
| 'rb'/'wb' | Binary read/write |
| 'r+' | Read + write |
| 'w+' | Read + write (truncate) |
# Read entire file with open('file.txt', encoding='utf-8') as f: content = f.read() # Read line by line (memory efficient) with open('file.txt') as f: for line in f: print(line.strip()) lines = f.readlines() # list of all lines line = f.readline() # one line # Write with open('out.txt', 'w', encoding='utf-8') as f: f.write('Hello\n') f.writelines(['a\n', 'b\n']) # Seek / Tell f.seek(0) # go to start f.tell() # current position (bytes) f.flush() # flush buffer
# --- Context Setup --- new = 0 # --------------------- from pathlib import Path p = Path('/home/user/docs/file.txt') p.name # 'file.txt' p.stem # 'file' p.suffix # '.txt' p.suffixes # ['.txt'] p.parent # Path('/home/user/docs') p.parts # ('/', 'home', 'user', ...) p.anchor # '/' Path.cwd() # current directory Path.home() # home directory p / 'subdir' / 'file.py' # join paths p.exists(); p.is_file(); p.is_dir() p.is_symlink(); p.is_absolute() p.stat() # os.stat_result p.mkdir(parents=True, exist_ok=True) p.rmdir() # must be empty p.touch() # create file p.unlink() # delete file p.rename(new); p.replace(new) # move p.read_text(encoding='utf-8') p.write_text('content') p.read_bytes(); p.write_bytes(b'data') list(p.iterdir()) # dir contents list(p.glob('*.py')) # pattern match list(p.rglob('*.py')) # recursive p.resolve() # absolute real path p.relative_to('/home/user') # 'docs/file.txt'
import os, shutil os.getcwd() # current dir os.chdir('/tmp') # change dir os.listdir('.') # list entries os.makedirs('a/b', exist_ok=True) os.remove('file.txt') os.rename('old', 'new') os.stat('file.txt') os.environ.get('HOME') os.getenv('PATH', '') os.path.join('a','b','c') # 'a/b/c' os.path.exists('file') os.path.dirname('/a/b/c') # '/a/b' os.path.basename('/a/b/c') # 'c' os.path.splitext('f.txt') # ('f', '.txt') os.path.abspath('.') os.path.expanduser('~') # Walk directory tree for root, dirs, files in os.walk('.'): for f in files: print(os.path.join(root, f)) shutil.copy('src', 'dst') # copy file shutil.copy2('src', 'dst') # copy + metadata shutil.copytree('src', 'dst') # copy directory shutil.rmtree('dir') # remove directory shutil.move('src', 'dst') shutil.make_archive('out', 'zip', 'dir')
import math # module from math import sqrt, pi # specific from math import * # all (avoid!) import numpy as np # alias from os.path import join as pjoin # Relative imports (inside packages) from . import sibling_module from .. import parent_module from .utils import helper # Conditional import try: import ujson as json except ImportError: import json # __all__ controls 'from mod import *' __all__ = ['PublicClass', 'public_func'] # sys.path manipulation import sys sys.path.append('/my/custom/path') sys.path.insert(0, '.') # highest priority import importlib mod = importlib.import_module('os.path') importlib.reload(mod)
import sys sys.argv # ['script.py','arg1'...] sys.path # module search paths sys.modules # loaded modules cache sys.version # '3.12.0 ...' sys.platform # 'linux'/'win32'/'darwin' sys.exit(0) # exit with code 0 sys.stdin / sys.stdout / sys.stderr sys.getrecursionlimit() # 1000 sys.setrecursionlimit(5000) sys.getsizeof([1,2,3]) # bytes
from datetime import date, time, datetime, timedelta today = date.today() # date(2024,1,15) now = datetime.now() # current datetime d = date(2024, 1, 15) dt = datetime(2024, 1, 15, 10, 30, 0) d.year / d.month / d.day dt.hour / dt.minute / dt.second td = timedelta(days=7, hours=3) future = now + td diff = date(2024,12,31) - today diff.days # int days now.strftime('%Y-%m-%d %H:%M:%S') # format datetime.strptime('2024-01-15', '%Y-%m-%d') from datetime import timezone datetime.now(timezone.utc) # UTC aware
# --- Context Setup --- obj = {"key": "value"} # --------------------- import json json.dumps({'a':1, 'b':[1,2,3]}) json.dumps(obj, indent=2, sort_keys=True) json.dumps(obj, default=str) # non-serializable with open('data.json', 'w') as f: json.dump(obj, f, indent=2) json.loads('{"a": 1}') with open('data.json') as f: data = json.load(f) import csv with open('data.csv') as f: for row in csv.reader(f): ... for row in csv.DictReader(f): print(row['name']) with open('out.csv', 'w', newline='') as f: writer = csv.writer(f) writer.writerow(['name', 'age']) writer.writerows([['Alice', 30], ['Bob', 25]])
import logging logging.basicConfig( level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s', filename='app.log' ) logging.debug('debug') # level 10 logging.info('info') # level 20 logging.warning('warn') # level 30 logging.error('error') # level 40 logging.critical('crit') # level 50 logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) handler = logging.StreamHandler() handler.setFormatter( logging.Formatter('%(name)s: %(message)s') ) logger.addHandler(handler)
# --- Context Setup --- async_generator = async def async_generator(): yield 1 fetch = async def fetch(url): pass long_operation = async def long_operation(): pass # --------------------- # --- Context Setup --- import sys class aiofiles: @staticmethod def open(*args, **kwargs): class AsyncFile: async def __aenter__(self): return self async def __aexit__(self, *args): pass async def read(self): return "" async def write(self, data): pass return AsyncFile() async def async_generator(): yield 1 async def fetch(url): pass async def long_operation(): pass url = "http://example.com" # --------------------- import asyncio # Coroutine function async def fetch(url): await asyncio.sleep(1) # non-blocking return f'fetched {url}' # Run coroutine asyncio.run(fetch('http://example.com')) # Run multiple concurrently async def main(): results = await asyncio.gather( fetch('url1'), fetch('url2'), fetch('url3'), ) return results # Tasks async def main(): task = asyncio.create_task(fetch('url')) await asyncio.sleep(0) # yield control result = await task # Async for / async with async for item in async_generator(): ... async with aiofiles.open('file') as f: data = await f.read() # asyncio primitives lock = asyncio.Lock() async with lock: ... event = asyncio.Event() await event.wait(); event.set() sem = asyncio.Semaphore(5) q = asyncio.Queue() await q.put(item); item = await q.get() # Timeout (3.11+) async with asyncio.timeout(5.0): await long_operation()
# --- Context Setup --- callback = lambda: None # --------------------- import threading def worker(n): print(f'Thread {n}') t = threading.Thread(target=worker, args=(1,)) t.daemon = True # dies with main thread t.start() t.join() # wait for completion lock = threading.Lock() rlock = threading.RLock() # reentrant with lock: shared_counter += 1 event = threading.Event() event.wait(); event.set(); event.clear() sem = threading.Semaphore(5) timer = threading.Timer(5.0, callback) timer.start(); timer.cancel() # multiprocessing from multiprocessing import Process, Pool, Queue def worker(n): ... p = Process(target=worker, args=(1,)) p.start(); p.join() # Pool for parallel map with Pool(4) as pool: results = pool.map(worker, range(10)) # also: pool.imap(), pool.starmap() q = Queue() q.put('data'); q.get(); q.empty()
# Create virtual environment $ python -m venv .venv $ python3 -m venv .venv # Activate $ .venv\Scripts\activate # Windows (cmd) $ .venv\Scripts\Activate.ps1 # Windows (PS) $ source .venv/bin/activate # macOS/Linux $ deactivate # deactivate # pip commands $ pip install requests $ pip install 'requests>=2.28' $ pip install requests==2.28.2 $ pip install -r requirements.txt $ pip uninstall requests $ pip list $ pip show requests $ pip freeze > requirements.txt $ pip install --upgrade pip $ pip install --user package # user-level # pipx (isolated tool install) $ pipx install black $ pipx run black script.py
# Code quality tools $ black . # auto-format $ isort . # sort imports $ flake8 . # style / lint $ pylint mymod.py # deep linting $ mypy mymod.py # type checking $ pytest # run tests $ pytest -v # verbose $ pytest -k 'test_name' # filter $ pytest --cov=. --cov-report=html # pyproject.toml (modern) [build-system] requires = ['setuptools>=61'] [project] name = 'mypackage' version = '1.0.0' dependencies = ['requests>=2.28'] [project.optional-dependencies] dev = ['pytest', 'black', 'mypy']
class Countdown: def __init__(self, n): self.n = n def __iter__(self): return self def __next__(self): if self.n <= 0: raise StopIteration self.n -= 1; return self.n + 1 for x in Countdown(3): print(x) # 3 2 1 it = iter([1,2,3]) next(it) # 1 next(it, 0) # 0 as default (no StopIteration)
class Descriptor: def __set_name__(self, owner, name): self.name = name def __get__(self, obj, objtype=None): if obj is None: return self return obj.__dict__.get(self.name) def __set__(self, obj, value): obj.__dict__[self.name] = value def __delete__(self, obj): del obj.__dict__[self.name] class MyClass: attr = Descriptor() # class-level!
# --- Context Setup --- class Base: pass # --------------------- # type() creates classes dynamically MyClass = type('MyClass', (Base,), {'attr': 1}) class Meta(type): def __new__(mcs, name, bases, namespace): print(f'Creating class {name}') return super().__new__(mcs, name, bases, namespace) class MyClass(metaclass=Meta): pass # __init_subclass__ class Base: def __init_subclass__(cls, **kw): super().__init_subclass__(**kw) print(f'Subclass {cls} created')
# --- Context Setup --- stmt = "pass" # --------------------- class Point: __slots__ = ['x', 'y'] # no __dict__! def __init__(self, x, y): self.x = x; self.y = y # ~40% less memory per instance # Cannot add arbitrary attributes # timeit import timeit timeit.timeit('x=2**10', number=100000) timeit.repeat(stmt, number=1000, repeat=5) # cProfile import cProfile cProfile.run('my_function()') # Debugger import pdb; pdb.set_trace() # breakpoint breakpoint() # Python 3.7+ shorthand # pdb commands: n s c l p pp b q u d r a # tracemalloc import tracemalloc tracemalloc.start() snapshot = tracemalloc.take_snapshot() stats = snapshot.statistics('lineno')
# --- Context Setup --- d = {"a": 1, "b": 2} default = 0 key = "" lst = [1, 2, 3] lst1 = [1, 2] lst2 = [3, 4] parts = [] word = "" word_count = {} x = 0 # --------------------- # Swap variables a, b = b, a # Check None (correct) if x is None: ... if x is not None: ... # Truthiness (not len check) if lst: ... # not: if len(lst) > 0 if not lst: ... # not: if len(lst) == 0 # EAFP (Python-preferred) try: val = d[key] except KeyError: val = default # String join (O(n), not O(nΒ²)) result = ''.join(parts) # fast # NOT: for p in parts: result += p # dict.get() for defaults count = word_count.get(word, 0) + 1 # enumerate instead of range+index for i, v in enumerate(lst): ... # NOT: for i in range(len(lst)): lst[i] # zip for parallel iteration for a, b in zip(lst1, lst2): ... # Context managers for resources with open('f') as f: ... # not: open() then close()
# --- Context Setup --- i = 0 # --------------------- # 1. Mutable default argument def f(lst=[]): ... # BUG: shared! def f(lst=None): # CORRECT if lst is None: lst = [] # 2. Float comparison 0.1 + 0.2 == 0.3 # False! abs(0.1+0.2-0.3) < 1e-9 # True # 3. Late binding in closures fns = [lambda: i for i in range(3)] # all 2! fns = [lambda i=i: i for i in range(3)] # 0,1,2 # 4. Chained assignment with mutable a = b = [] # SAME list! a.append(1); print(b) # [1] β shared! # 5. Integer caching a = 1000; b = 1000 a is b # False (not guaranteed for >256) a == b # True (use == for values!) # 6. += on immutables t = (1,2); old_id = id(t) t += (3,); id(t) == old_id # False! new obj
f(x=1)a = b + c| Style | Use For |
|---|---|
snake_case | variables, functions, modules |
PascalCase | classes |
UPPER_SNAKE | constants |
_private | internal use convention |
__mangled | name mangling in class |
__dunder__ | magic/special methods |
import subprocess r = subprocess.run( ['ls', '-la'], capture_output=True, text=True ) r.stdout; r.returncode; r.stderr subprocess.check_output(['cmd'], text=True) import argparse p = argparse.ArgumentParser(description='Tool') p.add_argument('filename') # positional p.add_argument('-v', '--verbose', action='store_true') p.add_argument('-n', type=int, default=5) p.add_argument('--choices', choices=['a','b']) args = p.parse_args() args.filename; args.verbose; args.n
# --- Context Setup --- val = 0 # --------------------- import heapq lst = [3,1,4,1,5,9,2,6] heapq.heapify(lst) # in-place min-heap heapq.heappush(lst, 0) # push heapq.heappop(lst) # pop smallest heapq.heappushpop(lst, 7) # push then pop heapq.heapreplace(lst, 7) # pop then push heapq.nlargest(3, lst) # [9,6,5] heapq.nsmallest(3, lst) # [0,1,1] # Max-heap: negate values heapq.heappush(lst, -val) # simulate max-heap
# --- Context Setup --- import csv # --------------------- from io import StringIO, BytesIO sio = StringIO() sio.write('Hello\nWorld') sio.getvalue() # 'Hello\nWorld' sio.seek(0) sio.read() # 'Hello\nWorld' # Use as file-like object csv.reader(StringIO('a,b,c')) bio = BytesIO(b'binary data') bio.read(6) # b'binary'
import hashlib hashlib.md5(b'hello').hexdigest() hashlib.sha256(b'hello').hexdigest() hashlib.sha512(b'hello').hexdigest() h = hashlib.new('sha256') h.update(b'hello'); h.update(b' world') h.hexdigest() import base64 base64.b64encode(b'hello world') base64.b64decode(b'aGVsbG8gd29ybGQ=') base64.urlsafe_b64encode(b'data?')
import unittest from unittest.mock import patch, MagicMock class TestMyCode(unittest.TestCase): def setUp(self): self.data = [1,2,3] def test_sum(self): self.assertEqual(sum(self.data), 6) self.assertTrue(len(self.data) > 0) self.assertIn(2, self.data) self.assertRaises(ValueError, int, 'abc') @unittest.skip('reason') def test_skipped(self): ... @patch('module.function') def test_mock(self, mock_fn): mock_fn.return_value = 42 if __name__ == '__main__': unittest.main()
# --- Context Setup --- obj = {"key": "value"} # --------------------- import copy copy.copy(obj) # shallow copy copy.deepcopy(obj) # deep copy import pprint pprint.pprint({'a': [1,2,3], 'b': {'c':4}}) pp = pprint.PrettyPrinter(indent=2, width=40) import textwrap textwrap.wrap('long text here', width=20) textwrap.fill('long text here', width=20) textwrap.dedent(''' indented text second line''') # remove common indent import tempfile tempfile.NamedTemporaryFile() tempfile.TemporaryDirectory() tempfile.mkstemp(); tempfile.mkdtemp()