Python
Python as interviewers actually probe it: the value and container semantics you reason about daily, the function/OOP/decorator machinery, async and the GIL, gradual typing and error handling, and the stdlib, performance, testing, and packaging habits that make code shippable. It is the default language for backend services, data pipelines, and AI/ML work, so almost every stack asks about it.
on this pageshowhide
guide
overview
~2 minPython interviews check whether you can predict what the interpreter will do, not whether you can write a loop. The opening minutes usually go to the object model under everyday code: names bound to objects, which types change in place, and why an aliased list or a default argument surprises people. From there the conversation moves to the machinery that makes code read as Python — functions as values, closures, decorators, generators, and the special methods a class implements to join in with the syntax — and then to concurrency, where the GIL is the first question and choosing between threads, processes and asyncio for a given workload is the real one. Senior rounds widen to what keeps a codebase shippable: gradual typing, the unhappy path, measuring before optimizing, packaging, and how a Python process behaves under a real operating system. The hub follows those seams. The language core is [Core Types and Syntax](/topics/lang-python-core-types), [Control Flow Constructs](/topics/lang-python-control-flow), [Collections and Data Structures](/topics/lang-python-collections), [Functions and Comprehensions](/topics/lang-python-functions-comprehensions), [OOP and Classes](/topics/lang-python-oop) and [Decorators and Generators](/topics/lang-python-decorators-generators). Correctness is [Errors and Context Managers](/topics/lang-python-errors-context), [Type Hints and Static Typing](/topics/lang-python-typing) and [Testing Idioms](/topics/lang-python-testing). Running work at once is [Concurrency and Async](/topics/lang-python-concurrency). The everyday batteries are the [Standard Library](/topics/lang-python-stdlib), with [Security and Safe APIs](/topics/lang-python-security) covering the calls whose defaults bite. The working-engineer layer is what happens after the source is correct: the [Interpreter Execution Model](/topics/lang-python-runtime), [Performance and Memory](/topics/lang-python-performance-memory), [Packaging and Environments](/topics/lang-python-packaging-envs), [Live Process Diagnostics](/topics/lang-python-diagnostics) and [Unix Runtime Behavior](/topics/lang-python-process). Learn the core first and in roughly that order: nearly every later answer assumes you can say which object a name refers to and whether an operation copies it. Add errors and context managers early, because the unhappy path comes up in every round. Leave asyncio until closures, generators and the iterator protocol feel routine — its questions look hard mainly when those are shaky. Typing and testing pay off from mid-level up; the runtime, diagnostics and Unix sections carry the most weight in backend and platform roles, where a question starts from a process that is slow, stuck or dead.
primer
### Names point at objects Assignment never copies. A name is a reference, a call binds each parameter to the object the caller passed, and whether a change shows up elsewhere depends on whether that object is mutable. Identity versus equality, hashability, shallow versus deep copies and the default-argument trap are this one idea seen from different sides, so interviewers open with it. ### Functions and classes are objects too Functions can be stored, passed and returned; a closure keeps the variables it captured; a class is an object created at runtime by executing its body. Decorators, `functools`, class decorators and metaclasses are ordinary code operating on those objects, which means a question about `@` syntax is really a question about when things are evaluated. ### Behaviour comes from protocols, not ancestry Python asks what an object can do rather than what it inherits from. `len()`, `for`, `with`, `in`, `+` and attribute access all dispatch to special methods, and any class that defines the right ones takes part. Iteration, context management, descriptors and the container ABCs are all protocols; static typing mirrors the stance with `Protocol`, where a class matches by shape. ### Laziness is a first-class tool Iterators hand out one item per request, generators suspend mid-function, and much of the standard library consumes and returns iterators rather than lists. Knowing where a pipeline stays lazy and where it quietly materializes decides both peak memory and whether the data can be read a second time. ### One lock shapes the concurrency story In the default CPython build, a global lock serializes bytecode execution, so threads help with waiting but not with pure-Python computing. That drives the model choice: threads for blocking I/O, processes for CPU-heavy work, asyncio for many concurrent waits on one thread with explicit `await` points. Free-threaded builds change the arithmetic, and interviewers increasingly ask what they break. ### Types are checked by tools, not the interpreter Annotations are metadata; a separate checker reads them and reports mismatches before the code runs. The system is gradual — `Any` turns checking off wherever it flows — so the craft lies in where to be precise, how narrowing follows control flow, and when a Protocol fits better than a base class. ### Failure and cleanup are explicit Exceptions form a class hierarchy, and the type you catch decides what escapes: a handler that is too broad swallows interrupts and bugs alike. `with` ties cleanup to a block so it runs on every exit path, which matters because CPython's prompt freeing of unreferenced objects is an implementation behaviour, not a language promise. ### CPython is the implementation you are asked about Most performance, memory and runtime questions assume CPython: reference counting backed by a cycle collector, source compiled to bytecode and run in frames, C code behind the builtins. Making Python fast usually means pushing the hot loop into C — a builtin, a standard-library routine, a native extension — after a profiler has said where the time actually goes.
- Name binding
- The link between an identifier and an object in a namespace; assignment, import, def, class and for all create bindings, and none of them copies the object.
- Mutability
- Whether an object's value can change after creation; lists, dicts and sets can, while ints, strings, tuples and frozensets cannot.
- Hashable
- Having a hash that never changes during the object's lifetime and an equality consistent with it; required of dict keys and set members.
- Special method
- A method named with double underscores on both sides that the interpreter calls to implement an operator, a builtin or a statement; also called a dunder method.
- Closure
- A nested function together with the variables it captured from an enclosing function, which stay alive as long as the inner function does.
- LEGB rule
- Python's name lookup order: local, enclosing function, global, then builtins. Assigning to a name makes it local unless declared global or nonlocal.
- Decorator
- A callable applied to a function or class at definition time; whatever it returns replaces the original under the same name.
- Iterator protocol
- The contract behind for loops: an iterable produces an iterator, and the iterator returns successive items until it signals exhaustion by raising StopIteration.
- Generator
- A function containing yield; calling it returns an iterator that runs the body in steps, pausing at each yield with its local state intact.
- Descriptor
- An object stored on a class that defines get, set or delete hooks and so controls attribute access; properties, methods and slots are built on it.
- Method resolution order
- The linearized list of classes, computed with the C3 algorithm, that attribute lookup searches and that super() follows; abbreviated MRO.
- Metaclass
- The class of a class, which controls how class objects are created; type is the default.
- Dataclass
- A class whose annotated fields let a decorator generate the initializer, repr and equality, plus ordering, hashing and immutability on request.
- Context manager
- An object that sets something up when a with block is entered and tears it down when the block exits, however it exits.
- GIL
- The global interpreter lock of standard CPython builds: a mutex a thread must hold to run bytecode, which serializes pure-Python execution across threads.
- Free-threaded build
- A CPython build without the global interpreter lock, letting threads execute Python code in parallel, at some single-threaded cost and with extension compatibility caveats.
- Event loop
- The asyncio scheduler that runs coroutines on one thread, resuming each when the I/O, timer or future it awaits is ready.
- Coroutine
- The object an async def function produces; it makes progress only when awaited or wrapped in a task, and gives up control at each await.
- Type checker
- A static analysis tool, such as mypy or pyright, that reads annotations and reports type errors without running the program.
- Protocol
- A typing construct for structural subtyping: any class with the listed methods and attributes satisfies it, with no inheritance required.
- Reference counting
- CPython's primary memory management: each object counts the references to it and is freed when the count reaches zero; a separate collector reclaims cycles.
- Virtual environment
- An isolated directory with its own interpreter entry point and site-packages, so each project installs dependencies without touching the system Python.
- Wheel
- The built distribution format for Python packages: an archive installed by unpacking, so the target machine needs no build step.
The sections form layers. At the bottom is the data model — [Core Types and Syntax](/topics/lang-python-core-types), [Collections](/topics/lang-python-collections) and [Control Flow](/topics/lang-python-control-flow) — which fixes what a value is, what it costs to store and search, and how code branches and loops over it. [Functions and Comprehensions](/topics/lang-python-functions-comprehensions) and [OOP and Classes](/topics/lang-python-oop) sit on top: a function is an object carrying its closure, and a class is an object whose special methods hook into the syntax the first layer defines. [Decorators and Generators](/topics/lang-python-decorators-generators) is where those two meet — a decorator is a higher-order function applied at definition time, a generator is a function that implements the iterator protocol for you — so gaps in either show up there first. The correctness sections run across the core rather than above it. [Errors and Context Managers](/topics/lang-python-errors-context) governs every exit path, and `with` is itself a protocol, often backed by a generator. [Type Hints](/topics/lang-python-typing) describes the same object model statically: Protocols formalize duck typing, typed dicts and dataclasses give shapes to records, generics describe containers. [Testing](/topics/lang-python-testing) leans on both, because patching is attribute rebinding and a mock object invents attributes on demand unless given a spec. Concurrency reuses all of it. asyncio turns the suspension generators introduced into a scheduling model, and its task groups apply the error layer to many tasks at once; [Executors and Sync/Async Interop](/topics/lang-python-concurrency-executors-interop) is where blocking code meets the event loop. The outer ring is the running process — [the interpreter](/topics/lang-python-runtime), [performance](/topics/lang-python-performance-memory), [packaging](/topics/lang-python-packaging-envs), [diagnostics](/topics/lang-python-diagnostics) and [Unix behaviour](/topics/lang-python-process) — where the questions start from an import that fails, a service that is slow, or a worker that will not exit. The [Standard Library](/topics/lang-python-stdlib) and [Security](/topics/lang-python-security) cut across every layer: most security questions are about a standard-library call used with its default settings. A small example where several layers meet: ```python from collections.abc import Iterable, Iterator from dataclasses import dataclass @dataclass(frozen=True) class Reading: sensor: str value: float def parse(lines: Iterable[str]) -> Iterator[Reading]: for line in lines: sensor, _, raw = line.partition(",") if raw.strip(): yield Reading(sensor, float(raw)) with open("readings.csv", encoding="utf-8") as fh: peak = max(parse(fh), key=lambda r: r.value, default=None) ``` The file object is already a lazy iterator of lines and the generator keeps it lazy, so memory stays flat however large the file is. The frozen dataclass is a hashable value type with generated equality. The annotations document the contract for a checker but are never enforced by the interpreter. `max` with a key function consumes the stream once, and the `with` block closes the file on every exit path — including a `ValueError` from a malformed number, which a real parser would have to decide how to handle.
- Core Types and Syntax →
Binding, mutability, identity and copying explain most of Python's surprises, and every other section assumes you can reason about them.
- Collections and Data Structures →
Lists, dicts and sets are the daily workhorses; interviewers probe their costs and hashing rules early and often.
- Functions and Comprehensions →
Parameter kinds, scoping, closures and comprehensions are the evaluation model that decorators, generators and asyncio are built from.
- OOP and Classes →
Construction, the MRO, special methods and dataclasses: how your own types join in with Python's syntax and builtins.
- Decorators and Generators →
Decorators and the iterator protocol turn functions into tools; learn them before asyncio, which reuses suspension and wrapping.
- Concurrency and Async →
With the core solid, take the GIL, threads, processes and asyncio, then practise defending one model for a concrete workload.
Explaining a surprise as pass-by-reference or pass-by-value: neither describes Python, and interviewers want the binding model from Core Types and Syntax.
Using
isto compare numbers or strings because it worked in the REPL: object caching is an implementation detail, and the same test fails for other values.Checking membership against a list inside a loop: every check scans the list, so the loop turns quadratic where a set would keep it linear.
Reading an iterator or generator twice and expecting the same items: the second pass sees nothing, often because a debug print consumed it first.
Writing a decorator without
functools.wraps: the wrapper's name and docstring replace the original's, which confuses logging, introspection and tools that rely on them.Adding threads to speed up a CPU-bound loop on a standard CPython build: the lock serializes the work, so the answer is processes or native code.
Calling a blocking function inside a coroutine: nothing else on that event loop runs until it returns; hand it to an executor or use an async client.
Wrapping a large block in a broad
exceptthat logs nothing: real bugs disappear along with the expected failure, and the traceback that explained them is gone.Treating annotations as runtime validation: a wrong type passes straight through unless a checker runs in CI or a library validates explicitly.
Optimizing before profiling: guesses about which part of Python code is slow are often wrong, and interviewers expect a measurement before a rewrite.
Installing into the system interpreter or an unpinned environment: builds stop being reproducible, and a transitive upgrade can break production with no code change.
Building shell commands or SQL by formatting untrusted input into a string: the data becomes syntax; see Security and Safe APIs.
This guide assumes CPython 3.12 or later. Most answers read the same on 3.9–3.11, but interviewers still ask about the releases that changed how everyday code is written: - **3.7** — dicts preserving insertion order became a language guarantee (CPython 3.6 already behaved that way); dataclasses, `contextvars` and `asyncio.run` arrived. - **3.8** — assignment expressions (the walrus operator) and positional-only parameters. - **3.9** — built-in collections usable as generics in annotations, and `zoneinfo`. - **3.10** — structural pattern matching and the `X | Y` union syntax. - **3.11** — exception groups with `except*`, `asyncio.TaskGroup`, and a large interpreter speed-up from the Faster CPython work. - **3.12** — dedicated syntax for type parameters and the `type` statement. - **3.13** — an experimental free-threaded build and an experimental JIT compiler. - **3.14** — the free-threaded build became officially supported, though still an opt-in build rather than the default, and annotations are evaluated lazily. The typing spelling is the change that most confuses candidates reading older material: ```python # 3.8-era spelling from typing import List, Optional, TypeVar T = TypeVar("T") def first(items: List[T]) -> Optional[T]: ... # 3.12 onward def first[T](items: list[T]) -> T | None: ... ``` The meaning did not change, only the syntax. Python 2 reached end of life in 2020; questions about it are now about migrating legacy code, not writing it.
CPython, the reference implementation, is developed by the core developers under the Python Software Foundation, and interviewers expect you to place the language in three settings. On the **backend**, it runs web services through Django, Flask and FastAPI, and pre-fork application servers are the usual way to use more than one core. In **data and machine learning**, it is the glue language over native libraries such as NumPy, pandas and PyTorch: the Python layer orchestrates, and the heavy numeric work runs in compiled code. In **automation and tooling**, it replaces shell scripts once they need data structures, tests or error handling. Around the language sits a tooling layer an interviewer may ask you to choose from: pip, venv, Poetry and uv for environments and dependencies; pytest as the common third-party runner beside the standard library's `unittest`; mypy and pyright for type checking; Ruff for linting and formatting. PyPy is an alternative implementation with a JIT, and Cython or Rust extensions are the usual native escape hatches. The comparisons that come up most are with **Go** and **Java** for services — Python trades raw throughput and a single deployable artifact for development speed and library reach — and with **R** for analysis, where Python wins on general-purpose engineering and R on statistical depth.
explore
- Core Types and Syntax123 questions
- Numbers and Booleans25 questions
- Strings and Bytes22 questions
- Mutability, Identity and Copying20 questions
- Sequence Semantics19 questions
- Truthiness and Comparisons18 questions
- Encoding and Normalization19 questions
- Functions and Comprehensions99 questions
- Parameters and Arguments21 questions
- Closures and Scoping19 questions
- Lambda and Higher-Order Functions19 questions
- The functools Toolkit17 questions
- Comprehensions and Generator Expressions16 questions
- Recursive Calls7 questions
- OOP and Classes128 questions
- Construction and Attributes19 questions
- Inheritance and the MRO20 questions
- Dunder Protocols22 questions
- Dataclasses and Value Types16 questions
- Descriptors and Attribute Access16 questions
- Metaclasses, ABCs and Class Machinery12 questions
- Symbolic Constant Types11 questions
- Copy and Pickle Hooks12 questions
- Decorators and Generators93 questions
- Decorator Mechanics34 questions
- The Iterator Protocol10 questions
- Generator Functions23 questions
- itertools Patterns15 questions
- Iteration Builtins11 questions
- Concurrency and Async127 questions
- The GIL and Threading20 questions
- Multiprocessing20 questions
- Asyncio Core34 questions
- Async Language Constructs12 questions
- Executors and Sync/Async Interop18 questions
- Ambient State Propagation12 questions
- True Parallel Execution11 questions
- Packaging and Environments118 questions
- The Import System34 questions
- Environments and Dependencies30 questions
- Distribution and Library Layout47 questions
- Release Targets and Upgrades7 questions
- Collections and Data Structures102 questions
- Lists and Tuples17 questions
- Dictionaries19 questions
- Sets and Frozensets12 questions
- The collections Module19 questions
- Sorting and Ordered Access12 questions
- Custom Container Types16 questions
- Compact Binary Sequences7 questions
- Type Hints and Static Typing110 questions
- Writing Annotations15 questions
- Generics and Variance24 questions
- Structural Typing and Protocols12 questions
- Typed Data Shapes16 questions
- Narrowing and Overloads12 questions
- Gradual Typing in Practice12 questions
- Annotations at Runtime12 questions
- Class Objects and Overrides7 questions
- Errors and Context Managers100 questions
- The Exception Model31 questions
- Exception Groups4 questions
- Context Managers and the with Statement23 questions
- Assertions and the -O Flag3 questions
- The warnings Module and Filters4 questions
- Handler Syntax Forms7 questions
- Finalization and Cleanup Order7 questions
- Failures at API Boundaries21 questions
- Standard Library156 questions
- Text Processing and Regular Expressions20 questions
- Files, Paths, and I/O20 questions
- Data Formats: JSON, CSV, and Pickle20 questions
- Dates, Times, and Time Zones19 questions
- OS Interaction, Processes, and CLIs13 questions
- Logging16 questions
- Binary Data and Buffers16 questions
- Wire-Level Networking12 questions
- Embedded Persistence8 questions
- Numbers and Precision12 questions
- Performance and Memory91 questions
- Memory Model23 questions
- Profiling and Benchmarking23 questions
- Optimization Idioms17 questions
- Execution and Acceleration16 questions
- Native Extension Boundary12 questions
- Testing Idioms73 questions
- unittest (Standard Library)25 questions
- unittest.mock23 questions
- doctest4 questions
- Python Test Design21 questions
- Security and Safe APIs97 questions
- Untrusted Input Handling28 questions
- Dynamic Execution Risks16 questions
- Host Attack Surfaces19 questions
- Cryptographic Primitives22 questions
- Information Disclosure12 questions
- Control Flow Constructs65 questions
- Iteration Statements27 questions
- Decision Structures16 questions
- Destructuring Syntax22 questions
- Interpreter Execution Model77 questions
- Compilation Pipeline16 questions
- Live Call Stack7 questions
- Reflection Over Objects12 questions
- Invocation and Environment22 questions
- Process Lifetime20 questions
- Live Process Diagnostics63 questions
- Stopping and Stepping12 questions
- Crashes and Hangs16 questions
- Instrumentation Hooks15 questions
- Error Reports12 questions
- Runtime Check Switches8 questions
- Unix Runtime Behavior95 questions
- Signals and Interrupts20 questions
- Spawning and Reaping Children15 questions
- Duplicating an Address Space13 questions
- Kernel Quota Exhaustion16 questions
- Long-Lived Service Patterns20 questions
- Standard Streams11 questions
- AI & Data Scientistroleanchors this topic
- AI Engineerroleanchors this topic
- AI Red Teamingroleanchors this topic
- Blockchain Developerroleanchors this topic
- Cyber Security Expertroleanchors this topic
- Data Analystroleanchors this topic
- Data Engineerroleanchors this topic
- DevSecOps Engineerroleanchors this topic
- Game Developerroleanchors this topic
- MLOps Engineerroleanchors this topic
- Machine Learning Engineerroleanchors this topic
- Network Engineerroleanchors this topic
- Pythonskillanchors this topic
- BI Analystrole
questions
1,717 · 17 sectionsWhat does `a < b < c` mean in Python, and how many times is `b` evaluated?
basics
~10 sPython expands a < b < c into a < b and b < c, but evaluates the middle expression exactly once. If the first comparison is false, the second comparison is never performed.
Why does `b = a` on a Python list leave both names pointing at one object?
basics
~20 sAssignment never copies in Python; it binds a second name to the same object. After b = a both names label one list, so b.append(4) is visible through a. Duplicate explicitly with a.copy(), a[:] or list(a).
Why use decimal.Decimal instead of float for currency amounts in Python?
basics
~20 sA float stores a binary fraction, so an amount written as 0.10 is not held exactly and the tiny errors accumulate as you add. decimal.Decimal stores base-10 digits, so 0.10 built from the string '0.10' is exact and money totals stay exact.
Why does 0.1 + 0.2 == 0.3 evaluate to False in Python?
basics
~20 sPython's float is IEEE-754 binary64, which stores exactly only fractions with a power-of-two denominator. 0.1, 0.2 and 0.3 each become nearby approximations, and the sum of the first two lands one step above the stored 0.3.
What is the difference between `is` and `==` in Python?
basics
~20 sis asks whether two names are bound to one and the same object, the identity test behind id(). == asks whether the objects compare equal, dispatching to eq. Compare values with ==; reserve is for None, True, False and sentinels.
What do *args and **kwargs collect in a Python function definition?
basics
~10 s*args collects the leftover positional arguments into a tuple; **kwargs collects the leftover keyword arguments into a dict. Both types are fixed, and both are empty rather than None when nothing extra is passed.
What does functools.lru_cache do to a function, and how does functools.cache differ?
basics
~20 sfunctools.lru_cache stores each call's result in a dictionary keyed by the arguments, so a repeat call returns the stored value without running the body. functools.cache, added in 3.9, is lru_cache(maxsize=None): unbounded, and it never evicts.
What is a free variable in a Python nested function, and how does a cell keep it alive?
basics
~20 sA free variable is a name an inner function reads but does not bind, whose binding belongs to an enclosing function. CPython moves that variable out of the frame into a one-slot cell object, and the inner function holds the same cell, so the value outlives the enclosing call.
Why does UnboundLocalError appear when a function does `x = x + 1` and x is a module-level global?
basics
~20 sPython decides scope at compile time: an assignment to x anywhere in the body makes x local for the whole function, so the read on the right-hand side hits an unset local slot instead of the module-level global.
In the comprehension [x for row in rows for x in row], which for clause is the outer loop?
basics
~20 sThe leftmost one. Clauses read left to right in the same order you would write nested for statements, so for row in rows is the outer loop and for x in row the inner one. The result is one flat list.
What is the difference between a class attribute and an instance attribute in Python?
basics
~20 sA class attribute is created in the class body and stored on the class, so every instance sees the same object. An instance attribute is created by assigning to self.name and lives in that one object's own namespace.
How do a plain method, a @classmethod and a @staticmethod differ in Python?
basics
~20 sA plain method takes the instance as its first argument, self. A classmethod takes the class as cls, so a subclass gets its own class. A staticmethod receives nothing implicitly; it is just a function stored in the class namespace.
In Python, what does `_name` mean versus `__name` on a class attribute?
basics
~20 sA single leading underscore is pure convention: it marks an attribute as internal and the interpreter ignores it. A double leading underscore inside a class body is rewritten by the compiler to _ClassName__name, which avoids subclass collisions rather than blocking access.
In a typing.NamedTuple class, how do you give fields defaults and add methods?
basics
~20 sWrite the record as a class: an annotated name with an assigned value becomes that field's default, and ordinary def statements become real methods on the generated tuple subclass. Defaulted fields must come last, exactly as with function parameters.
What does the @dataclass decorator generate, and which class attributes become fields?
basics
~10 s@dataclass writes init, repr and eq from the class's annotated attributes, in declaration order. Only annotated names become fields; a plain assignment with no annotation stays an ordinary class attribute.
What does the @ decorator syntax above a Python def actually do?
basics
~20 s@deco written above def f() is shorthand for defining f and then rebinding that name: f = deco(f). A decorator is any callable that takes the function object and returns the object the name will point at.
What do the maxsize and typed arguments to functools.lru_cache control?
basics
~20 smaxsize caps how many results are stored - 128 by default, None for unbounded, 0 for none - and a full table drops the least recently used entry. typed=True adds argument types to the key, so 1 and 1.0 stop sharing.
What object does a Python class decorator receive, and what does the class name end up bound to?
basics
~20 sA class decorator is called with the finished class object, after the class body has already executed, and whatever it returns is bound to the class name. Most decorators mutate the class and return the same object.
Why must a decorator wrapping an `async def` function define its wrapper with `async def` and await inside?
basics
~20 sCalling an async def function runs none of its body; it returns a coroutine object. A plain def wrapper only holds that unstarted coroutine, so anything it does around the call observes nothing. The wrapper must be async def and await inside.
Why does @retry(times=3) need one more function layer than a bare @retry?
basics
~20 s@retry(times=3) calls retry(times=3) first and decorates with whatever that call returns. So retry must be a factory that returns a decorator, which then takes the function and returns the wrapper - three nested levels instead of two.
What does Python's `async with` statement do that a plain `with` cannot, and which methods does it call?
basics
~10 sasync with awaits aenter before the block and awaits aexit after it, so setup and teardown can perform I/O. A plain with calls enter and exit synchronously and cannot await anything.
Why use asyncio.Lock instead of threading.Lock inside a coroutine?
basics
~20 sasyncio.Lock suspends only the awaiting task, so the event loop keeps running everything else. threading.Lock blocks the entire thread the loop runs on, freezing every task on that loop — including the one that would release the lock.
What must an object implement for Python's `async for` to iterate it?
basics
~10 sIts type must define __aiter__, an ordinary method returning an async iterator, and that iterator's type must define __anext__, a coroutine that resolves to the next item or raises StopAsyncIteration when the stream ends.
Why does asyncio.create_task() require you to keep a reference to the Task it returns?
basics
~20 sThe event loop registers running tasks only in a weak set, so the Task object create_task() hands back may be the only strong reference. Drop it and the task can be garbage-collected while still suspended, and the work never finishes.
When asyncio.wait_for() times out, what happens to the wrapped coroutine?
basics
~10 sasyncio.wait_for cancels the wrapped awaitable when the deadline passes, waits for that cancellation to finish unwinding, then raises TimeoutError to the caller. The inner work is stopped, not left running in the background.
What does a `[project.scripts]` entry in pyproject.toml create when the package is installed?
basics
~20 sIt declares a console command. At install time the installer generates a small wrapper in the environment's script directory that imports the named module and calls the named function, so typing the command runs your code.
What does `pip install mypkg[redis]` do, and where is that extra declared?
basics
~20 sIt installs mypkg itself plus the optional dependency set named redis. That set is declared in the project's pyproject.toml under the [project.optional-dependencies] table, where each key is an extra name and its value is a list of requirement strings.
Why does pip compile a package with a C extension from source, and what does that build need?
basics
~20 sBecause no prebuilt wheel matched this machine's interpreter version, ABI and platform, pip fell back to the source archive. Compiling it needs a C compiler and linker, CPython's development headers (Python.h), and the project's declared build backend.
How do you ship a JSON schema file inside your Python package's wheel?
basics
~20 sPut the file inside the importable package directory and declare it as package data in your build backend's configuration. Setuptools needs a package-data entry, or include-package-data together with MANIFEST.in; hatchling and flit ship non-code files under the package by default.
What does a single leading underscore on a Python module or class attribute name mean?
basics
~20 sA single leading underscore marks a name as internal: not part of the public API and free to change without notice. It is a convention, not enforcement — outside code can still import and call it.
How does array.array store integers differently from a Python list?
basics
~20 sA Python list stores pointers to full int objects scattered on the heap. array.array stores raw machine values of one typecode packed back to back, so it holds only numbers of that type and costs far less memory.
Why does slicing a memoryview of a bytearray avoid the copy that slicing the bytearray itself makes?
basics
~20 sSlicing a bytearray allocates a second bytearray and copies the bytes. A memoryview slice only records an offset and length into the same storage, so it costs the same tiny amount whatever the slice size.
Which special methods let a custom class support len(obj), obj[k] and obj[k] = v?
basics
~10 sPython calls len for len(obj), getitem for reading obj[k], and setitem for assigning obj[k] = v. All three are looked up on the type, not the instance, and len must return a non-negative int.
How do `dict.get` and `dict.setdefault` differ from indexing a Python dict with `d[key]`?
basics
~20 sIndexing a missing key raises KeyError. dict.get returns a default instead - None unless you pass one - and never changes the dict. dict.setdefault returns the existing value or inserts your default and returns that, so it mutates.
Why does using a list as a dict key raise TypeError, and what works instead?
basics
~20 sA dict key must be hashable. Lists deliberately have no hash, because their contents can change and a key's hash must stay stable, so Python raises TypeError. Use a tuple of hashable items, or a frozenset when order is irrelevant.
How do `typing.Any` and `object` differ as annotations to a type checker?
basics
~20 sAny switches checking off for a value: any attribute, call or assignment is allowed. object is the top type, so every value fits it, but you may use only what every object supports until you narrow with isinstance.
What does `Optional[str]` mean in a Python type annotation?
basics
~20 sOptional[str] from the typing module means the value is either a str or None - exactly the union str | None. It says nothing about omitting an argument: such a parameter is still required unless it also has a default.
Are Python type annotations enforced at runtime, and what does the interpreter do with them?
basics
~20 sNo. CPython never compares a value against its annotation. It only records annotations as metadata in the annotations dictionary on functions, classes and modules; enforcement comes from a separate static type checker or from library code that reads them.
In Python typing, how does a parameter annotated `type[Digest]` differ from one annotated `Digest`?
basics
~10 stype[Digest] accepts the class object itself, Digest or any subclass, so the function can call it to build instances. A bare Digest annotation accepts an already-built instance instead.
What does typing.TypedDict express that a plain dict[str, str] annotation cannot?
basics
~20 styping.TypedDict names each key of a dictionary and gives every key its own value type, so a type checker can flag a misspelled, missing or undeclared key. dict[str, str] only says string keys, string values.
What does Python's `assert` statement do, and what happens to it under `python -O`?
basics
~20 sassert expr, msg raises AssertionError with msg when expr is falsy and does nothing otherwise. Starting the interpreter with -O, or with PYTHONOPTIMIZE set, removes every assert statement at compile time, so the check never runs.
What does logging.exception() record that logging.error() alone does not?
basics
~10 slogging.exception() logs at ERROR level and attaches the traceback of the exception currently being handled, because it passes exc_info=True for you. logging.error() logs only the message unless you pass exc_info=True yourself.
Why translate a third-party library's exception into your own exception class at a module boundary?
basics
~20 sSo callers depend on your abstraction rather than on the library. If a database driver's error class escapes your function, every caller must import that driver to catch it, and replacing the driver breaks all of them.
How does contextlib.asynccontextmanager turn an async generator function into an async context manager?
basics
~20 scontextlib.asynccontextmanager decorates an async generator function that yields exactly once. Calling it builds an object for use with async with: everything before the yield is the setup, the yielded value is what as binds, and everything after the yield is the teardown.
How does @contextlib.contextmanager turn a generator function into a context manager?
basics
~10 scontextlib.contextmanager decorates a generator function that yields exactly once. Everything before the yield runs on entry, the yielded value is what the as clause binds, and everything after the yield runs on exit.
Why does base64.b64encode reject a str, and what do you write instead?
basics
~10 sbase64.b64encode is a bytes-to-bytes function: it needs a bytes-like object and returns bytes. Encode text first with str.encode('utf-8'), then call .decode('ascii') on the result to get a str for JSON.
How do struct.pack and struct.unpack convert Python values to and from bytes?
basics
~20 sstruct.pack takes a format string plus values and returns a bytes object laid out exactly as that format describes. struct.unpack reverses it and always returns a tuple, even when the format has a single field.
How do Python's uuid.uuid4, uuid.uuid1 and uuid.uuid5 differ in where their bits come from?
basics
~20 suuid.uuid4 fills 122 bits from the operating system's random source. uuid.uuid1 encodes a timestamp plus the host's 48-bit node address. uuid.uuid5 hashes a namespace UUID together with a name, so the same name always produces the same UUID.
Why is str.split(',') wrong for parsing a CSV line, and what does csv.reader do instead?
basics
~20 sA comma inside a quoted field is data, not a separator, so str.split(',') tears such a row apart. csv.reader runs the quoting state machine over the file object and yields each record as a list of strings.
How do json.load, json.loads, json.dump and json.dumps differ?
basics
~20 sThe trailing s means string. json.loads parses JSON text already in memory and json.load reads it from an open file object; json.dumps returns a JSON string, while json.dump writes that text straight into a file object.
Why can reference counting alone never free two Python objects that point at each other?
basics
~20 sEach object in the cycle holds a reference to the other, so neither count drops to zero even after every outside name is gone. CPython runs a second, tracing collector — the gc module — to find and free such groups.
In CPython, what does `del x` actually do — does it free the object?
basics
~20 sdel x only unbinds the name x from its namespace and drops one reference to the object. CPython deallocates the object at the moment its reference count reaches zero, so if a list, attribute or another name still holds it, nothing is freed.
What does weakref.ref give you that an ordinary Python reference does not?
basics
~10 sA weakref.ref points at an object without counting toward the references that keep it alive. Call the ref like a function to get the object back, or None once the object has been collected.
Why slice a memoryview of a large bytes object instead of the bytes itself?
basics
~20 sSlicing a bytes object allocates a new bytes and copies the requested range. A memoryview exposes the same memory through the buffer protocol, so slicing it returns another view over the original block in constant time, copying nothing.
What happens to the GIL while zlib.compress runs on a large buffer?
basics
~20 sThe C code inside zlib drops the global interpreter lock before it starts deflating and takes it back when the call finishes. Other Python threads run real bytecode during that window, so threaded compression genuinely overlaps.
Why does a test that calls an async def function without awaiting it still pass?
basics
~20 sCalling an async def function only builds a coroutine object; the body never runs. Assertions then inspect that object rather than a result, so the test passes vacuously. CPython only warns, at garbage-collection time, that the coroutine was never awaited.
Why is a collaborator constructed inside a Python class harder to test than one passed in?
basics
~20 sA collaborator built inside the class gives a test no way in, so the only override left is patching the name the module imported. Take the collaborator as a constructor parameter and the test simply passes a small fake object.
Why does assigning over a module attribute in a test leak into later tests?
basics
~20 sAn imported module exists once per process, cached in sys.modules, so rebinding one of its attributes changes it for every user until something restores it. A plain assignment has no rollback, and a failing assertion skips the restore line.
What does Python's doctest module treat as a test inside a docstring?
basics
~20 sAny docstring line that starts with the >>> prompt. doctest runs that statement and compares whatever it prints to standard output against the lines beneath it, character for character, up to the first blank line.
What does calling a unittest.mock.AsyncMock return, and how does that differ from Mock?
basics
~10 sCalling an AsyncMock runs nothing and hands back a coroutine object; awaiting that coroutine produces the mock's return_value. A plain Mock returns its return_value immediately, and awaiting a Mock raises TypeError.
How does hashlib's update, digest and hexdigest cycle hash data incrementally?
basics
~20 sA hashlib hash object accumulates bytes across repeated update() calls, so feeding data in chunks matches hashing it in one call. digest() returns the raw bytes, hexdigest() the same value as a hex string; reading either does not reset the object.
Which hashlib functions are built for password storage, and why not hashlib.sha256?
basics
~20 shashlib offers two password functions: pbkdf2_hmac and scrypt. Both take a per-user salt and a work factor you choose, so one guess costs real time. hashlib.sha256 is built to be fast, which is exactly wrong for passwords.
Why must a password-reset token come from `secrets`, not `random`?
basics
~20 sThe random module's default generator is a Mersenne Twister whose entire internal state can be reconstructed from a few hundred observed outputs, so later tokens become predictable. secrets draws each token from the operating system's cryptographic source instead.
Why does `hmac.compare_digest` exist when `==` already compares two bytes objects?
basics
~20 s== on two bytes objects stops at the first differing byte, so the time it takes reveals how much of a secret an attacker guessed right. hmac.compare_digest always does the same work; secrets.compare_digest is the same function.
What does setting ssl.SSLContext.verify_mode to ssl.CERT_NONE actually give up?
basics
~10 sIt turns off certificate verification, so a Python client accepts any certificate at all, including one an attacker generated. The connection stays encrypted but is no longer authenticated, which defeats the point of TLS.
In Python's `a if cond else b`, what is evaluated and in what order?
basics
~20 sPython evaluates cond first. If it is truthy, only a is evaluated and becomes the value; otherwise only b is. The branch that is not chosen never runs, so its side effects and its exceptions never happen.
Why does a Python dict dispatch table store `handler` rather than `handler()` as its values?
basics
~20 sA dict literal evaluates every value eagerly while the dict is being built, so handler() would run the handler right there and store its return value. Storing the bare name keeps the function object, which you call after the lookup.
How does Python decide which branch of an if/elif/else chain executes?
basics
~10 sConditions are tested top to bottom and the first truthy one wins: its block runs and the whole rest of the chain is skipped. If none is true, the optional else block runs.
What does Python's walrus operator `:=` do, and how does it differ from `=`?
basics
~20 s:= is an assignment expression: it binds a name and also evaluates to the assigned value, so it can be used inside a condition or a call. Plain = is a statement, which produces no value at all.
What is the difference between `break` and `continue` in a Python loop?
basics
~20 sbreak ends the innermost enclosing loop immediately and control resumes after it. continue abandons only the rest of the current pass and moves on to the next item or condition test. Neither statement touches an outer loop.
What is the difference between Python's eval() and exec()?
basics
~10 seval() evaluates one expression and returns its value. exec() runs statements - assignments, loops, imports, definitions - and always returns None. Hand eval() a statement and it raises SyntaxError.
What is Python's __pycache__ directory and when does CPython reuse a .pyc file?
basics
~20 sCPython caches each module's compiled bytecode in a pycache directory next to the source. It reuses that .pyc only when the magic number matches the interpreter and the recorded source timestamp and size still match.
Why does a Python set of strings iterate in a different order on each run?
basics
~20 sCPython salts str and bytes hashes with a seed picked at interpreter start-up, so the same strings land in different slots of the set's hash table each run. Set iteration follows slot order, so the order changes.
Why does a local file named logging.py shadow the standard library's logging module?
basics
~10 sThe interpreter prepends the running script's own directory to sys.path, so import searches it before the standard library and takes the first match. Run with -P, or set PYTHONSAFEPATH=1, to drop that prepended entry.
What does Python's -O flag actually do to assert statements and __debug__?
basics
~20 sRunning the interpreter with -O compiles away every assert statement and sets the built-in constant debug to False, so if debug blocks vanish too. It performs no other optimization and does not make code faster.
How does warnings.warn() differ from raising an exception in Python?
basics
~20 swarnings.warn() reports a problem without stopping execution: the call returns and the code keeps running. It also passes through the filter list in warnings.filters first, so the same call may print, be silenced, or be turned into a raised exception.
What does Python's "coroutine was never awaited" RuntimeWarning mean?
basics
~20 sCalling an async def function only builds a coroutine object; none of its body runs until something awaits it or wraps it in a task. When that unstarted object is garbage collected, CPython emits the warning.
What causes a Python RecursionError, and what do sys.getrecursionlimit and sys.setrecursionlimit control?
basics
~20 sCPython counts the Python frames stacked on the current thread and raises RecursionError once that count passes the ceiling sys.getrecursionlimit() reports, which is 1000 on a fresh interpreter. sys.setrecursionlimit() moves the ceiling; it does not enlarge the thread's real stack.
What does Python's built-in breakpoint() do, and how does PYTHONBREAKPOINT control it?
basics
~20 sCalling breakpoint() pauses the program and drops into a debugger. It dispatches through sys.breakpointhook, which by default calls pdb.set_trace. Setting PYTHONBREAKPOINT=0 makes every call a no-op; setting it to a dotted callable path routes calls there instead.
In a Python traceback, what do the `~~~^^^` marks under the source line point at?
basics
~10 sThey mark the exact sub-expression that failed. Since Python 3.11 (PEP 657) every bytecode instruction carries start and end column offsets, so the traceback underlines the failing operation instead of blaming the whole line.
Why can open() without a with block leak a file descriptor in Python?
basics
~20 sA file object holds a kernel descriptor until something closes it. CPython usually closes it when the last reference disappears, but that is a refcounting implementation detail. A with block closes deterministically, even when the body raises.
Why do pre-fork Python application servers run several worker processes instead of one?
basics
~20 sBecause one CPython process executes Python bytecode on one core at a time under the global interpreter lock. Forking several workers, each with its own interpreter and its own lock, puts real work on every core and isolates a crash.
How do you bound a blocking call with signal.alarm when it takes no timeout?
basics
~10 sInstall a SIGALRM handler with signal.signal that raises an exception, call signal.alarm(seconds) just before the blocking call, and call signal.alarm(0) in a finally block. The handler's exception unwinds the blocked call.
How do signal.SIGINT and signal.SIGTERM differ in a Python process by default?
basics
~20 sCPython installs its own default handler for signal.SIGINT that raises KeyboardInterrupt in the main thread, so finally blocks and atexit hooks still run. signal.SIGTERM keeps the operating system default, which terminates the process immediately with no Python cleanup.
Why does Python's print output appear instantly in a terminal but stall when the process is piped?
basics
~10 sCPython picks buffering per stream at startup: a terminal gets line buffering, so every newline flushes; a pipe or file gets a block buffer, 128 KiB on Python 3.14, that waits until it fills.