home
›
cheatsheets
›
backend_development
›
python
Free to read Every card and diagram, and Quiz me
2
Free account Star cards. Your Quiz me marks are kept, so next time you drill just what you missed
3
Pro All 59 answers on this sheet, My sheet across every topic, and PDFs
A free account keeps your stars and Quiz me marks. Pro adds all 59 answers on this sheet, My sheet across every topic, and PDFs.
/
Read
Quiz me
Comfortable
Compact
My misses 0
Click any code to copy it
Done 0 /0
To review 0
Python 2 or an old way, still asked
The usual way today
Newer, Python 3.10 or later
On this sheet
The model
01 Names & objects02 Types & collections03 Syntax, style & loops04 Functions & decorators05 Iterators & generators06 Classes & OOP07 Errors, cleanup & testing08 Modules, files & venvs09 Memory & performance10 Threads, processes & async
The model A name is a label bound to an object. Assignment and function calls copy nothing, so two names can share one object. Mutable objects change in place. Immutable objects can't, so any change builds a new object.CPython compiles source to bytecode, and its virtual machine interprets it. Reference counting frees most objects. The GIL lets one thread run Python bytecode at a time. Threads suit I/O-bound work, and processes suit CPU-bound work.
Names & objects what assignment, copy and == do Compare with == or is Compare values a == b
Compare identity a is b
Tip == calls __eq__, so each class decides what equal means. Use is only for singletons, as in value is None.
Trap CPython reuses small integers, so x is y can be true by accident. Never use is to compare numbers or strings.
Mutability and passing arguments Name the mutable types list, dict, set and most objects of your own classes.
Name the immutable types Numbers, str, tuple, frozenset and bytes.
Change the caller's list items.append(4)
Rebind a parameter items = [0] Only the local name moves.
Tip Python passes by assignment, with no copy. Immutable arguments can only be rebound, so they behave as if passed by value.
Trap A function that changes a mutable argument also changes the caller's object. To protect it, pass items.copy().
Shallow or deep copy original copy inner shallow A new outer list that shares the inner list.original copy inner inner deep The inner list gets copied too, so nothing is shared.Make a shallow copy copy.copy(a) a.copy() a[:]
Make a deep copy copy.deepcopy(a)
Tip deepcopy is slower and uses more memory. It also handles objects that refer to themselves.
Trap After a shallow copy, changing shallow[0][0] changes the original too. The difference shows only with nested, mutable objects.
Local and global scope Look up a name LEGB. Local, enclosing, global, then built-in.
Rebind an outer name global n nonlocal n
Trap Assign to a name anywhere in a function and it's local for the whole body. Reading it first raises UnboundLocalError .
Types & collections pick the right container Built-in types Name the built-in types int, float, complex, str, list, tuple, set, frozenset, dict, bool, NoneType.
Tip Python is dynamically typed. A variable has no fixed type, and type(value) tells you the type of the value it holds.
List or tuple Hold items that change [1, 2, 3]
Hold a fixed group (1, 2)
Tip A tuple is a little faster and smaller, because Python allocates it once at a fixed size.
Trap A list can't be a dict key or a set member. A tuple can, when all its items are hashable.
Dictionaries Read a required key d[key]
Read an optional key d.get(key, default)
Tip A lookup takes O(1) on average. d[key] raises KeyError on a missing key. Since Python 3.7, a dict keeps insertion order.
Trap Keys must be hashable. Strings, numbers and tuples of hashable items work, but lists don't.
Comprehensions Build a filtered list [x*x for x in a if x]
Build a set, a dict or a generator {x for x in a} {k: v for k, v in a} (x for x in a)
Trap Parentheses build a generator expression, not a tuple.
Built-in functions to know Sort a copy, or in place sorted(a) a.sort()
Map, filter or pair items map(f, a) filter(f, a) zip(a, b)
Inspect an object isinstance(x, int) hasattr(x, "y")
Trap map, filter, enumerate and zip return lazy iterators, not lists. Wrap one in list() to see its values.
Syntax, style & loops the shape of a Python file Indentation, PEP 8 and names Mark a code block Indent it. PEP 8 says 4 spaces per level.
Name functions, classes and constants snake_case CapWords UPPER_CASE
Write an identifier Use a letter or _ first, then letters, digits and _. Case matters, and it can't be a keyword.
Tip PEP 8 also limits lines to 79 characters. It orders imports as standard library, third-party, then your own modules.
Trap A wrong indent moves a line into another block or stops the file from running. Python raises IndentationError , or TabError for mixed tabs and spaces.
Conditions and loops Run the first true block if a: elif b: else:
Pick one of two values x if ok else y
Match patterns (3.10+) match value:
Loop over items, or while a condition holds for x in a: while n < 3:
Loop with the index enumerate(a)
End a loop, skip an iteration, or do nothing break continue pass
Tip A condition doesn't need to be a Boolean. Python treats 0, empty collections and None as false.
Trap break exits only the innermost loop. It also skips the loop's else clause, which runs only when no break happened.
Python 2 or Python 3 Print in Python 2 print "hello" Python 3 uses print("hello").
Divide in Python 2 5 / 2 It gave 2. Python 3 gives 2.5, and // does floor division.
Read input in Python 2 raw_input() Python 3 uses input(), which returns a string.
Count lazily in Python 2 xrange(n) Python 3's range() works like it.
Tip Python 2 reached end of life on January 1, 2020. Python 3 strings are Unicode by default, with a separate bytes type.
Trap A range is a lazy sequence, not an iterator. It supports len(), indexing and in, and you can loop over it many times.
Functions & decorators arguments, closures, wrappers Define a function, with a safe default def add(item, items=None): not items=[] if items is None: items = [] a new list on every call items.append(item) return items
Pass by position or name f(1, 2) f(a=1)
Tip A function with no return gives back None.
Trap items=[] creates one list when Python defines the function. Every call that relies on the default shares it.
Argument kinds, *args and **kwargs Make args positional-only def f(a, /):
Make args keyword-only def f(*, timeout):
Collect extras in a tuple and a dict def f(*args, **kwargs):
Spread a list or a dict f(*nums) f(**opts)
Tip Python reads the stars, not the names, so *values works too. / lets authors rename a parameter later without breaking callers.
Closures and lambdas def make_multiplier(factor): def multiply(x): return x * factor remembers factor return multiply
Capture a loop value now lambda n=x: n
Sort by one field key=lambda s: s[1]
Tip A lambda is one expression, with no loops or assignments. A def gets a real name in tracebacks.
Trap Closures use late binding, so they read a variable when they run. Lambdas made in a loop all see its last value.
Decorators def logged(func): @functools.wraps(func) def wrapper(*args, **kwargs): print(func.__name__) return func(*args, **kwargs) return wrapper
Apply a decorator @logged Short for f = logged(f).
Trap Without @functools.wraps(func) , the wrapper hides the original function's name and docstring.
Iterators & generators produce values one at a time Iterable or iterator Walk a list by hand iter(nums) next(it) next raises StopIteration at the end.
Make an object iterable, or an iterator __iter__ __next__
Tip Every iterator is iterable, but not every iterable is an iterator. A for loop calls iter() once, then next().
Trap An iterator is single-use. A list can loop many times, but a generator or a file object is empty after one pass.
Generators and yield Yield a value and pause yield a It keeps its local variables.
Create a generator gen = fib(10) The body doesn't run yet.
Tip Only one value exists at a time, so a generator walks a huge file or an endless stream in constant memory.
Trap A generator is single-pass. Once consumed it's empty, and it has no len() or indexing.
Classes & OOP build objects and share behaviour Classes, objects and variables Set up each new object __init__(self, name)
Create an object p = Person("Alice")
Share across instances school = "ABC" Write it in the class body.
Keep a value per object self.name = name
Tip self is the object the method was called on. Python passes it in for you.
Trap s.school = "x" hides the class variable for s only. A list as a class variable is shared by every instance.
Instance, class and static methods Work on the object def describe(self):
Receive the class @classmethod
Keep a plain helper @staticmethod
Tip A class method is often an alternative constructor, like dict.fromkeys(). On a subclass, it builds that subclass.
Inheritance, super() and overriding Inherit from a class class Dog(Animal): Redefine a method to override it.
Call the next class super().speak()
Call a parent directly Animal.speak(self)
Accept varying arguments def add(self, *args): Python has no overloading. A later def replaces the earlier one.
Tip Use inheritance for a true "is a" relationship. Otherwise prefer composition, where a class holds another.
Trap super() calls the next class in the MRO, Dog.__mro__, not simply the parent. In a diamond, a direct parent call can run a base class twice.
Private names and properties Mark a name internal self._balance Python doesn't enforce it.
Stop subclasses clashing self.__balance Python renames it _Account__balance.
Control attribute access @property @x.setter
Trap Python has no private keyword. Name mangling isn't real privacy, because a._Account__balance still works.
Magic methods Define + and == __add__ __eq__
Give a string to users, or to developers __str__ __repr__
Work with len() and for loops __len__ __iter__
Trap Defining __eq__ without __hash__ makes instances unhashable.
Dataclasses Generate the methods @dataclass
Default to a new list field(default_factory=list)
Make it read-only, comparable or smaller frozen=True order=True slots=True
Tip @dataclass writes __init__, __repr__ and __eq__. __post_init__ runs after it, so validate there.
Trap Fields with defaults must come after fields without them.
Metaclasses and monkey patching Say what a metaclass is class A(metaclass=M): The class of a class. The default is type.
Avoid a metaclass __init_subclass__ Or use a class decorator.
Patch a method at runtime Calc.add = logged mock.patch patch is for tests and restores the original.
Trap Monkey patching is risky in production. The change is global, invisible at the call site, and can break when the library changes.
Errors, cleanup & testing catch errors, run tests try, except, else, finally No error Error
except ValueError as e: Handle an error
else: Run the success path
finally: Clean up in every case
the block runs the block is skipped
Tip finally runs even after return. An exception no except matches triggers it, then keeps propagating.
Trap Python checks except blocks in order, so put the most specific first. A broad except Exception can hide bugs.
Custom exceptions and assert Define and raise your own class E(Exception): raise E("bad age")
Keep the original cause raise E("x") from err
Check an assumption assert n > 0, "msg"
Tip In a larger project, derive every error from one base exception, so callers can catch them all with one except.
Trap python -O removes every assert. Never use one to validate input or check permissions. Raise ValueError instead.
Context managers and with Always close a file with open(p) as f:
Write your own __enter__ __exit__ @contextmanager
Tip The teardown runs even if the block raises. If __exit__ returns a true value, Python suppresses the exception.
Unit tests and debugging Compare values in a test self.assertEqual(a, 5)
Check for an exception with self.assertRaises(E):
Find and run tests python -m unittest pytest
Fake a slow dependency unittest.mock
Pause and step through breakpoint() pdb.set_trace()
Log output you can switch off logger.debug("x")
Tip Read the full traceback first. It names the exception and the line. Prints give a quick look, and logging suits production.
Modules, files & venvs organize code and dependencies Modules, packages and imports Tell a module from a package A module is one .py file. A package is a directory of modules.
Import a module, or give it an alias import math import numpy as np
Import chosen names, or inside a package from math import sqrt from .mod import fn
Set what import * brings in __all__ = ["fn"]
Tip __init__.py runs on first import, so keep it light. A directory without one is a namespace package.
Trap Avoid from math import *. It hides where each name came from.
The __name__ check if __name__ == "__main__": false on import main()
Tip python mymodule.py sets __name__ to "__main__". An import sets it to the module's name, so the block is skipped.
File modes Read, fail if missing open(p, "r")
Write, emptying it first open(p, "w")
Append, create if needed open(p, "a")
Create, fail if it exists open(p, "x")
Tip Add b for bytes, or + to read and write, as in "r+". Open files in a with block so Python closes them.
Trap "w" truncates an existing file to empty. Use "x" to protect existing data.
Virtual environments Create a venv python -m venv .venv
Activate it on Unix or Windows source .venv/bin/activate .venv\Scripts\activate
Record exact versions, then rebuild from them pip freeze > requirements.txt pip install -r requirements.txt
Tip Treat it as disposable. Don't commit it or copy it to another machine. Recreate it from the requirements file.
Threads, processes & async pick a concurrency model Threads, processes and the GIL thread 1 thread 2 process 1 process 2 time CPU-bound work. Threads take turns holding the GIL. Each process has its own GIL, so processes run at once and finish sooner.
Run I/O-bound work ThreadPoolExecutor(4) A waiting thread releases the GIL.
Run CPU-bound work ProcessPoolExecutor(4) It costs more memory, and data between processes gets copied.
Say what the GIL is A CPython mutex. One thread runs bytecode at a time, which keeps built-in types safe.
Run threads without the GIL (3.13+) Use the optional free-threaded build, officially supported from 3.14. It's not the default.
Trap Threads don't speed up CPU-bound Python code, because only one runs at a time. The free-threaded build runs single-threaded code somewhat slower.
async and await task A await task B await task C await time One thread. Solid bars run on the event loop one at a time, and dashed bars wait on I/O. The waits overlap, so the total is close to the slowest.
Define a coroutine, and await it async def f(): await f()
Start the event loop asyncio.run(main())
Run coroutines concurrently asyncio.gather(a(), b())
Cancel related tasks when one fails (3.11+) asyncio.TaskGroup()
Run blocking code asyncio.to_thread(f)
Tip Calling a coroutine function only returns a coroutine object. Nothing runs until you await or schedule it.
Trap A coroutine gives up control only at an await. A blocking call or a long CPU loop freezes every other task.
Nothing matches. Try a shorter word, or clear the search.
Practice 59 interview questions Full answers, from beginner to expert.
Found this useful? Pass it on.
Share on LinkedIn
Share on X
Copy link
This sheet stays free. With Pro , you can:
Every answer on this sheet. Read the full answer to all 59 questions it links, not just the 7 free ones.
My sheet. The cards you star and the ones you miss in Quiz me, from every sheet, on one page to quiz and print.
PDFs. Download this sheet and My sheet, laid out for A4, with every question a link to its answer.
The rest of Pro. Every answer in the question library, and 1,000 AI credits a month.