Python Complete Revision Cheat Sheet
On this page
Quick Start: Python Basics
Python is a high-level, interpreted, dynamically typed language known for its clean syntax and readability. It supports multiple paradigms: procedural, object-oriented, and functional.
Run a file: python script.py
Interactive shell: python or python3 in terminal
Check version: python --version
Install packages: pip install package_name
Virtual environment: python -m venv venv then source venv/bin/activate (Mac/Linux) or venv\Scripts\activate (Windows)
Variables and Data Types
# Variables (no declaration needed)
name = 'Alice'
age = 30
price = 9.99
is_active = True
nothing = None
# Type checking
type(name) # str
type(age) # int
type(price) # float
type(is_active) # bool
# Type conversion
int('42') # 42
float('3.14') # 3.14
str(100) # '100'
bool(0) # False
bool(1) # True
bool('') # FalseCore Data Types Summary
Type | Example | Mutable | Ordered |
|---|---|---|---|
str | 'hello' | No | Yes |
int | 42 | No | N/A |
float | 3.14 | No | N/A |
bool | True / False | No | N/A |
list | [1, 2, 3] | Yes | Yes |
tuple | (1, 2, 3) | No | Yes |
dict | {'a': 1} | Yes | Yes (3.7+) |
set | {1, 2, 3} | Yes | No |
frozenset | frozenset({1,2}) | No | No |
NoneType | None | No | N/A |
Strings
s = 'Hello, World!'
# Common methods
s.upper() # 'HELLO, WORLD!'
s.lower() # 'hello, world!'
s.strip() # removes leading/trailing whitespace
s.replace('H','J') # 'Jello, World!'
s.split(', ') # ['Hello', 'World!']
s.startswith('He') # True
s.endswith('!') # True
len(s) # 13
s[0] # 'H'
s[-1] # '!'
s[0:5] # 'Hello'
s[::-1] # reverse string
# f-strings (Python 3.6+) - preferred
name = 'Alice'
age = 30
print(f'Name: {name}, Age: {age}') # Name: Alice, Age: 30
print(f'{age * 2}') # 60
print(f'{price:.2f}') # formatted float
# String joining
words = ['Python', 'is', 'great']
' '.join(words) # 'Python is great'
# Multi-line string
text = '''Line 1
Line 2
Line 3'''Lists
nums = [1, 2, 3, 4, 5]
# Access
nums[0] # 1
nums[-1] # 5
nums[1:3] # [2, 3]
nums[::2] # [1, 3, 5]
# Modify
nums.append(6) # [1,2,3,4,5,6]
nums.insert(0, 0) # insert at index
nums.extend([7, 8]) # extend with another list
nums.remove(3) # remove first occurrence of value
nums.pop() # remove and return last item
nums.pop(0) # remove and return item at index
nums.sort() # sort in place
nums.sort(reverse=True)
nums.reverse() # reverse in place
nums.index(4) # find index of value
nums.count(2) # count occurrences
len(nums) # length
# Sorted (returns new list, does not modify original)
sorted(nums)
sorted(nums, reverse=True)
sorted(words, key=len) # sort by length
# Copy
nums_copy = nums.copy() # shallow copy
nums_copy = nums[:] # also shallow copy
# Check membership
3 in nums # True
9 not in nums # TrueTuples
t = (1, 2, 3)
point = (10, 20)
# Access (same as list but immutable)
t[0] # 1
t[-1] # 3
# Unpacking
x, y = point # x=10, y=20
a, *rest = (1,2,3,4) # a=1, rest=[2,3,4]
# Single-element tuple (note the comma)
one = (42,)
# Named tuple
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(3, 4)
p.x # 3
p.y # 4Dictionaries
person = {'name': 'Alice', 'age': 30, 'city': 'NYC'}
# Access
person['name'] # 'Alice'
person.get('age') # 30
person.get('salary', 0) # 0 (default if key missing)
# Modify
person['email'] = '[email protected]' # add/update
del person['city'] # delete key
person.pop('age') # remove and return value
# Iteration
for key in person:
print(key)
for key, value in person.items():
print(key, value)
for value in person.values():
print(value)
# Check membership
'name' in person # True
'salary' in person # False
# Useful methods
person.keys() # dict_keys([...])
person.values() # dict_values([...])
person.items() # dict_items([...])
person.update({'age': 31, 'country': 'US'}) # merge/update
# Merge (Python 3.9+)
merged = dict1 | dict2
# Default dict
from collections import defaultdict
d = defaultdict(int)
d['missing_key'] += 1 # no KeyError; defaults to 0Sets
s1 = {1, 2, 3, 4}
s2 = {3, 4, 5, 6}
s1.add(5) # add element
s1.remove(1) # remove (raises error if missing)
s1.discard(99) # remove safely (no error)
# Set operations
s1 | s2 # union: {1,2,3,4,5,6}
s1 & s2 # intersection: {3,4}
s1 - s2 # difference: {1,2}
s1 ^ s2 # symmetric difference: {1,2,5,6}
# Membership (O(1) average)
3 in s1 # True
# Convert list to set to remove duplicates
unique = list(set([1,2,2,3,3,4])) # [1,2,3,4]Control Flow
# if / elif / else
if score >= 90:
grade = 'A'
elif score >= 80:
grade = 'B'
elif score >= 70:
grade = 'C'
else:
grade = 'F'
# Ternary (one-liner)
status = 'adult' if age >= 18 else 'minor'
# for loop
for i in range(5): # 0 1 2 3 4
print(i)
for i in range(1, 10, 2): # 1 3 5 7 9
print(i)
for item in my_list:
print(item)
for i, item in enumerate(my_list):
print(i, item)
for a, b in zip(list1, list2):
print(a, b)
# while loop
n = 0
while n < 5:
print(n)
n += 1
# Loop controls
break # exit loop entirely
continue # skip to next iteration
pass # do nothing placeholder
# for...else (else runs if loop completes without break)
for item in items:
if condition:
break
else:
print('No break occurred')Comprehensions
# List comprehension
squares = [x**2 for x in range(10)]
evens = [x for x in range(20) if x % 2 == 0]
flat = [n for row in matrix for n in row]
# Dict comprehension
square_map = {x: x**2 for x in range(5)}
# {0:0, 1:1, 2:4, 3:9, 4:16}
# Set comprehension
unique_lens = {len(word) for word in words}
# Generator expression (lazy evaluation, memory efficient)
total = sum(x**2 for x in range(1000000))
# Nested with condition
result = [x*y for x in range(5) for y in range(5) if x != y]Functions
# Basic function
def greet(name):
return f'Hello, {name}!'
# Default parameters
def greet(name, greeting='Hello'):
return f'{greeting}, {name}!'
# *args (variable positional arguments)
def add(*args):
return sum(args)
add(1, 2, 3) # 6
# **kwargs (variable keyword arguments)
def info(**kwargs):
for k, v in kwargs.items():
print(f'{k}: {v}')
info(name='Alice', age=30)
# Combining all
def func(a, b, *args, key='default', **kwargs):
pass
# Lambda (anonymous function)
square = lambda x: x**2
sort_key = lambda item: item['age']
sorted(people, key=lambda p: p['age'])
# Type hints (Python 3.5+)
def add(a: int, b: int) -> int:
return a + b
# Unpacking arguments
def add(a, b, c):
return a + b + c
args = [1, 2, 3]
add(*args) # positional unpacking
kwargs = {'a':1, 'b':2, 'c':3}
add(**kwargs) # keyword unpackingDecorators
# Basic decorator
def my_decorator(func):
def wrapper(*args, **kwargs):
print('Before call')
result = func(*args, **kwargs)
print('After call')
return result
return wrapper
@my_decorator
def say_hello():
print('Hello!')
# functools.wraps (preserves function metadata)
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
# Common built-in decorators
@staticmethod # no self or cls parameter
@classmethod # receives cls instead of self
@property # access method like an attributeObject-Oriented Programming (OOP)
class Animal:
species = 'Unknown' # class variable (shared)
def __init__(self, name, age):
self.name = name # instance variable
self.age = age
def speak(self):
return f'{self.name} makes a sound'
def __str__(self):
return f'Animal({self.name}, {self.age})'
def __repr__(self):
return f'Animal(name={self.name!r}, age={self.age!r})'
@classmethod
def from_dict(cls, data):
return cls(data['name'], data['age'])
@staticmethod
def is_valid_age(age):
return age > 0
# Inheritance
class Dog(Animal):
def __init__(self, name, age, breed):
super().__init__(name, age) # call parent __init__
self.breed = breed
def speak(self): # method override
return f'{self.name} says Woof!'
# Multiple inheritance
class C(A, B):
pass
# Abstract class
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14159 * self.radius ** 2Key Dunder (Magic) Methods
Method | Triggered By |
|---|---|
__init__ | Object creation |
__str__ | str(obj) and print(obj) |
__repr__ | repr(obj), debugging |
__len__ | len(obj) |
__eq__ | obj == other |
__lt__ | obj less than other |
__add__ | obj + other |
__contains__ | item in obj |
__iter__ | for item in obj |
__getitem__ | obj[key] |
__enter__ / __exit__ | with statement (context manager) |
Error Handling
# try / except / else / finally
try:
result = 10 / 0
except ZeroDivisionError as e:
print(f'Error: {e}')
except (TypeError, ValueError) as e:
print(f'Type or Value error: {e}')
except Exception as e:
print(f'Unexpected error: {e}')
else:
print('No error occurred') # runs if no exception
finally:
print('Always runs') # cleanup code
# Raise exceptions
raise ValueError('Invalid input')
raise TypeError(f'Expected int, got {type(x)}')
# Custom exception
class InsufficientFundsError(Exception):
def __init__(self, amount, balance):
self.amount = amount
self.balance = balance
super().__init__(f'Cannot withdraw {amount}, balance is {balance}')
# Context manager (with statement)
with open('file.txt', 'r') as f:
content = f.read()
# file automatically closed after blockCommon Built-in Exceptions
ValueError: Right type, wrong value (int('abc'))
TypeError: Wrong type for operation ('a' + 1)
KeyError: Dict key not found
IndexError: List index out of range
AttributeError: Object has no such attribute
FileNotFoundError: File does not exist
ImportError: Module not found
StopIteration: Iterator exhausted
OverflowError: Arithmetic result too large
RecursionError: Maximum recursion depth exceeded
File I/O
# Read entire file
with open('data.txt', 'r') as f:
content = f.read() # entire file as string
# Read line by line (memory efficient)
with open('data.txt', 'r') as f:
for line in f:
print(line.strip())
# Read all lines into list
with open('data.txt', 'r') as f:
lines = f.readlines() # list of strings with newlines
# Write to file
with open('output.txt', 'w') as f: # 'w' overwrites
f.write('Hello\n')
with open('log.txt', 'a') as f: # 'a' appends
f.write('New log entry\n')
# Write multiple lines
lines = ['line1\n', 'line2\n', 'line3\n']
with open('output.txt', 'w') as f:
f.writelines(lines)
# JSON
import json
with open('data.json', 'r') as f:
data = json.load(f) # file to dict
with open('data.json', 'w') as f:
json.dump(data, f, indent=2)
json_str = json.dumps(data) # dict to JSON string
data = json.loads(json_str) # JSON string to dict
# CSV
import csv
with open('data.csv', 'r') as f:
reader = csv.DictReader(f)
for row in reader:
print(row['column_name'])Iterators and Generators
# Generator function (uses yield)
def count_up(n):
i = 0
while i < n:
yield i
i += 1
for num in count_up(5):
print(num) # 0 1 2 3 4
# Generator expression
gen = (x**2 for x in range(10))
next(gen) # 0
next(gen) # 1
# Custom iterator
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
# itertools (very useful)
import itertools
itertools.chain([1,2], [3,4]) # [1,2,3,4]
itertools.combinations([1,2,3], 2) # (1,2),(1,3),(2,3)
itertools.permutations([1,2,3], 2)
itertools.product([1,2], [3,4]) # cartesian product
itertools.groupby(data, key=lambda x: x['dept'])
itertools.islice(generator, 5) # take first 5Modules and Packages
# Import styles
import math
from math import sqrt, pi
from math import sqrt as sq
from os import path as p
import numpy as np # aliased import
# Useful standard library modules
import os # OS operations, path handling
import sys # system parameters and functions
import re # regular expressions
import json # JSON encode/decode
import datetime # date and time
import collections # Counter, defaultdict, deque, OrderedDict
import itertools # combinatoric iterators
import functools # reduce, lru_cache, wraps, partial
import pathlib # modern file path handling
import logging # logging framework
import copy # shallow and deep copy
import random # random numbers
import string # string constants
import time # time functions
import threading # thread-based parallelism
import subprocess # run shell commands
# __name__ guard (entry point check)
if __name__ == '__main__':
main()Important Built-in Functions
# Sequence operations
len([1,2,3]) # 3
sum([1,2,3]) # 6
min([3,1,2]) # 1
max([3,1,2]) # 3
abs(-5) # 5
round(3.14159, 2) # 3.14
sorted([3,1,2]) # [1,2,3]
reversed([1,2,3]) # iterator
enumerate(['a','b']) # (0,'a'),(1,'b')
zip([1,2],[3,4]) # (1,3),(2,4)
map(str, [1,2,3]) # ['1','2','3']
filter(None, [0,1,'',2]) # [1,2]
any([0, False, 1]) # True
all([1, True, 'a']) # True
# Object introspection
type(obj) # type of object
isinstance(obj, int) # True if obj is int or subclass
hasattr(obj, 'method') # check attribute exists
getattr(obj, 'name') # get attribute value
setattr(obj, 'name', v) # set attribute value
dir(obj) # list all attributes/methods
vars(obj) # __dict__ of object
callable(obj) # True if obj is callable
# Functional tools
from functools import reduce, lru_cache, partial
reduce(lambda a,b: a+b, [1,2,3,4]) # 10
@lru_cache(maxsize=128)
def fib(n):
return n if n < 2 else fib(n-1) + fib(n-2)
double = partial(pow, exp=2)
double(3) # 9Regular Expressions
import re
text = 'Contact: [email protected] or [email protected]'
# Match at start
re.match(r'Contact', text)
# Search anywhere
re.search(r'\w+@\w+\.\w+', text)
# Find all matches
re.findall(r'\w+@\w+\.\w+', text)
# ['[email protected]', '[email protected]']
# Substitute
re.sub(r'\s+', '-', 'hello world') # 'hello-world'
# Split
re.split(r',\s*', 'a, b,c , d') # ['a','b','c','d']
# Compile for reuse (faster in loops)
pattern = re.compile(r'\d+')
pattern.findall('123 abc 456') # ['123', '456']
# Common patterns
r'\d+' # one or more digits
r'\w+' # one or more word chars
r'\s+' # one or more whitespace
r'^start' # starts with
r'end$' # ends with
r'[a-z]+' # lowercase letters
r'(\d{3})-?(\d{4})' # groupsdatetime
from datetime import datetime, date, timedelta
now = datetime.now() # current datetime
today = date.today() # current date
# Create specific datetime
dt = datetime(2026, 1, 15, 10, 30) # year,month,day,hour,min
# Formatting
dt.strftime('%Y-%m-%d %H:%M:%S') # '2026-01-15 10:30:00'
dt.strftime('%d/%m/%Y') # '15/01/2026'
# Parsing
datetime.strptime('2026-01-15', '%Y-%m-%d')
# Arithmetic
tomorrow = today + timedelta(days=1)
next_week = today + timedelta(weeks=1)
diff = datetime(2026,12,31) - datetime.now() # timedelta
diff.days # number of daysCommon Mistakes and Pitfalls
Mutable default arguments: Never use def func(lst=[]) as the list is shared across calls; use def func(lst=None) then lst = lst or [] inside.
Shallow vs deep copy: copy.copy() copies references for nested objects; use copy.deepcopy() when you need fully independent copies.
is vs ==: is checks identity (same object in memory); == checks equality (same value). Never use is to compare strings or integers outside of None checks.
Integer division: In Python 3, / always returns float; use // for integer floor division.
Modifying a list while iterating: Causes skipped elements; iterate over a copy (for item in lst[:]) or use list comprehension instead.
Global vs local scope: To modify a global variable inside a function, declare it with the global keyword first.
Late binding closures: Variables in closures are looked up at call time, not definition time; use default argument (x=x) to capture current value.
Chained comparison works but confuses: 1 == 1 == 1 is True in Python (unlike most languages).
Forgetting self: Instance methods must have self as their first parameter; forgetting it causes TypeError on call.
String immutability: Strings cannot be changed in place; string operations always return new strings.
Top Interview Questions and Answers 2026
Q1: What is the difference between a list and a tuple?
Lists are mutable (can be changed after creation) and use square brackets. Tuples are immutable (cannot be changed) and use parentheses. Tuples are faster, use less memory, and can be used as dictionary keys or set members because they are hashable. Use tuples for fixed collections and lists when you need to modify the sequence.
Q2: What are *args and **kwargs?
*args allows a function to accept any number of positional arguments, collected into a tuple. **kwargs allows any number of keyword arguments, collected into a dictionary. They can be combined: def func(a, *args, **kwargs). The names args and kwargs are conventions; the asterisks are what matter syntactically.
Q3: What is a decorator and how does it work?
A decorator is a function that takes another function as input, wraps it with additional behavior, and returns the modified function. Using @decorator_name above a function definition is syntactic sugar for func = decorator_name(func). Common uses include logging, authentication, caching (lru_cache), and timing.
Q4: What is the difference between deep copy and shallow copy?
A shallow copy (copy.copy or lst[:]) creates a new object but references the same nested objects. A deep copy (copy.deepcopy) recursively copies all nested objects, creating a fully independent clone. For simple flat structures, shallow copy is sufficient; for nested mutable objects, use deep copy to avoid unintended mutations.
Q5: How does Python manage memory?
Python uses reference counting as its primary memory management strategy. Each object tracks how many references point to it; when the count reaches zero, the memory is freed. A cyclic garbage collector handles reference cycles (objects referencing each other). The gc module provides access to the garbage collector, and id() returns the memory address of an object.
Q6: What is a generator and how is it different from a list?
A generator is a lazy iterator that yields one value at a time instead of holding all values in memory. It is created with a yield statement (generator function) or a generator expression (parentheses instead of brackets). Generators are memory-efficient for large datasets since they compute values on demand, but unlike lists they cannot be indexed or reused after exhaustion.
Q7: What is the GIL and how does it affect multithreading?
The Global Interpreter Lock (GIL) is a mutex in CPython that allows only one thread to execute Python bytecode at a time. This means CPU-bound tasks do not get true parallelism with threads. For CPU-bound work, use multiprocessing or concurrent.futures.ProcessPoolExecutor. For I/O-bound tasks (network, disk), threading is still effective because the GIL is released during I/O waits.
Q8: Explain list comprehension vs map vs filter.
List comprehension ([expr for x in iterable if cond]) is the most Pythonic and readable; it always returns a list. map(func, iterable) applies a function to each item and returns a lazy iterator; useful when a named function already exists. filter(func, iterable) returns items where func returns True, also lazy. For simple cases prefer comprehensions; use map/filter when chaining with other iterators for memory efficiency.
Q9: What is the difference between __str__ and __repr__?
__str__ is intended for human-readable output, used by print() and str(). __repr__ is intended for developer-facing output (debugging and logging), used by repr() and the interactive shell. The rule of thumb: __repr__ should ideally return a string that could recreate the object (eval-able), while __str__ should be user-friendly. If only __repr__ is defined, it serves as fallback for __str__ too.
Q10: What are Python namespaces and scope (LEGB rule)?
Python resolves variable names using the LEGB rule in order: Local (inside current function), Enclosing (in enclosing functions for closures), Global (module-level), Built-in (Python built-ins like len and range). The global keyword declares that a variable assignment refers to the global scope. The nonlocal keyword allows modification of an enclosing (but non-global) scope variable from within a nested function.