[{"data":1,"prerenderedAt":4068},["ShallowReactive",2],{"hub-python":3},{"framework":4,"topics":15,"qa":140},{"id":5,"description":6,"extension":7,"icon":8,"meta":9,"name":10,"order":11,"slug":8,"stem":12,"tier":13,"__hash__":14},"frameworks\u002Fframeworks\u002Fpython.yml","Python interview questions across language fundamentals, data structures and common gotchas for backend, data and automation roles.","yml","python",{},"Python",3,"frameworks\u002Fpython",1,"QsijsotyAr-3rnhJDWWZmc7hE6HAhylS5t1dKpigMOA",[16,24,33,41,50,59,68,77,86,95,104,113,122,131],{"id":17,"description":18,"extension":7,"frameworkSlug":8,"meta":19,"name":20,"order":13,"slug":21,"stem":22,"__hash__":23},"topics\u002Ftopics\u002Fpython-fundamentals.yml","Mutability, data types and common Python gotchas that come up in nearly every Python interview.",{},"Fundamentals","fundamentals","topics\u002Fpython-fundamentals","s0g44vxzPWFDbmltZbaw52-Rnfw29-sEc6vjnipDrEk",{"id":25,"description":26,"extension":7,"frameworkSlug":8,"meta":27,"name":28,"order":29,"slug":30,"stem":31,"__hash__":32},"topics\u002Ftopics\u002Fpython-data-structures.yml","Lists, tuples, dicts and sets — Python's built-in collections, their performance trade-offs, and the collections module that extends them.",{},"Data Structures",2,"data-structures","topics\u002Fpython-data-structures","5tEAHhQ_2jvDcJvjqh92pHCyvfs7wtBVemoRM7jsmw0",{"id":34,"description":35,"extension":7,"frameworkSlug":8,"meta":36,"name":37,"order":11,"slug":38,"stem":39,"__hash__":40},"topics\u002Ftopics\u002Fpython-iteration.yml","List, dict and set comprehensions, generators, and the iterator protocol — Python's expressive, lazy approach to building and consuming sequences.",{},"Comprehensions & Iteration","iteration","topics\u002Fpython-iteration","MFHt1c7Td8PboHEJiPzmzQr_zE4YWxNM7l1ZZ5NDMkI",{"id":42,"description":43,"extension":7,"frameworkSlug":8,"meta":44,"name":45,"order":46,"slug":47,"stem":48,"__hash__":49},"topics\u002Ftopics\u002Fpython-functions.yml","Arguments and unpacking, closures, decorators and higher-order functions — how Python treats functions as first-class objects.",{},"Functions",4,"functions","topics\u002Fpython-functions","A7vY4l7qHsjEdCjRLnpcHG6gaVS2ZfcWNuyO4ISlSJQ",{"id":51,"description":52,"extension":7,"frameworkSlug":8,"meta":53,"name":54,"order":55,"slug":56,"stem":57,"__hash__":58},"topics\u002Ftopics\u002Fpython-oop.yml","Classes, inheritance and the MRO, dunder methods, properties and dataclasses — object-oriented Python and its data model.",{},"Object-Oriented Programming",5,"oop","topics\u002Fpython-oop","HWQNuZjsu-ieQLL6BEbbeZIHJCEhXocNXPj2wzkQUbQ",{"id":60,"description":61,"extension":7,"frameworkSlug":8,"meta":62,"name":63,"order":64,"slug":65,"stem":66,"__hash__":67},"topics\u002Ftopics\u002Fpython-exceptions.yml","try\u002Fexcept\u002Felse\u002Ffinally, the exception hierarchy, custom exceptions and context managers — robust error handling in Python.",{},"Errors & Exceptions",6,"exceptions","topics\u002Fpython-exceptions","AxiThX7RyYVRhjW0TnsHXhYt19xLlcCrOU2utOgPrfs",{"id":69,"description":70,"extension":7,"frameworkSlug":8,"meta":71,"name":72,"order":73,"slug":74,"stem":75,"__hash__":76},"topics\u002Ftopics\u002Fpython-functional.yml","map, filter and reduce, functools and itertools — composing programs from functions and lazy iterators.",{},"Functional Programming",7,"functional","topics\u002Fpython-functional","yvelsIB4sUvQAtMK3cGW7znvMHUf4DW6XEe6gbzy4Ag",{"id":78,"description":79,"extension":7,"frameworkSlug":8,"meta":80,"name":81,"order":82,"slug":83,"stem":84,"__hash__":85},"topics\u002Ftopics\u002Fpython-modules.yml","The import system, packages, __main__, and virtual environments — how Python code is organized, shared and run.",{},"Modules, Packages & Environments",8,"modules","topics\u002Fpython-modules","a8_a5opZ2WGRb6hv-AG427fbrffe_nPB8563DcP43W8",{"id":87,"description":88,"extension":7,"frameworkSlug":8,"meta":89,"name":90,"order":91,"slug":92,"stem":93,"__hash__":94},"topics\u002Ftopics\u002Fpython-concurrency.yml","Threads and the GIL, multiprocessing, asyncio and concurrent.futures — running Python code concurrently and in parallel.",{},"Concurrency & Parallelism",9,"concurrency","topics\u002Fpython-concurrency","5RIdEXxDtKsbX3veVxq6vhE87mv01LWJ2ut_nkPxEQM",{"id":96,"description":97,"extension":7,"frameworkSlug":8,"meta":98,"name":99,"order":100,"slug":101,"stem":102,"__hash__":103},"topics\u002Ftopics\u002Fpython-internals.yml","Reference counting, garbage collection, object identity and the CPython execution model — how Python manages memory and runs your code.",{},"Memory & Internals",10,"internals","topics\u002Fpython-internals","U2yHsQaj-JeXNyACZ2urxbwgXTTPHaZg1F16EMvjKow",{"id":105,"description":106,"extension":7,"frameworkSlug":8,"meta":107,"name":108,"order":109,"slug":110,"stem":111,"__hash__":112},"topics\u002Ftopics\u002Fpython-typing.yml","Type annotations, the typing module, generics and protocols — optional static typing for modern Python.",{},"Type Hints & Typing",11,"typing","topics\u002Fpython-typing","m9rEesQ-tmtEb8ibbPg4hXIEFkDaRfVfLdvDypVFl-g",{"id":114,"description":115,"extension":7,"frameworkSlug":8,"meta":116,"name":117,"order":118,"slug":119,"stem":120,"__hash__":121},"topics\u002Ftopics\u002Fpython-stdlib.yml","pathlib and os, datetime, regular expressions and JSON\u002FCSV — the batteries-included modules every Python developer reaches for.",{},"Standard Library Essentials",12,"stdlib","topics\u002Fpython-stdlib","sRFqpDw98pJhtlo77axy-qulzRwuEEAdxjjHQKKWSGA",{"id":123,"description":124,"extension":7,"frameworkSlug":8,"meta":125,"name":126,"order":127,"slug":128,"stem":129,"__hash__":130},"topics\u002Ftopics\u002Fpython-testing.yml","pytest and unittest, fixtures and mocking — writing automated tests for Python code.",{},"Testing",13,"testing","topics\u002Fpython-testing","HwvMxQFl9E4PubF9w7vHbjXIlh3Y1LpYkOTKVAmw754",{"id":132,"description":133,"extension":7,"frameworkSlug":8,"meta":134,"name":135,"order":136,"slug":137,"stem":138,"__hash__":139},"topics\u002Ftopics\u002Fpython-idioms.yml","EAFP vs LBYL, PEP 8 style, and the common gotchas and anti-patterns that separate idiomatic Python from translated code.",{},"Pythonic Idioms",14,"idioms","topics\u002Fpython-idioms","WsKznDcUUGEslzKQaB4t9l0sMLRgYpcH3joEEaq7zYA",[141,225,301,376,451,525,673,747,822,897,972,1047,1122,1197,1272,1351,1423,1498,1576,1654,1728,1803,1874,1949,2023,2098,2173,2248,2323,2402,2480,2559,2643,2716,2795,2866,2941,3016,3089,3162,3237,3311,3389,3464,3538,3612,3687,3762,3841,3920,3994],{"id":142,"title":143,"body":144,"description":148,"difficulty":150,"extension":151,"framework":10,"frameworkSlug":8,"meta":152,"navigation":153,"order":13,"path":154,"questions":155,"questionsCount":217,"related":218,"seo":219,"seoDescription":220,"stem":221,"subtopic":222,"topic":90,"topicSlug":92,"updated":223,"__hash__":224},"qa\u002Fpython\u002Fconcurrency\u002Fgil.md","Gil",{"type":145,"value":146,"toc":147},"minimark",[],{"title":148,"searchDepth":29,"depth":29,"links":149},"",[],"hard","md",{},true,"\u002Fpython\u002Fconcurrency\u002Fgil",[156,160,165,169,173,177,181,185,189,193,197,201,205,209,213],{"id":157,"difficulty":150,"q":158,"a":159},"what-is-the-gil","What is the GIL?","The **Global Interpreter Lock** is a mutex in **CPython** that allows **only one\nthread to execute Python bytecode at a time**. Even on a multi-core machine, a\nmultithreaded pure-Python program runs its bytecode on **one core at a time** —\nthreads take turns holding the lock.\n\nIt exists to make CPython's memory management (especially **reference counting**)\nsimple and fast: without it, every refcount update would need its own lock. The\ninterpreter releases the GIL periodically and **around blocking I\u002FO** so other\nthreads can run.\n\n```python\nimport threading\n# both threads exist, but the GIL serializes their bytecode execution\ndef work():\n    total = 0\n    for _ in range(10_000_000):   # CPU-bound — holds the GIL\n        total += 1\n\nt1 = threading.Thread(target=work)\nt2 = threading.Thread(target=work)\nt1.start(); t2.start(); t1.join(); t2.join()   # ~no speedup vs one thread\n```\n\nWhy it matters: the GIL is a **CPython implementation detail** (not in the language\nspec, and absent in Jython\u002Fthe free-threaded 3.13+ build) that shapes when threads\ndo and don't help.\n",{"id":161,"difficulty":162,"q":163,"a":164},"threading-vs-multiprocessing","medium","What is the difference between threading and multiprocessing?","**Threads** share one process and one memory space, so they're cheap to create and\nshare data directly — but in CPython they're serialized by the **GIL**.\n**Processes** each have their own interpreter and memory, so they run on\n**separate cores in true parallel**, bypassing the GIL — at the cost of higher\nstartup overhead and needing to **serialize (pickle) data** to communicate.\n\n```python\nfrom threading import Thread\nfrom multiprocessing import Process\n\nThread(target=fn)    # shared memory, GIL-bound, light\nProcess(target=fn)   # separate memory, real parallelism, heavier\n```\n\nThreads communicate through shared objects (guarded by locks); processes\ncommunicate through `Queue`, `Pipe`, or shared-memory primitives because they don't\nshare state.\n\nRule of thumb: **threads for I\u002FO-bound** concurrency, **processes for CPU-bound**\nparallelism.\n",{"id":166,"difficulty":150,"q":167,"a":168},"cpu-vs-io-bound","Why don't threads speed up CPU-bound work but help I\u002FO-bound work?","For **CPU-bound** work, threads are constantly executing bytecode, so they're\nalways contending for the **GIL** — only one runs at a time, and you get no\nparallel speedup (often a small slowdown from lock contention and context switches).\n\nFor **I\u002FO-bound** work, a thread that's waiting on the network, disk, or a database\nis **blocked outside the interpreter** — and CPython **releases the GIL during\nblocking I\u002FO**. So other threads run while one waits, giving real concurrency.\n\n```python\nimport requests, threading\n\ndef fetch(url):\n    requests.get(url)     # blocks on the network -> GIL released here\n\n# 10 threads overlap their waiting time -> much faster than sequential\nthreads = [threading.Thread(target=fetch, args=(u,)) for u in urls]\n```\n\nRule of thumb: if your bottleneck is **waiting**, use threads (or asyncio); if it's\n**computing**, use **processes** to get past the GIL.\n",{"id":170,"difficulty":150,"q":171,"a":172},"race-conditions-locks","What is a race condition and how do locks prevent it?","A **race condition** occurs when two threads access shared mutable state and the\nresult depends on **timing**. Even `x += 1` is not atomic — it's read, add, write,\nand a thread can be switched out mid-sequence, so updates get lost.\n\nA **lock** (`threading.Lock`) creates a **critical section**: only the thread\nholding it can proceed, the rest wait, so the read-modify-write happens atomically.\nUsing `with lock:` guarantees the lock is always released, even on exceptions.\n\n```python\nimport threading\ncounter = 0\nlock = threading.Lock()\n\ndef increment():\n    global counter\n    for _ in range(100_000):\n        with lock:           # only one thread in here at a time\n            counter += 1     # now safe from lost updates\n```\n\nBeware over-locking: acquiring multiple locks in different orders can cause\n**deadlock**. Rule of thumb: guard **every** access to shared mutable state, keep\ncritical sections small, and prefer thread-safe `queue.Queue` for handoff.\n",{"id":174,"difficulty":162,"q":175,"a":176},"threadpool-vs-processpool","When do you use ThreadPoolExecutor vs ProcessPoolExecutor?","Both come from `concurrent.futures` and share the same API — `submit` \u002F\n`map` returning `Future` objects — so you can swap them with one line. The\ndifference is the **worker type**: `ThreadPoolExecutor` runs tasks in **threads**\n(shared memory, GIL-bound) and `ProcessPoolExecutor` runs them in **separate\nprocesses** (true parallelism, pickled args).\n\n```python\nfrom concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor\n\n# I\u002FO-bound: many network\u002Ffile waits -> threads\nwith ThreadPoolExecutor(max_workers=20) as ex:\n    results = list(ex.map(download, urls))\n\n# CPU-bound: heavy computation -> processes (one per core)\nwith ProcessPoolExecutor() as ex:\n    results = list(ex.map(crunch_numbers, datasets))\n```\n\nUse **ThreadPoolExecutor for I\u002FO-bound** tasks (downloads, DB calls) where waiting\ndominates, and **ProcessPoolExecutor for CPU-bound** tasks to use all cores —\nremembering its arguments and return values must be **picklable**.\n\nRule of thumb: pick the executor by the bottleneck (waiting vs computing), and let\n`concurrent.futures` handle the pool lifecycle and result collection.\n",{"id":178,"difficulty":162,"q":179,"a":180},"why-gil-exists","Why does CPython have a GIL at all?","Because CPython manages memory with **reference counting**, and incrementing\n\u002F decrementing refcounts from multiple threads without protection would\ncorrupt them. The GIL is a **single coarse lock** that makes the interpreter\nand refcounting **thread-safe cheaply**, keeps single-threaded code fast, and\nmakes integrating C extensions simple.\n\n```text\nobject -> refcount field\nthread A: incref            # not atomic on their own\nthread B: decref            # GIL serializes these -> no corruption\n```\n\nRule of thumb: the GIL is a deliberate trade — simpler, faster\nsingle-threaded execution and easy C interop, at the cost of multi-core\nthreading for pure Python.\n",{"id":182,"difficulty":150,"q":183,"a":184},"gil-release","When does the GIL get released?","A thread releases the GIL **periodically** (driven by `sys.setswitchinterval`,\n~5ms by default) so others can run, and **around blocking I\u002FO** — file\u002Fsocket\nreads, `time.sleep`, etc. Well-written **C extensions** (NumPy, hashlib,\nzlib) also release it during heavy native work, allowing real parallelism.\n\n```python\nimport sys\nsys.getswitchinterval()    # ~0.005 seconds between forced switches\n```\n\nRule of thumb: pure-Python CPU loops hold the GIL; I\u002FO and GIL-releasing C\ncode let other threads make progress — which is why threads help those cases.\n",{"id":186,"difficulty":150,"q":187,"a":188},"gil-310-free-threading","Is the GIL going away?","Python 3.13 introduced an **experimental free-threaded build** (PEP 703) that\ncan run **without the GIL**, enabling true multi-core threading. It's\n**opt-in** (a separate build), still maturing, and can slow single-threaded\ncode. The standard CPython you get by default **still has the GIL**.\n\n```text\npython3.13          # GIL build (default)\npython3.13t         # free-threaded (\"t\") build, no GIL, experimental\n```\n\nRule of thumb: for the foreseeable future, design as if the GIL exists; the\nfree-threaded build is promising but not yet the default.\n",{"id":190,"difficulty":162,"q":191,"a":192},"lock-vs-rlock","What is the difference between Lock and RLock?","A **`Lock`** can be acquired once; if the **same thread** tries to acquire it\nagain before releasing, it **deadlocks**. An **`RLock`** (reentrant lock) can\nbe acquired multiple times **by the thread that holds it**, requiring an equal\nnumber of releases — useful for recursive code or nested method calls that\neach lock.\n\n```python\nimport threading\nlock = threading.RLock()\n\ndef outer():\n    with lock:\n        inner()           # re-acquires same lock -> fine with RLock\n\ndef inner():\n    with lock:\n        ...               # would deadlock with a plain Lock\n```\n\nRule of thumb: use `Lock` by default; reach for `RLock` only when the same\nthread legitimately needs to re-acquire a lock it already holds.\n",{"id":194,"difficulty":150,"q":195,"a":196},"deadlock","What causes a deadlock and how do you avoid it?","A **deadlock** happens when threads wait on each other's locks in a cycle —\nclassically thread A holds lock 1 and wants lock 2 while thread B holds lock 2\nand wants lock 1. Neither proceeds. Prevent it by **always acquiring locks in\na consistent global order**, using **timeouts**, or holding fewer locks.\n\n```python\n# both threads acquire in the SAME order -> no cycle possible\ndef transfer(a, b):\n    first, second = sorted((a, b), key=id)\n    with first.lock:\n        with second.lock:\n            ...\n```\n\nRule of thumb: impose a fixed lock-acquisition order everywhere, and keep\ncritical sections small.\n",{"id":198,"difficulty":162,"q":199,"a":200},"thread-safe-structures","Are Python's built-in data structures thread-safe?","Individual operations on `list`, `dict`, and `queue.Queue` are protected by\nthe GIL so they won't corrupt, but **compound operations are not atomic**.\n`x += 1` or \"check then act\" sequences can interleave and race. Use a `Lock`\nfor multi-step updates, or **`queue.Queue`**, which is fully synchronized for\nproducer\u002Fconsumer hand-off.\n\n```python\nfrom queue import Queue\nq = Queue()                # thread-safe put\u002Fget, no manual lock needed\n\ncounter = 0\ndef inc():\n    global counter\n    counter += 1           # NOT atomic: load, add, store can interleave\n```\n\nRule of thumb: rely on the GIL only for single C-level operations; guard\nany multi-step update with a lock or use `queue.Queue`.\n",{"id":202,"difficulty":162,"q":203,"a":204},"daemon-threads","What is a daemon thread?","A **daemon thread** is a background thread the program **does not wait for** at\nexit — when all non-daemon threads finish, the interpreter shuts down and\nkills daemons abruptly (no cleanup, `finally` may not run). Non-daemon\nthreads, by contrast, keep the process alive until they complete.\n\n```python\nimport threading\nt = threading.Thread(target=loop, daemon=True)\nt.start()                  # won't block program exit\n```\n\nRule of thumb: use daemon threads for fire-and-forget background work, but\nnever for tasks that must finish or flush state cleanly on shutdown.\n",{"id":206,"difficulty":162,"q":207,"a":208},"join-threads","What does thread.join do?","**`join()`** blocks the calling thread until the target thread **finishes**\n(or an optional `timeout` elapses). It's how you wait for results \u002F ensure\ncompletion before moving on. Without joining, the main thread may continue or\nexit while workers are still running.\n\n```python\nthreads = [threading.Thread(target=work, args=(i,)) for i in range(5)]\nfor t in threads: t.start()\nfor t in threads: t.join()      # wait for all to complete\nprint(\"all done\")\n```\n\nRule of thumb: start all threads first, then join them all, so they run\nconcurrently rather than one-at-a-time.\n",{"id":210,"difficulty":162,"q":211,"a":212},"threadlocal","What is threading.local used for?","**`threading.local()`** creates an object whose **attributes are per-thread** —\neach thread sees its own independent values on the same object. It's used to\nhold thread-specific state (a DB connection, a request context) without\npassing it around or locking shared state.\n\n```python\nimport threading\nctx = threading.local()\n\ndef handler():\n    ctx.user = get_current_user()   # private to this thread\n    ...\n```\n\nRule of thumb: use thread-locals to avoid sharing mutable state at all —\nno shared data means no locks and no races.\n",{"id":214,"difficulty":162,"q":215,"a":216},"gil-misconception","Does the GIL make Python code automatically thread-safe?","**No.** The GIL guarantees only that a **single bytecode operation** won't be\ninterrupted mid-way — it does **not** make multi-step logic atomic. A thread\ncan be suspended between bytecodes, so `counter += 1` (load, add, store) can\nstill race and lose updates.\n\n```python\n# with two threads each doing 100000 increments,\n# the final value is often LESS than 200000 without a lock\ncounter = 0\ndef work():\n    global counter\n    for _ in range(100_000):\n        counter += 1        # interleaves -> lost updates\n```\n\nRule of thumb: never assume \"the GIL protects me\" — guard shared mutable\nstate with explicit locks regardless.\n",15,null,{"description":148},"Python interview questions on threading and the GIL — what the GIL is, threads vs multiprocessing, CPU-bound vs I\u002FO-bound work, race conditions and locks, and ThreadPoolExecutor vs ProcessPoolExecutor.","python\u002Fconcurrency\u002Fgil","Threading & the GIL","2026-06-18","ulCAJFHOfAXoiItKqwBhXBJi4G3_0Jt5N8-Y3JHVbw8",{"id":226,"title":227,"body":228,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":232,"navigation":153,"order":13,"path":233,"questions":234,"questionsCount":217,"related":218,"seo":296,"seoDescription":297,"stem":298,"subtopic":299,"topic":28,"topicSlug":30,"updated":223,"__hash__":300},"qa\u002Fpython\u002Fdata-structures\u002Flists.md","Lists",{"type":145,"value":229,"toc":230},[],{"title":148,"searchDepth":29,"depth":29,"links":231},[],{},"\u002Fpython\u002Fdata-structures\u002Flists",[235,239,243,247,251,256,260,264,268,272,276,280,284,288,292],{"id":236,"difficulty":162,"q":237,"a":238},"list-vs-array","What is the difference between a Python list and an array?","A **`list`** is Python's built-in, **dynamically-sized, heterogeneous**\ncontainer — it can hold objects of any type because each slot stores a\n**reference** to a boxed Python object. The standard-library **`array.array`**\n(and NumPy's `ndarray`) is **homogeneous and typed**, storing raw C values\ncontiguously, which is far more **memory-efficient** for large numeric data.\n\n```python\nfrom array import array\nnums = [1, 2, \"three\"]      # list — mixed types allowed\ntyped = array(\"i\", [1, 2, 3])  # array — all C ints, compact\ntyped.append(\"x\")           # TypeError — type-checked\n\nimport sys\nsys.getsizeof([1, 2, 3])    # bigger: stores pointers\nsys.getsizeof(array(\"i\", [1, 2, 3]))  # smaller: packed ints\n```\n\nRule of thumb: reach for a plain **`list`** for general-purpose, mixed-type\ncollections; use **`array`** or **NumPy** when you have large amounts of\nuniform numeric data and care about memory or vectorized speed.\n",{"id":240,"difficulty":162,"q":241,"a":242},"slicing-semantics","How does list slicing work, including negative steps?","A slice is `lst[start:stop:step]`. `start` is **inclusive**, `stop` is\n**exclusive**, and any part can be **omitted** (defaults: start of list, end of\nlist, step 1). A **negative step** walks the list **backwards**, and when it's\nnegative the defaults for `start`\u002F`stop` flip to the **end** and the\n**beginning**.\n\n```python\na = [0, 1, 2, 3, 4, 5]\na[1:4]      # [1, 2, 3]   — stop is exclusive\na[:3]       # [0, 1, 2]   — omitted start = 0\na[::2]      # [0, 2, 4]   — every other element\na[::-1]     # [5, 4, 3, 2, 1, 0]  — reversed copy\na[4:1:-1]   # [4, 3, 2]   — backwards, stop exclusive\n```\n\nSlicing **never raises** for out-of-range indices (`a[10:20]` is just `[]`),\nunlike single-element indexing. Remember `a[::-1]` is the idiomatic way to get a\n**reversed shallow copy**.\n",{"id":244,"difficulty":162,"q":245,"a":246},"append-extend-insert","What is the difference between append, extend, and insert?","**`append(x)`** adds a single item to the end in **amortized O(1)**.\n**`extend(iterable)`** adds **each element** of an iterable to the end (also\namortized O(1) per element). **`insert(i, x)`** places an item at index `i`,\nwhich is **O(n)** because every following element must shift right.\n\n```python\na = [1, 2, 3]\na.append([4, 5])   # [1, 2, 3, [4, 5]]  — the LIST is one element\na = [1, 2, 3]\na.extend([4, 5])   # [1, 2, 3, 4, 5]    — unpacks the iterable\na.insert(0, 99)    # [99, 1, 2, 3, 4, 5] — O(n) shift\n```\n\nThe classic trap: `append([4, 5])` nests the whole list as one item, whereas\n`extend([4, 5])` merges its elements. Avoid `insert(0, …)` in a loop — it's\nO(n) each time; use `collections.deque` for fast front insertion.\n",{"id":248,"difficulty":162,"q":249,"a":250},"comprehension-vs-loop","Are list comprehensions faster than equivalent for loops?","Usually **yes**. A comprehension runs its iteration in **optimized C-level\nbytecode** and avoids the repeated `list.append` **attribute lookup and method\ncall** that a manual loop incurs, so it's typically **20–40% faster** as well as\nmore concise. It also creates the loop variable in its **own scope**, so it\ndoesn't leak.\n\n```python\n# manual loop — explicit append each iteration\nsquares = []\nfor n in range(1000):\n    squares.append(n * n)\n\n# comprehension — faster and clearer\nsquares = [n * n for n in range(1000)]\n```\n\nUse a comprehension when you're **building a list** from an expression. But if\nthe body has side effects or grows complex (nested conditionals, multiple\nstatements), a readable `for` loop is the better choice — clarity beats a small\nspeed win.\n",{"id":252,"difficulty":253,"q":254,"a":255},"sort-vs-sorted","easy","What is the difference between list.sort() and sorted()?","**`list.sort()`** sorts a list **in place** and returns **`None`** — it mutates\nthe original and only works on lists. **`sorted(iterable)`** returns a **new\nsorted list** and leaves the input untouched, accepting **any iterable** (tuples,\nsets, generators, dict keys).\n\n```python\nnums = [3, 1, 2]\nresult = nums.sort()        # result is None! nums is now [1, 2, 3]\n\noriginal = (3, 1, 2)\nnew = sorted(original)      # new = [1, 2, 3], original unchanged\nsorted([\"bb\", \"a\"], key=len, reverse=True)  # ['bb', 'a']\n```\n\nBoth are **stable** (equal elements keep their order) and run in **O(n log n)**\n(Timsort). Watch the gotcha: `x = mylist.sort()` leaves `x` as `None`. Use\n`sort()` to save memory when you don't need the original; use `sorted()` when you\nmust preserve it or are sorting a non-list iterable.\n",{"id":257,"difficulty":162,"q":258,"a":259},"list-multiplication-trap","What is the trap with [[]] * 3?","List multiplication **copies references, not objects**. `[[]] * 3` makes three\nslots pointing at the **same inner list**, so mutating one mutates all. Build\nindependent rows with a comprehension instead.\n\n```python\ngrid = [[0]] * 3\ngrid[0].append(1)        # [[1], [1], [1]] !  all share one list\n\ngrid = [[0] for _ in range(3)]   # correct: 3 distinct lists\n```\n\nRule of thumb: `*` is safe for immutables (`[0] * 3`) but never for nested\nmutable containers — use a comprehension there.\n",{"id":261,"difficulty":162,"q":262,"a":263},"remove-while-iterating","Why shouldn't you remove items from a list while iterating it?","The iterator tracks an **index** that keeps advancing while the list shrinks,\nso removing an element makes the loop **skip** the next one. Iterate over a\n**copy**, or build a new list with a comprehension \u002F `filter`.\n\n```python\nnums = [1, 2, 2, 3]\nfor n in nums[:]:            # iterate a copy\n    if n == 2:\n        nums.remove(n)\n# cleaner: nums = [n for n in nums if n != 2]\n```\n\nRule of thumb: don't mutate a list you're looping over — filter into a new\nlist or iterate a slice copy.\n",{"id":265,"difficulty":162,"q":266,"a":267},"shallow-copy-list","How do you copy a list, and what is shallow vs deep?","`list(x)`, `x[:]`, and `x.copy()` make a **shallow** copy — a new outer list\nwhose elements are the **same objects**. Nested mutable elements are still\nshared. Use **`copy.deepcopy`** to recursively copy everything.\n\n```python\nimport copy\na = [[1], [2]]\nb = a[:]                 # shallow\nb[0].append(9)           # affects a too -> a == [[1, 9], [2]]\nc = copy.deepcopy(a)     # fully independent\n```\n\nRule of thumb: shallow copy is enough for flat lists; deep-copy when nested\nmutable objects must be independent.\n",{"id":269,"difficulty":253,"q":270,"a":271},"negative-index","How does negative indexing work?","Negative indices count **from the end**: `-1` is the last element, `-2` the\nsecond-to-last, and so on. It's shorthand to avoid `len(x) - 1`. Going beyond\nthe start (`-len-1`) raises `IndexError`, but **slicing** never does.\n\n```python\nx = [10, 20, 30]\nx[-1]            # 30\nx[-2]            # 20\nx[-5]            # IndexError\nx[-5:]           # [10, 20, 30]  -> slice clamps, no error\n```\n\nRule of thumb: use negative indices for \"from the end\" access; prefer slicing\nwhen out-of-range should be tolerated.\n",{"id":273,"difficulty":253,"q":274,"a":275},"enumerate-list","How do you loop with both index and value?","Use **`enumerate(seq, start=0)`**, which yields `(index, value)` pairs — far\ncleaner and less error-prone than `range(len(seq))` plus manual indexing. The\n`start` argument sets the first index.\n\n```python\nfor i, name in enumerate([\"a\", \"b\"], start=1):\n    print(i, name)       # 1 a \u002F 2 b\n```\n\nRule of thumb: never write `for i in range(len(x)): x[i]` — use `enumerate`\nwhen you need the index alongside the value.\n",{"id":277,"difficulty":162,"q":278,"a":279},"list-as-stack-queue","Can you use a list as a stack and a queue?","As a **stack** (LIFO): yes — `append`\u002F`pop` at the end are O(1). As a\n**queue** (FIFO): technically `append` + `pop(0)`, but `pop(0)` and\n`insert(0, x)` are **O(n)** because every element shifts. Use\n**`collections.deque`** for an efficient queue.\n\n```python\nstack = []; stack.append(1); stack.pop()        # O(1) both\n\nfrom collections import deque\nq = deque(); q.append(1); q.popleft()           # O(1) FIFO\n```\n\nRule of thumb: list is a fine stack; use `deque` whenever you pop\u002Finsert at\nthe front.\n",{"id":281,"difficulty":162,"q":282,"a":283},"in-operator-list","What is the time complexity of `x in my_list`?","**O(n)** — membership scans the list element by element. For repeated\nmembership tests on large data, convert to a **set** (O(1) average) or **dict**\nfirst. The convenience of `in` on a list hides a linear cost.\n\n```python\nif target in big_list:       # O(n) each call\n    ...\n\nlookup = set(big_list)       # O(n) once\nif target in lookup:         # O(1) thereafter\n    ...\n```\n\nRule of thumb: a one-off `in` on a list is fine; for many lookups, build a set\nor dict.\n",{"id":285,"difficulty":162,"q":286,"a":287},"list-unpacking-star","How does starred unpacking work with lists?","A **`*name`** in an unpacking target captures the \"rest\" as a list. It can sit\nanywhere in the pattern — start, middle, or end — grabbing everything not\nmatched by the fixed names.\n\n```python\nfirst, *rest = [1, 2, 3, 4]      # first=1, rest=[2, 3, 4]\n*init, last = [1, 2, 3]          # init=[1, 2], last=3\na, *mid, b = [1, 2, 3, 4]        # a=1, mid=[2, 3], b=4\n```\n\nRule of thumb: use starred unpacking to split off head\u002Ftail elements without\nslicing index arithmetic.\n",{"id":289,"difficulty":162,"q":290,"a":291},"sort-key-reverse","How do you sort a list of objects by a field?","Pass a **`key`** function returning the sort value; add **`reverse=True`** to\ndescend. For multiple criteria return a **tuple** — Python sorts\nlexicographically and the sort is **stable**, so equal keys keep their\noriginal order.\n\n```python\npeople.sort(key=lambda p: p.age)\npeople.sort(key=lambda p: (p.last, p.first))     # by last, then first\nfrom operator import attrgetter\npeople.sort(key=attrgetter(\"age\"), reverse=True) # fast, descending\n```\n\nRule of thumb: use `key` (with `operator.attrgetter`\u002F`itemgetter` for speed)\nand a tuple key for multi-level sorting.\n",{"id":293,"difficulty":150,"q":294,"a":295},"bisect-insort","How do you keep a list sorted efficiently as you insert?","Use the **`bisect`** module: `bisect.insort(lst, x)` inserts `x` keeping the\nlist sorted, and `bisect.bisect_left\u002Fright` finds the insertion point via\n**binary search (O(log n))**. The insert itself is still O(n) due to shifting,\nbut you avoid re-sorting the whole list each time.\n\n```python\nimport bisect\ndata = [1, 3, 5]\nbisect.insort(data, 4)       # [1, 3, 4, 5]\nbisect.bisect_left(data, 4)  # 2  -> index where 4 sits\n```\n\nRule of thumb: use `bisect` for ordered inserts and fast lookups in a sorted\nlist instead of appending and re-sorting.\n",{"description":148},"Python interview questions on lists vs arrays, slicing semantics and negative steps, append vs extend vs insert, comprehensions, and sort vs sorted.","python\u002Fdata-structures\u002Flists","Lists & Slicing","odH58hRwVK6RyatuVcEfDLL5nbFK0kcxnPuLMRZlKP0",{"id":302,"title":303,"body":304,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":308,"navigation":153,"order":13,"path":309,"questions":310,"questionsCount":217,"related":218,"seo":371,"seoDescription":372,"stem":373,"subtopic":374,"topic":63,"topicSlug":65,"updated":223,"__hash__":375},"qa\u002Fpython\u002Fexceptions\u002Fcontext-managers.md","Context Managers",{"type":145,"value":305,"toc":306},[],{"title":148,"searchDepth":29,"depth":29,"links":307},[],{},"\u002Fpython\u002Fexceptions\u002Fcontext-managers",[311,315,319,323,327,331,335,339,343,347,351,355,359,363,367],{"id":312,"difficulty":253,"q":313,"a":314},"what-is-context-manager","What is a context manager and what does the with statement do?","A **context manager** is an object that defines **setup and teardown** logic to run\naround a block of code. The **`with` statement** uses it to guarantee that the\nteardown happens — even if the block raises an exception or returns early — so you\ndon't have to write manual `try\u002Ffinally`.\n\n```python\nwith open(\"data.txt\") as f:   # __enter__ runs, returns the file\n    data = f.read()\n# __exit__ runs here automatically — the file is closed\n```\n\nThe classic use is resource management: files, network sockets, database\nconnections, and locks. Rule of thumb: any \"acquire then must-release\" pattern is\na candidate for a `with` block.\n",{"id":316,"difficulty":162,"q":317,"a":318},"enter-exit","How do __enter__ and __exit__ work?","To make a class a context manager you implement two dunder methods. **`__enter__`**\nruns at the start of the `with` block and its return value is bound to the `as`\nvariable. **`__exit__`** runs when the block ends — always — receiving the\nexception type, value, and traceback (all `None` if the block succeeded).\n\n```python\nclass Timer:\n    def __enter__(self):\n        import time; self.start = time.time()\n        return self                      # bound to 'as t'\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        import time; print(time.time() - self.start)\n        return False                     # don't suppress exceptions\n\nwith Timer() as t:\n    do_work()\n```\n\n`__exit__` always runs, which is what makes cleanup reliable. Rule of thumb:\nacquire the resource in `__enter__`, release it in `__exit__`, and `return self`\nif callers need the object.\n",{"id":320,"difficulty":162,"q":321,"a":322},"contextlib-decorator","How does contextlib.contextmanager simplify writing one?","The **`@contextlib.contextmanager`** decorator lets you write a context manager as a\n**generator** instead of a class. Code **before `yield`** is the setup (`__enter__`),\nthe **yielded value** becomes the `as` target, and code **after `yield`** is the\nteardown (`__exit__`).\n\n```python\nfrom contextlib import contextmanager\n\n@contextmanager\ndef opened(path):\n    f = open(path)          # setup\n    try:\n        yield f             # value bound to 'as'\n    finally:\n        f.close()           # teardown — runs even on error\n\nwith opened(\"data.txt\") as f:\n    print(f.read())\n```\n\nThe `try\u002Ffinally` around the `yield` is essential — without it the teardown is\nskipped when the block raises. Rule of thumb: reach for the decorator for simple,\none-off managers; write a class when you need state across multiple methods.\n",{"id":324,"difficulty":150,"q":325,"a":326},"exceptions-in-exit","How does a context manager handle exceptions in __exit__?","When the `with` block raises, Python passes the exception details into `__exit__`.\nThe crucial part is the **return value**: returning a **truthy** value tells Python\nto **suppress** the exception, while returning `False`\u002F`None` lets it **propagate**\nnormally.\n\n```python\nclass Suppress:\n    def __enter__(self): return self\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        if exc_type is ValueError:\n            print(\"swallowed:\", exc_val)\n            return True          # suppress ValueError\n        return False             # re-raise anything else\n\nwith Suppress():\n    raise ValueError(\"oops\")     # swallowed, program continues\n```\n\n`contextlib.suppress(ValueError)` is the ready-made version of this pattern. Rule\nof thumb: only suppress exceptions you genuinely intend to ignore — accidentally\nreturning a truthy value hides bugs.\n",{"id":328,"difficulty":253,"q":329,"a":330},"multiple-context-managers","How do you use multiple context managers and what are real uses?","You can manage several resources in one `with` by separating them with commas (or\nusing parenthesized form in 3.10+). They are **entered left to right** and\n**exited in reverse order**, so cleanup unwinds correctly.\n\n```python\nwith open(\"in.txt\") as src, open(\"out.txt\", \"w\") as dst:\n    dst.write(src.read())\n# dst closed first, then src\n\nimport threading\nlock = threading.Lock()\nwith lock:                 # acquire on enter, release on exit\n    shared_counter += 1\n```\n\nCommon real uses: **files** (auto-close), **locks** (auto-release even on error),\n**database transactions** (commit\u002Frollback), and temporarily **changing state**\nlike `decimal.localcontext`. Rule of thumb: if you ever write `try\u002Ffinally` to\nrelease something, a context manager expresses it more clearly.\n",{"id":332,"difficulty":150,"q":333,"a":334},"exit-return-suppress","How does __exit__ suppress an exception?","`__exit__(exc_type, exc_value, tb)` receives the exception (or three `None`s\non clean exit). Returning a **truthy value swallows** the exception; returning\n`None`\u002Ffalsy lets it propagate. Suppressing should be deliberate — silently\neating errors hides bugs.\n\n```python\nclass Ignore:\n    def __enter__(self): return self\n    def __exit__(self, et, ev, tb):\n        return et is ValueError      # swallow only ValueError\n\nwith Ignore():\n    raise ValueError(\"gone\")         # suppressed\n```\n\nRule of thumb: return `True` from `__exit__` only when you intentionally mean\nto suppress that specific exception.\n",{"id":336,"difficulty":162,"q":337,"a":338},"contextlib-suppress","What does contextlib.suppress do?","**`contextlib.suppress(*exc_types)`** is a context manager that **ignores**\nthe listed exceptions inside its block — a clean replacement for a\n`try\u002Fexcept: pass`. It only swallows the named types; others propagate.\n\n```python\nfrom contextlib import suppress\nwith suppress(FileNotFoundError):\n    os.remove(\"maybe.txt\")           # no error if it's missing\n\n# equivalent to:\ntry: os.remove(\"maybe.txt\")\nexcept FileNotFoundError: pass\n```\n\nRule of thumb: use `suppress` for the narrow \"ignore this expected error\"\ncase instead of an empty-except block.\n",{"id":340,"difficulty":162,"q":341,"a":342},"contextlib-closing","What is contextlib.closing for?","**`closing(thing)`** turns any object with a `.close()` method into a context\nmanager that calls `close()` on exit — useful for objects that don't natively\nsupport `with`. It guarantees cleanup even on exceptions.\n\n```python\nfrom contextlib import closing\nfrom urllib.request import urlopen\nwith closing(urlopen(url)) as page:\n    data = page.read()               # page.close() runs automatically\n```\n\nRule of thumb: wrap legacy \"has close() but no with\" objects in `closing()`\nto get deterministic teardown.\n",{"id":344,"difficulty":162,"q":345,"a":346},"contextmanager-yield-cleanup","In a @contextmanager generator, why wrap yield in try\u002Ffinally?","The code **before `yield`** is setup, the value **yielded** is bound to `as`,\nand code **after `yield`** is teardown. If the `with` body raises, the\nexception is thrown **into** the generator at the `yield`, so cleanup runs\nonly if you put it in a **`finally`**.\n\n```python\nfrom contextlib import contextmanager\n@contextmanager\ndef transaction(db):\n    db.begin()\n    try:\n        yield db\n        db.commit()\n    finally:\n        db.close()                   # always runs, even on error\n```\n\nRule of thumb: in a `@contextmanager`, always guard teardown with\n`try\u002Ffinally` so it survives exceptions in the body.\n",{"id":348,"difficulty":150,"q":349,"a":350},"exitstack","What problem does contextlib.ExitStack solve?","**`ExitStack`** manages a **dynamic\u002Fvariable number** of context managers —\nwhen you don't know how many you'll open (e.g. a list of files). You\n`enter_context()` each, and all are unwound in reverse order when the stack\nexits, even on error.\n\n```python\nfrom contextlib import ExitStack\nwith ExitStack() as stack:\n    files = [stack.enter_context(open(p)) for p in paths]\n    # all files closed automatically at block end\n```\n\nRule of thumb: use `ExitStack` when the set of resources is computed at\nruntime rather than known statically.\n",{"id":352,"difficulty":150,"q":353,"a":354},"reusable-vs-reentrant","Can you reuse a context manager instance across multiple with blocks?","It depends on the implementation. A **`@contextmanager` generator is\nsingle-use** — its generator is exhausted after one `with`, so reusing it\nraises `RuntimeError`. A class-based manager can be reusable (or even\nreentrant) **if you design `__enter__`\u002F`__exit__` to support it**, like\n`threading.Lock`.\n\n```python\nfrom contextlib import contextmanager\n@contextmanager\ndef cm(): yield\nc = cm()\nwith c: ...\nwith c: ...          # RuntimeError: generator already executed\n```\n\nRule of thumb: assume `@contextmanager` objects are one-shot; create a fresh\none per `with` unless the manager explicitly supports reuse.\n",{"id":356,"difficulty":162,"q":357,"a":358},"enter-return-value","What determines the value bound by `as` in a with statement?","It's whatever **`__enter__` returns**. It is **not** necessarily the context\nmanager itself — e.g. `open()` returns the file object, while a lock's\n`__enter__` often returns `None`. Forgetting to `return self`\u002Fthe resource is\na common bug.\n\n```python\nclass Conn:\n    def __enter__(self):\n        self.handle = connect()\n        return self.handle          # this is what `as` binds\n    def __exit__(self, *a):\n        self.handle.close()\n\nwith Conn() as h:                   # h is the handle, not Conn\n    h.query(...)\n```\n\nRule of thumb: design `__enter__` to return the object the user actually needs\ninside the block.\n",{"id":360,"difficulty":150,"q":361,"a":362},"async-context-manager","What is an async context manager?","One implementing **`__aenter__`\u002F`__aexit__`** (coroutines), driven by **`async\nwith`**. It's for setup\u002Fteardown that involves awaiting — opening an async DB\nconnection or HTTP session. `contextlib.asynccontextmanager` provides the\ngenerator form.\n\n```python\nfrom contextlib import asynccontextmanager\n@asynccontextmanager\nasync def session():\n    s = await open_session()\n    try:\n        yield s\n    finally:\n        await s.close()\n\nasync with session() as s: ...\n```\n\nRule of thumb: use async context managers whenever acquiring or releasing the\nresource itself requires `await`.\n",{"id":364,"difficulty":162,"q":365,"a":366},"nested-vs-parenthesized","How do you open several context managers in one with statement?","Separate them with commas. They enter **left to right** and exit **right to\nleft**. Python 3.10+ allows **parenthesized** multi-line groups for\nreadability. This avoids deep nesting of `with` blocks.\n\n```python\nwith open(\"in.txt\") as fin, open(\"out.txt\", \"w\") as fout:\n    fout.write(fin.read())\n\n# 3.10+ parenthesized form:\nwith (\n    open(\"a\") as a,\n    open(\"b\") as b,\n):\n    ...\n```\n\nRule of thumb: combine related managers in one `with`; for a runtime-sized\nlist, use `ExitStack` instead.\n",{"id":368,"difficulty":150,"q":369,"a":370},"contextmanager-exception-inject","How do you handle an exception inside a @contextmanager generator?","When the `with` body raises, the exception is **re-raised at the `yield`**\ninside the generator. You can `try\u002Fexcept` around the `yield` to react; to\n**suppress** it, simply catch it and don't re-raise (the generator returning\nnormally suppresses). Re-raise (or don't catch) to let it propagate.\n\n```python\nfrom contextlib import contextmanager\n@contextmanager\ndef ignore_value_error():\n    try:\n        yield\n    except ValueError:\n        pass                         # suppresses ValueError from the body\n\nwith ignore_value_error():\n    raise ValueError(\"gone\")         # swallowed\n```\n\nRule of thumb: catch at the `yield` to observe or suppress body exceptions;\nlet them propagate by not catching.\n",{"description":148},"Python interview questions on context managers and the with statement, __enter__\u002F__exit__, contextlib.contextmanager, exception handling in __exit__, and multiple context managers.","python\u002Fexceptions\u002Fcontext-managers","Context Managers & with","6xUDsIscbgy1KbqOaWQLVEqUT__feFGqBSFXbtBFBqU",{"id":377,"title":378,"body":379,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":383,"navigation":153,"order":13,"path":384,"questions":385,"questionsCount":217,"related":218,"seo":446,"seoDescription":447,"stem":448,"subtopic":449,"topic":72,"topicSlug":74,"updated":223,"__hash__":450},"qa\u002Fpython\u002Ffunctional\u002Ffunctools.md","Functools",{"type":145,"value":380,"toc":381},[],{"title":148,"searchDepth":29,"depth":29,"links":382},[],{},"\u002Fpython\u002Ffunctional\u002Ffunctools",[386,390,394,398,402,406,410,414,418,422,426,430,434,438,442],{"id":387,"difficulty":162,"q":388,"a":389},"lru-cache","What does functools.lru_cache do?","**`functools.lru_cache`** is a decorator that **memoizes** a function — it stores\nresults keyed by the arguments and returns the cached value on repeat calls,\navoiding recomputation. `maxsize` caps how many results are kept, evicting the\n**least-recently-used** entries; `lru_cache(maxsize=None)` (or `functools.cache`\nin 3.9+) caches without limit.\n\n```python\nfrom functools import lru_cache\n\n@lru_cache(maxsize=None)\ndef fib(n):\n    return n if n \u003C 2 else fib(n - 1) + fib(n - 2)\n\nfib(50)                 # fast — each n computed once\nfib.cache_info()        # hits, misses, maxsize, currsize\nfib.cache_clear()       # reset the cache\n```\n\nArguments must be **hashable** (they're used as dict keys), and the function should\nbe **pure** — caching an impure function returns stale results. Rule of thumb: use\nit for expensive, deterministic calls with repeated inputs.\n",{"id":391,"difficulty":162,"q":392,"a":393},"partial","What is functools.partial used for?","**`functools.partial`** creates a new callable with some arguments of an existing\nfunction **pre-filled**. It's a clean way to specialize a general function without\nwriting a wrapper or a `lambda`.\n\n```python\nfrom functools import partial\n\ndef power(base, exp):\n    return base ** exp\n\nsquare = partial(power, exp=2)   # exp fixed to 2\ncube   = partial(power, exp=3)\n\nsquare(5)    # 25\ncube(2)      # 8\n```\n\nPartials are handy for callbacks, event handlers, and configuring functions passed\nto `map`\u002F`sorted`\u002FGUI bindings. Rule of thumb: reach for `partial` when you keep\ncalling the same function with one or two fixed arguments.\n",{"id":395,"difficulty":162,"q":396,"a":397},"wraps","Why do you use functools.wraps when writing a decorator?","A decorator replaces the original function with a wrapper, which **loses the\noriginal's metadata** — its `__name__`, `__doc__`, and signature now point at the\nwrapper. **`functools.wraps`** copies that metadata from the wrapped function onto\nthe wrapper, so introspection, debugging, and documentation still work.\n\n```python\nfrom functools import wraps\n\ndef log(fn):\n    @wraps(fn)                 # copy fn's metadata to wrapper\n    def wrapper(*args, **kwargs):\n        print(\"calling\", fn.__name__)\n        return fn(*args, **kwargs)\n    return wrapper\n\n@log\ndef greet(): \"say hi\"\ngreet.__name__   # 'greet'  (without @wraps it'd be 'wrapper')\n```\n\nWithout `@wraps`, tools like `help()`, tracebacks, and doc generators show the\nwrapper instead of the real function. Rule of thumb: always add `@wraps(fn)` to the\ninner function of any decorator.\n",{"id":399,"difficulty":162,"q":400,"a":401},"reduce","What does functools.reduce do?","**`functools.reduce`** repeatedly applies a two-argument function across an iterable,\n**folding** it down to a single accumulated value. It carries a running result,\ncombining it with each element in turn; an optional **initializer** seeds the\naccumulator (and makes it safe on empty iterables).\n\n```python\nfrom functools import reduce\n\nreduce(lambda acc, x: acc + x, [1, 2, 3, 4])      # 10\nreduce(lambda acc, x: acc * x, [1, 2, 3, 4], 1)   # 24, seeded with 1\n```\n\nFor common folds Python already has built-ins (`sum`, `max`, `min`, `any`, `all`)\nthat are clearer and faster — `reduce` shines for custom accumulation logic. Rule\nof thumb: prefer a built-in or an explicit loop unless the fold is genuinely\nbespoke, since `reduce` can hurt readability.\n",{"id":403,"difficulty":150,"q":404,"a":405},"cached-property-singledispatch","What are cached_property and singledispatch?","**`functools.cached_property`** turns a method into a property whose result is\n**computed once and stored on the instance**, so later accesses are cheap. The\ncached value lives in the instance `__dict__` and is recomputed only if you delete\nit. **`functools.singledispatch`** creates a **generic function** that dispatches to\ndifferent implementations based on the **type of the first argument** — function\noverloading by type.\n\n```python\nfrom functools import cached_property, singledispatch\n\nclass Dataset:\n    @cached_property\n    def stats(self):           # expensive; runs once per instance\n        return expensive_scan(self.data)\n\n@singledispatch\ndef describe(x): return f\"value: {x}\"\n@describe.register\ndef _(x: list): return f\"list of {len(x)}\"\n@describe.register\ndef _(x: int): return f\"int {x}\"\n\ndescribe([1, 2])   # 'list of 2'\ndescribe(7)        # 'int 7'\n```\n\n`cached_property` trades memory for speed on costly, stable computations;\n`singledispatch` keeps type-specific behaviour in separate, registerable functions\ninstead of a big `if\u002Fisinstance` chain. Rule of thumb: cache derived values that\ndon't change, and dispatch when behaviour varies cleanly by argument type.\n",{"id":407,"difficulty":162,"q":408,"a":409},"cache-vs-lru-cache","What is functools.cache and how does it differ from lru_cache?","**`@cache`** (3.9+) is an **unbounded** memoizer — shorthand for\n`lru_cache(maxsize=None)`. **`@lru_cache(maxsize=N)`** keeps only the N most\nrecent results, evicting the least-recently-used. Unbounded is simpler and\nslightly faster but can grow memory without limit.\n\n```python\nfrom functools import cache, lru_cache\n\n@cache                       # never evicts\ndef fib(n): ...\n\n@lru_cache(maxsize=128)      # bounded\ndef fetch(url): ...\n```\n\nRule of thumb: use `cache` for small\u002Ffinite key spaces; `lru_cache(maxsize)`\nwhen the key space is large and you must cap memory.\n",{"id":411,"difficulty":162,"q":412,"a":413},"lru-cache-hashable","What are the constraints on lru_cache arguments?","All arguments must be **hashable**, because the cache keys on them — so you\ncan't memoize a function taking a `list` or `dict`. The cached function also\nexposes **`.cache_info()`** (hits\u002Fmisses) and **`.cache_clear()`**. Mutable\ndefault results are shared, so don't mutate returned objects.\n\n```python\nfrom functools import lru_cache\n@lru_cache\ndef f(x): ...\n\nf([1, 2])           # TypeError: unhashable type: 'list'\nf.cache_info()      # CacheInfo(hits=.., misses=.., maxsize=.., currsize=..)\nf.cache_clear()\n```\n\nRule of thumb: only memoize pure functions with hashable args; convert lists\nto tuples before calling.\n",{"id":415,"difficulty":162,"q":416,"a":417},"total-ordering","What does functools.total_ordering do?","It **fills in the missing rich-comparison methods** from the ones you define.\nProvide `__eq__` plus one of `__lt__`\u002F`__le__`\u002F`__gt__`\u002F`__ge__`, and the\ndecorator generates the rest. Saves boilerplate, at a small performance cost\nversus writing them all by hand.\n\n```python\nfrom functools import total_ordering\n@total_ordering\nclass Version:\n    def __init__(self, n): self.n = n\n    def __eq__(self, o): return self.n == o.n\n    def __lt__(self, o): return self.n \u003C o.n\n    # __le__, __gt__, __ge__ generated automatically\n```\n\nRule of thumb: use `total_ordering` to make a class fully orderable from just\n`__eq__` and one ordering method.\n",{"id":419,"difficulty":150,"q":420,"a":421},"partialmethod","What is functools.partialmethod?","Like `partial`, but for **methods** in a class body — it pre-binds arguments\nwhile still receiving `self` correctly. Handy for generating related methods\n(e.g. setters with a fixed state) without repetitive wrappers.\n\n```python\nfrom functools import partialmethod\nclass Cell:\n    def set_state(self, state): self.state = state\n    activate   = partialmethod(set_state, True)\n    deactivate = partialmethod(set_state, False)\n\nc = Cell(); c.activate()      # self.state = True\n```\n\nRule of thumb: use `partialmethod` to derive specialized methods from a\ngeneral one inside a class.\n",{"id":423,"difficulty":162,"q":424,"a":425},"reduce-with-initial","Why pass an initializer to functools.reduce?","The third argument seeds the accumulator. Without it, `reduce` raises\n**`TypeError` on an empty iterable** and uses the first element as the seed.\nAn initializer gives a safe default for empty input and sets the result type.\n\n```python\nfrom functools import reduce\nreduce(lambda a, b: a + b, [], 0)        # 0, not an error\nreduce(lambda a, b: a + b, [])           # TypeError: empty iterable\n\nreduce(lambda acc, x: acc | {x}, items, set())   # build a set\n```\n\nRule of thumb: always pass an initializer to `reduce` when the iterable might\nbe empty or you want a specific starting type.\n",{"id":427,"difficulty":150,"q":428,"a":429},"singledispatch-register","How do you register implementations with singledispatch?","Decorate the generic function with **`@singledispatch`**, then add type-specific\nversions with **`@func.register`** (annotate the first parameter's type or pass\nit explicitly). Dispatch is on the **first argument's type**. There's also\n`singledispatchmethod` for methods.\n\n```python\nfrom functools import singledispatch\n@singledispatch\ndef show(x): return str(x)\n\n@show.register\ndef _(x: list): return \", \".join(map(str, x))\n\n@show.register(int)\ndef _(x): return f\"int:{x}\"\n```\n\nRule of thumb: use `singledispatch` to add type-based behavior to a function\nwithout a chain of `isinstance` checks.\n",{"id":431,"difficulty":162,"q":432,"a":433},"cached-property-vs-property","How does cached_property differ from property?","**`@property`** recomputes on every access; **`@cached_property`** computes\n**once**, then **stores the result in the instance `__dict__`**, returning it\ndirectly thereafter. It needs a writable `__dict__` (so it doesn't work with\n`__slots__`) and isn't recomputed if dependencies change.\n\n```python\nfrom functools import cached_property\nclass Dataset:\n    @cached_property\n    def stats(self):\n        return expensive_scan(self.data)   # computed on first access only\n```\n\nRule of thumb: use `cached_property` for expensive, **stable** derived values;\nuse `property` when the value can change between accesses.\n",{"id":435,"difficulty":162,"q":436,"a":437},"wraps-what-it-copies","What exactly does functools.wraps copy?","It copies the wrapped function's **`__name__`, `__doc__`, `__module__`,\n`__qualname__`, `__dict__`, and `__annotations__`** onto the wrapper, and sets\n**`__wrapped__`** to point at the original. Without it, decorated functions\nreport the wrapper's name and lose their docstring, breaking introspection and\n`help()`.\n\n```python\nfrom functools import wraps\ndef log(fn):\n    @wraps(fn)\n    def inner(*a, **k):\n        return fn(*a, **k)\n    return inner\n\n@log\ndef greet(): \"say hi\"\ngreet.__name__       # 'greet', not 'inner'\ngreet.__wrapped__    # the original function\n```\n\nRule of thumb: always apply `@wraps(fn)` to the inner function in a decorator\nto preserve metadata.\n",{"id":439,"difficulty":162,"q":440,"a":441},"partial-vs-lambda","When is functools.partial better than a lambda?","`partial` **binds arguments at definition time** and is picklable,\nintrospectable (`.func`, `.args`, `.keywords`), and avoids the **late-binding\nclosure trap** of lambdas in loops. A lambda re-evaluates free variables when\ncalled, which can surprise you.\n\n```python\nfrom functools import partial\n# lambda late-binding bug:\nfns = [lambda: i for i in range(3)]      # all return 2\n# partial captures now:\nfns = [partial(lambda x: x, i) for i in range(3)]   # 0, 1, 2\n```\n\nRule of thumb: prefer `partial` when you need to pre-bind args reliably\n(loops, callbacks, pickling); a lambda is fine for trivial inline logic.\n",{"id":443,"difficulty":253,"q":444,"a":445},"reduce-vs-sum","When should you NOT use reduce?","When a **built-in or comprehension** is clearer: use `sum`, `min`, `max`,\n`math.prod`, `\"\".join`, `all`\u002F`any` instead of `reduce`. `reduce` is justified\nonly for genuinely custom accumulations with no built-in equivalent — and even\nthen a plain loop is often more readable.\n\n```python\nfrom functools import reduce\nreduce(lambda a, b: a + b, nums)     # just use sum(nums)\nreduce(lambda a, b: a * b, nums)     # use math.prod(nums)\n```\n\nRule of thumb: reach for a built-in first; use `reduce` only for non-standard\nfolds, and prefer a loop if it reads more clearly.\n",{"description":148},"Python interview questions on functools — lru_cache and cache, partial, wraps, reduce, cached_property, and singledispatch.","python\u002Ffunctional\u002Ffunctools","functools","tZ-vv3Dlmsj1sG7H3LgJ-oLRcVMhDY2eIhcHNDZYNSY",{"id":452,"title":453,"body":454,"description":148,"difficulty":150,"extension":151,"framework":10,"frameworkSlug":8,"meta":458,"navigation":153,"order":13,"path":459,"questions":460,"questionsCount":217,"related":218,"seo":521,"seoDescription":522,"stem":523,"subtopic":453,"topic":45,"topicSlug":47,"updated":223,"__hash__":524},"qa\u002Fpython\u002Ffunctions\u002Fdecorators.md","Decorators",{"type":145,"value":455,"toc":456},[],{"title":148,"searchDepth":29,"depth":29,"links":457},[],{},"\u002Fpython\u002Ffunctions\u002Fdecorators",[461,465,469,473,477,481,485,489,493,497,501,505,509,513,517],{"id":462,"difficulty":162,"q":463,"a":464},"what-is-decorator","What is a decorator and how does it work?","A **decorator** is a **callable that takes a function and returns a (usually\nwrapped) function**, letting you add behaviour **without modifying** the original.\nThe `@decorator` syntax above a `def` is just sugar for **reassigning the name** to\nthe decorator's result: `func = decorator(func)`.\n\n```python\ndef log_calls(func):\n    def wrapper(*args, **kwargs):    # accept any signature\n        print(f\"calling {func.__name__}\")\n        return func(*args, **kwargs) # delegate to the original\n    return wrapper\n\n@log_calls\ndef add(a, b):\n    return a + b\n# equivalent to: add = log_calls(add)\n\nadd(2, 3)   # prints \"calling add\", returns 5\n```\n\nThis works because functions are **first-class objects** — they can be passed\naround and returned. Decorators are the idiomatic way to factor out\n**cross-cutting concerns** (logging, timing, caching, access control).\n",{"id":466,"difficulty":150,"q":467,"a":468},"functools-wraps","Why should you use functools.wraps in a decorator?","Without it, the wrapper **replaces the original function's identity**: the\ndecorated object reports the **wrapper's** `__name__`, `__doc__`, signature, and\n`__module__`, which breaks introspection, debugging, and tools that rely on\nmetadata. **`functools.wraps`** copies that metadata from the original onto the\nwrapper.\n\n```python\nimport functools\n\ndef log_calls(func):\n    @functools.wraps(func)       # copy name, docstring, __wrapped__, etc.\n    def wrapper(*args, **kwargs):\n        return func(*args, **kwargs)\n    return wrapper\n\n@log_calls\ndef greet():\n    \"say hello\"\n    ...\n\ngreet.__name__   # \"greet\"  (without wraps -> \"wrapper\")\ngreet.__doc__    # \"say hello\"\n```\n\nIt also sets `__wrapped__`, so `inspect.signature` and unwrapping still work.\nRule of thumb: **always** apply `@functools.wraps(func)` to your wrapper — it's\neffectively free and prevents subtle bugs.\n",{"id":470,"difficulty":150,"q":471,"a":472},"decorator-with-arguments","How do you write a decorator that takes arguments?","You add **another layer of nesting**: an outer function takes the **decorator's\narguments** and returns the actual decorator, which takes the function and returns\nthe wrapper. So `@repeat(3)` first **calls** `repeat(3)` to get a decorator, which\nis then applied to the function.\n\n```python\nimport functools\n\ndef repeat(n):                       # takes the decorator argument\n    def decorator(func):             # takes the function\n        @functools.wraps(func)\n        def wrapper(*args, **kwargs):\n            for _ in range(n):\n                result = func(*args, **kwargs)\n            return result\n        return wrapper\n    return decorator\n\n@repeat(3)                           # repeat(3) returns 'decorator'\ndef ping():\n    print(\"pong\")\n```\n\nThe mental model: `@repeat(3)` is `ping = repeat(3)(ping)` — three calls deep.\nRemember the parentheses: `@repeat(3)` (with args) differs from `@repeat` (passing\nthe function directly), and forgetting them is a common bug.\n",{"id":474,"difficulty":150,"q":475,"a":476},"class-based-decorator","How do you implement a decorator as a class?","A class becomes a decorator by being **callable** — define **`__call__`**. The\n`__init__` receives the decorated function; `__call__` runs the wrapping logic on\neach invocation. This is handy when the decorator needs to **hold state** (like a\ncall count) in a clean, attribute-based way.\n\n```python\nimport functools\n\nclass CountCalls:\n    def __init__(self, func):\n        functools.update_wrapper(self, func)  # the class-based wraps\n        self.func = func\n        self.count = 0\n    def __call__(self, *args, **kwargs):\n        self.count += 1\n        print(f\"call #{self.count}\")\n        return self.func(*args, **kwargs)\n\n@CountCalls\ndef hello():\n    print(\"hi\")\n\nhello(); hello()      # \"call #1\" then \"call #2\"\nhello.count           # 2 — state lives on the instance\n```\n\nUse `functools.update_wrapper` (the function-form of `wraps`) to preserve\nmetadata. Class decorators shine for **stateful** decorators; for simple stateless\nones, a nested function with a `nonlocal` closure is usually lighter.\n",{"id":478,"difficulty":162,"q":479,"a":480},"stacking-decorators","When you stack multiple decorators, in what order do they apply?","Decorators apply **bottom-up** (nearest the function first) at **definition\ntime**, but the resulting wrappers **execute top-down** at **call time**. Stacking\nis just nested application: the top decorator wraps the result of the ones below\nit.\n\n```python\n@a\n@b\ndef f(): ...\n# equivalent to: f = a(b(f))   — b wraps first, a wraps outermost\n\ndef bold(fn):\n    return lambda: \"\u003Cb>\" + fn() + \"\u003C\u002Fb>\"\ndef italic(fn):\n    return lambda: \"\u003Ci>\" + fn() + \"\u003C\u002Fi>\"\n\n@bold\n@italic\ndef text():\n    return \"hi\"\n\ntext()   # \"\u003Cb>\u003Ci>hi\u003C\u002Fi>\u003C\u002Fb>\"  — bold is outer, runs around italic\n```\n\nSo the **closest** decorator is applied first but its logic runs **innermost**.\nOrder matters whenever decorators have side effects or transform results — e.g.\nput `@staticmethod` outermost, or `@app.route` above `@login_required` so auth\nruns before the view.\n",{"id":482,"difficulty":253,"q":483,"a":484},"decorator-syntax-sugar","What does the @ decorator syntax desugar to?","**`@dec`** above a definition is just `func = dec(func)` — the decorator is\ncalled with the function and its **return value rebinds the name**. That's the\nwhole mechanism; everything else is convention.\n\n```python\n@log\ndef greet(): ...\n# identical to:\ndef greet(): ...\ngreet = log(greet)\n```\n\nRule of thumb: read `@dec` as \"replace the name with `dec(name)`\".\n",{"id":486,"difficulty":162,"q":487,"a":488},"preserve-metadata","What breaks if you forget functools.wraps in a decorator?","The decorated function reports the **wrapper's** `__name__`, `__doc__`, and\nsignature instead of the original's — breaking `help()`, debuggers,\nintrospection, and some frameworks (e.g. ones that read function names for\nrouting). `@wraps(fn)` copies that metadata over.\n\n```python\nfrom functools import wraps\ndef log(fn):\n    @wraps(fn)               # without this, greet.__name__ == 'inner'\n    def inner(*a, **k): return fn(*a, **k)\n    return inner\n```\n\nRule of thumb: always wrap the inner function with `@wraps(fn)` so the\ndecorated function keeps its identity.\n",{"id":490,"difficulty":150,"q":491,"a":492},"decorator-with-without-args","How do you write a decorator that works with or without arguments?","Make the argument optional and detect whether the first positional is the\nfunction itself. If called as `@dec` the function is passed directly; if\n`@dec(...)` it isn't. Use a keyword-only config plus a `func=None` check.\n\n```python\nfrom functools import wraps, partial\ndef retry(func=None, *, times=3):\n    if func is None:\n        return partial(retry, times=times)   # called as @retry(times=5)\n    @wraps(func)\n    def inner(*a, **k):\n        for _ in range(times):\n            try: return func(*a, **k)\n            except Exception: pass\n    return inner\n```\n\nRule of thumb: return `partial(dec, **opts)` when the function slot is empty,\nso both `@dec` and `@dec(...)` work.\n",{"id":494,"difficulty":162,"q":495,"a":496},"class-decorator","Can you decorate a class, and what does a class decorator do?","Yes — a **class decorator** receives the class and returns a (usually modified)\nclass. It's used to register classes, inject methods\u002Fattributes, or wrap them.\n`@dataclass` is the canonical example. It runs **after** the class body\nexecutes.\n\n```python\nregistry = {}\ndef register(cls):\n    registry[cls.__name__] = cls\n    return cls\n\n@register\nclass Plugin: ...\n```\n\nRule of thumb: use class decorators to augment or register a class without\nsubclassing or a metaclass.\n",{"id":498,"difficulty":162,"q":499,"a":500},"stacking-order","In what order do stacked decorators apply?","They apply **bottom-up** at definition time (the nearest decorator wraps\nfirst), but **execute top-down** at call time (the outermost runs first).\n`@a @b def f` means `a(b(f))`.\n\n```python\n@bold          # outer: runs second when called, wraps last\n@italic        # inner: runs first when called, wraps first\ndef text(): return \"hi\"\n# text = bold(italic(text))\n```\n\nRule of thumb: read the stack as nested calls — bottom decorator is innermost,\ntop is outermost.\n",{"id":502,"difficulty":162,"q":503,"a":504},"property-as-decorator","How is property a decorator?","**`@property`** turns a method into a **managed attribute** with getter\nsemantics; **`@x.setter`** and **`@x.deleter`** add write\u002Fdelete behavior. It's\na descriptor that runs your method on attribute access, enabling computed or\nvalidated attributes without changing call sites.\n\n```python\nclass C:\n    @property\n    def value(self): return self._v\n    @value.setter\n    def value(self, v):\n        if v \u003C 0: raise ValueError\n        self._v = v\n\nc = C(); c.value = 5        # calls the setter\n```\n\nRule of thumb: use `@property` to expose computed\u002Fvalidated attributes that\nlook like plain attribute access.\n",{"id":506,"difficulty":162,"q":507,"a":508},"decorator-side-effects","When does a decorator's code run?","The decorator **call** (and any setup outside the wrapper) runs **once, at\nimport\u002Fdefinition time**. Only the **inner wrapper** runs on each call. So\nregistration, validation, or logging placed in the decorator body executes\nwhen the module loads, not per call.\n\n```python\ndef trace(fn):\n    print(\"decorating\", fn.__name__)   # runs at import\n    def inner(*a, **k):\n        print(\"calling\")               # runs each call\n        return fn(*a, **k)\n    return inner\n```\n\nRule of thumb: put per-call logic in the inner wrapper; one-time setup goes in\nthe decorator body.\n",{"id":510,"difficulty":162,"q":511,"a":512},"staticmethod-classmethod-decorators","How do staticmethod and classmethod work as decorators?","**`@staticmethod`** makes a method that takes **no implicit first arg** — just\na namespaced plain function. **`@classmethod`** passes the **class** as `cls`,\nenabling alternative constructors and class-level behavior. Both are\ndescriptors applied via decorator syntax.\n\n```python\nclass Date:\n    @classmethod\n    def today(cls):           # cls = Date (or a subclass)\n        return cls(...)\n    @staticmethod\n    def is_leap(y):           # no self\u002Fcls\n        return y % 4 == 0\n```\n\nRule of thumb: `classmethod` for alternative constructors\u002Ffactory methods;\n`staticmethod` for utility functions logically grouped under a class.\n",{"id":514,"difficulty":162,"q":515,"a":516},"decorator-state","How can a decorator maintain state across calls?","Store it in the **enclosing closure** (a captured variable) or on the\n**wrapper function's attributes**. A class-based decorator can keep state on\n`self`. Use this for counters, caches, or rate limiters.\n\n```python\nfrom functools import wraps\ndef count_calls(fn):\n    @wraps(fn)\n    def inner(*a, **k):\n        inner.calls += 1\n        return fn(*a, **k)\n    inner.calls = 0\n    return inner\n```\n\nRule of thumb: keep decorator state in the closure or on the wrapper\u002F`self`,\nnot in globals.\n",{"id":518,"difficulty":150,"q":519,"a":520},"parametrized-decorator-structure","What is the three-level structure of a decorator with arguments?","It's **factory → decorator → wrapper**: the outermost function takes the\n**arguments** and returns a decorator; that decorator takes the **function**\nand returns the **wrapper** that runs at call time. Three nested `def`s.\n\n```python\nfrom functools import wraps\ndef repeat(n):                       # 1) takes args\n    def decorator(fn):               # 2) takes the function\n        @wraps(fn)\n        def wrapper(*a, **k):        # 3) runs at call time\n            for _ in range(n):\n                r = fn(*a, **k)\n            return r\n        return wrapper\n    return decorator\n\n@repeat(3)\ndef hi(): print(\"hi\")\n```\n\nRule of thumb: a parametrized decorator needs three layers — remember\nargs-layer, function-layer, call-layer.\n",{"description":148},"Python interview questions on decorators, functools.wraps, decorators with arguments, class-based decorators, stacking order, and real-world use cases.","python\u002Ffunctions\u002Fdecorators","AbQjQTao-96FAyQube8U7Wb2NigM3q8JtcaSwMpw-Lo",{"id":526,"title":527,"body":528,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":532,"navigation":153,"order":13,"path":533,"questions":534,"questionsCount":666,"related":218,"seo":667,"seoDescription":668,"stem":669,"subtopic":670,"topic":20,"topicSlug":21,"updated":671,"__hash__":672},"qa\u002Fpython\u002Ffundamentals\u002Fmutability.md","Mutability",{"type":145,"value":529,"toc":530},[],{"title":148,"searchDepth":29,"depth":29,"links":531},[],{},"\u002Fpython\u002Ffundamentals\u002Fmutability",[535,539,543,547,551,555,559,563,567,571,575,579,583,587,591,595,599,603,607,611,615,619,623,626,630,634,638,642,646,650,654,658,662],{"id":536,"difficulty":253,"q":537,"a":538},"mutable-immutable","Which Python types are mutable and which are immutable?","**Immutable** (the value can never change in place — any \"change\" makes a new\nobject): `int`, `float`, `complex`, `bool`, `str`, `tuple`, `frozenset`,\n`bytes`, and `None`.\n\n**Mutable** (can be modified in place): `list`, `dict`, `set`, `bytearray`,\nand most custom class instances.\n\n```python\ns = \"hello\"\nprint(id(s))\ns += \" world\"        # looks like mutation...\nprint(id(s))         # ...but id() changed — a NEW string was created\n\nnums = [1, 2, 3]\nprint(id(nums))\nnums.append(4)       # genuine in-place mutation\nprint(id(nums))      # same id — same object\n```\n\nWhy it matters: mutability drives how **assignment, function arguments, and\n`==`\u002F`is` behave**. Immutable objects are also **hashable** (usable as dict\nkeys \u002F set members), while mutable ones generally aren't.\n",{"id":540,"difficulty":150,"q":541,"a":542},"default-arg","What is the mutable default argument trap?","A function's default argument is **evaluated once, when the `def` statement\nruns** — not on each call. So a mutable default (like `[]` or `{}`) is created\na single time and **shared across every call**, accumulating state between\ninvocations.\n\n```python\ndef add(item, bucket=[]):   # the same list object on every call\n    bucket.append(item)\n    return bucket\n\nadd(1)   # [1]\nadd(2)   # [1, 2]  \u003C- surprise! the list persisted\nadd(3)   # [1, 2, 3]\n```\n\nThe fix is the standard `None` sentinel — use `None` as the default and create\na fresh object inside the body:\n\n```python\ndef add(item, bucket=None):\n    if bucket is None:      # new list per call\n        bucket = []\n    bucket.append(item)\n    return bucket\n```\n",{"id":544,"difficulty":162,"q":545,"a":546},"is-vs-eq","What is the difference between `is` and `==`?","`==` tests **value equality** — \"do these represent the same data?\" — by\ncalling the object's `__eq__`. `is` tests **identity** — \"are these the *exact\nsame object* in memory?\" — comparing `id()`s. They often agree, but not always.\n\n```python\na = [1, 2, 3]\nb = [1, 2, 3]\na == b   # True  — equal contents\na is b   # False — two distinct list objects\n\nc = a\nc is a   # True  — same object\n```\n\n**Rule: use `is` only for singletons** — `None`, `True`, `False` (e.g.\n`if x is None:`). Don't use `is` for numbers or strings: small ints and some\nstrings *appear* to work because CPython caches\u002Finterns them\n(`256 is 256` -> `True`, but `1000 is 1000` can be `False`), which is an\nimplementation detail you shouldn't rely on.\n",{"id":548,"difficulty":162,"q":549,"a":550},"shallow-deep-copy","What is the difference between a shallow and a deep copy?","A **shallow copy** (`copy.copy`, `list(x)`, `x[:]`, `dict(x)`) creates a new\nouter container but **copies references** to the nested objects — so the inner\nobjects are still **shared**. A **deep copy** (`copy.deepcopy`) recursively\ncopies everything, producing a fully **independent** structure.\n\n```python\nimport copy\noriginal = [[1, 2], [3, 4]]\n\nshallow = copy.copy(original)\nshallow[0].append(99)\nprint(original)   # [[1, 2, 99], [3, 4]]  \u003C- inner list was shared!\n\ndeep = copy.deepcopy(original)\ndeep[0].append(99)\nprint(original)   # unchanged — fully independent\n```\n\nUse a shallow copy when the elements are immutable (or sharing is fine); reach\nfor `deepcopy` when you have nested mutable structures and need true isolation\n(note it's slower and handles cycles).\n",{"id":552,"difficulty":162,"q":553,"a":554},"tuple-mutable","Can a tuple contain mutable objects?","Yes. A tuple's **immutability is shallow**: you can't reassign or resize its\nslots, but each slot is just a reference, and if that reference points to a\nmutable object, that object can still be changed in place.\n\n```python\nt = (1, [2, 3])\nt[1].append(4)     # allowed — mutating the list inside\nprint(t)           # (1, [2, 3, 4])\n\nt[1] = [9]         # TypeError — can't reassign a tuple slot\n```\n\nA consequence interviewers love: a tuple containing a list is **not\nhashable**, because hashability requires all contents to be immutable — so\n`hash((1, [2]))` raises `TypeError`, and such a tuple can't be a dict key.\n",{"id":556,"difficulty":150,"q":557,"a":558},"hashable","Why can't you use a list as a dictionary key?","Dictionary keys (and set members) must be **hashable**. Hashing requires that an\nobject's hash value **stays constant for its lifetime**, which in practice means\nit must be **immutable**. Lists are mutable, so Python deliberately makes them\n**unhashable** — they have no `__hash__`.\n\n```python\nd = {}\nd[[1, 2]] = 'x'   # TypeError: unhashable type: 'list'\nd[(1, 2)] = 'x'   # tuples are immutable -> hashable\n```\n\nThe reason is correctness: a dict places a key in a bucket based on its hash. If\na key could mutate after insertion, its hash would change and you'd never be\nable to find it again. Use an immutable equivalent — a **tuple** instead of a\nlist, a `frozenset` instead of a set.\n",{"id":560,"difficulty":162,"q":561,"a":562},"id-function","What does the id() function tell you?","`id(obj)` returns a unique integer **identity** for an object — in CPython, its\nmemory address. Two names with the same `id` refer to the **same object**; `is`\nis essentially an `id` comparison.\n\n```python\na = [1, 2]\nb = a\nid(a) == id(b)   # True  — same object\na is b           # True\nc = [1, 2]\nid(a) == id(c)   # False — equal value, different object\n```\n\n`id` is useful for understanding aliasing and why mutation through one name is\nvisible through another. The actual value is implementation-specific (don't rely\non it being an address).\n",{"id":564,"difficulty":150,"q":565,"a":566},"small-int-cache","Why does `is` sometimes work for equal integers?","CPython **pre-caches small integers** from **−5 to 256** as singletons, so equal\nvalues in that range share one object and `is` returns `True`. Outside that range,\nequal integers are usually distinct objects.\n\n```python\na = 256\nb = 256\na is b      # True  — cached\n\nc = 257\nd = 257\nc is d      # False — separate objects (in a REPL)\nc == d      # True  — always compare values with ==\n```\n\nThis is a CPython implementation detail, **not** a language guarantee. Never use\n`is` to compare numbers — use `==`. `is` is only for singletons like `None`.\n",{"id":568,"difficulty":150,"q":569,"a":570},"string-interning","What is string interning in Python?","CPython **interns** some strings — storing one shared copy — so identical\nstring literals can be the same object. Short, identifier-like strings are\ninterned automatically; others may not be.\n\n```python\na = \"hello\"\nb = \"hello\"\na is b           # True  — interned literal\n\nc = \"hello world!\"\nd = \"hello world!\"\nc is d           # often False (not auto-interned)\n\nimport sys\ne = sys.intern(\"hello world!\")  # force interning\n```\n\nLike int caching, this is an optimization detail. Always compare string **values**\nwith `==`, not identity with `is`.\n",{"id":572,"difficulty":150,"q":573,"a":574},"augmented-tuple","What happens when you use += on a list inside a tuple?","You get a surprising result: the list **is** mutated, **and** a `TypeError` is\nraised. `t[0] += [3]` does `t[0] = t[0] + [3]` — the `+=` extends the list in place\n(succeeds), then tries to reassign the tuple slot (fails, since tuples are\nimmutable).\n\n```python\nt = ([1, 2], 'x')\nt[0] += [3]      # TypeError: 'tuple' object does not support item assignment\nprint(t)         # ([1, 2, 3], 'x')  — the list WAS extended!\n```\n\nSo the mutation happens before the assignment error. Use `t[0].extend([3])` if you\nwant to mutate the inner list without the confusing error.\n",{"id":576,"difficulty":162,"q":577,"a":578},"list-slicing-copy","Does slicing a list create a copy?","Yes — slicing produces a **new (shallow) list** containing the same element\nreferences. `lst[:]` is a common idiom for a shallow copy. But the **elements**\nthemselves are shared, so nested mutables are still linked.\n\n```python\na = [1, 2, 3]\nb = a[:]          # shallow copy\nb.append(4)\na                 # [1, 2, 3] — unaffected\n\nnested = [[1], [2]]\ncopy = nested[:]\ncopy[0].append(9)\nnested            # [[1, 9], [2]] — inner list shared\n```\n\nSlicing copies the outer list only; use `copy.deepcopy` for full independence of\nnested structures.\n",{"id":580,"difficulty":150,"q":581,"a":582},"pass-by-object","Is Python pass-by-value or pass-by-reference?","Neither exactly — Python is **pass-by-object-reference** (a.k.a. \"pass by\nassignment\"). The function receives a reference to the same object, so it can\n**mutate** a mutable argument in place, but **rebinding** the parameter doesn't\naffect the caller.\n\n```python\ndef mutate(lst): lst.append(4)     # caller sees this\ndef rebind(lst): lst = [0]         # caller does NOT see this\n\ndata = [1, 2, 3]\nmutate(data); print(data)  # [1, 2, 3, 4]\nrebind(data); print(data)  # [1, 2, 3, 4] (unchanged)\n```\n\nImmutable args (ints, strings, tuples) can't be mutated, so they *appear*\npass-by-value. The key is mutate-in-place vs reassign-the-name.\n",{"id":584,"difficulty":150,"q":585,"a":586},"deepcopy-cycles","How does deepcopy handle circular references?","`copy.deepcopy` tracks already-copied objects in a **memo dictionary**, so it\nhandles **circular references** without infinite recursion — each object is copied\nonce and reused.\n\n```python\nimport copy\na = [1, 2]\na.append(a)            # a contains itself\nb = copy.deepcopy(a)   # works — no infinite loop\nb[2] is b              # True — the cycle is preserved in the copy\n```\n\nA naive recursive copy would loop forever; `deepcopy`'s memo makes it safe. You\ncan customize copying via `__deepcopy__`\u002F`__copy__` methods on your classes.\n",{"id":588,"difficulty":162,"q":589,"a":590},"is-pitfall","When is using `is` instead of `==` a bug?","Using `is` to compare **values** is a bug — it tests identity, which only\ncoincidentally matches for cached singletons (small ints, interned strings,\n`None`). It fails unpredictably for other values.\n\n```python\nx = 1000\nx is 1000        # may be False (and raises a SyntaxWarning in 3.8+)\nx == 1000        # True\n\na = \"long string value\"\na is \"long string value\"  # often False\n```\n\nRule: use `==` for value equality; reserve `is` for `None`, `True`, `False`, and\nsentinel objects. Linters flag `is` comparisons with literals.\n",{"id":592,"difficulty":150,"q":593,"a":594},"custom-hashable","How do you make a custom class hashable?","Implement both `__eq__` and `__hash__`, keeping them **consistent**: equal objects\nmust have equal hashes. Base the hash on the same immutable fields used for\nequality.\n\n```python\nclass Point:\n    def __init__(self, x, y):\n        self.x, self.y = x, y\n    def __eq__(self, other):\n        return (self.x, self.y) == (other.x, other.y)\n    def __hash__(self):\n        return hash((self.x, self.y))\n\n{Point(1, 2), Point(1, 2)}   # one element — treated as equal\n```\n\nDefining `__eq__` **without** `__hash__` makes the class **unhashable** (Python\nsets `__hash__ = None`). Only hash on fields that don't change after creation.\n",{"id":596,"difficulty":162,"q":597,"a":598},"frozen-dataclass","What is a frozen dataclass?","A `@dataclass(frozen=True)` makes instances **immutable** — attempting to set an\nattribute raises `FrozenInstanceError`. Frozen dataclasses also get a `__hash__`\nautomatically, so they're usable as dict keys \u002F set members.\n\n```python\nfrom dataclasses import dataclass\n\n@dataclass(frozen=True)\nclass Point:\n    x: int\n    y: int\n\np = Point(1, 2)\np.x = 9          # FrozenInstanceError\n{p: \"origin\"}    # hashable\n```\n\nIt's the concise modern way to define immutable value objects with auto-generated\n`__init__`, `__eq__`, `__repr__`, and `__hash__`.\n",{"id":600,"difficulty":253,"q":601,"a":602},"string-immutability","Why are strings immutable in Python?","Once created, a `str` can't be changed — any \"modification\" returns a **new**\nstring. Immutability enables interning, safe use as dict keys (cached hash), thread\nsafety, and predictable behavior.\n\n```python\ns = \"hello\"\ns[0] = \"H\"           # TypeError: 'str' does not support item assignment\ns = s.replace(\"h\", \"H\")  # new string, rebind\ns += \" world\"        # new string each time\n```\n\nRepeated concatenation in a loop creates many throwaway strings — prefer\n`\"\".join(parts)` for efficiency, the Python analog of `StringBuilder`.\n",{"id":604,"difficulty":162,"q":605,"a":606},"tuple-vs-list","What is the difference between a tuple and a list?","- **`list`** — mutable, variable-length, for **homogeneous, changing** sequences.\n- **`tuple`** — immutable, fixed, for **heterogeneous, fixed records** (and\n  hashable, so usable as dict keys).\n\n```python\npoint = (3, 4)        # fixed record — tuple\nscores = [90, 85]     # changing collection — list\nscores.append(70)     #\npoint[0] = 9          # tuples are immutable\n\nd = {(0, 0): \"origin\"}  # tuple key ; list key would fail\n```\n\nTuples are slightly faster and more memory-efficient, and signal \"this shouldn't\nchange.\" Use a list when you need to add\u002Fremove\u002Freorder.\n",{"id":608,"difficulty":150,"q":609,"a":610},"mutate-during-iteration","What happens if you modify a list while iterating over it?","Modifying a list's size during iteration **skips or repeats elements** because the\ninternal index shifts under you — a classic bug (Python doesn't always raise, it\nsilently misbehaves; dicts\u002Fsets *do* raise `RuntimeError`).\n\n```python\nnums = [1, 2, 3, 4]\nfor n in nums:\n    if n % 2 == 0:\n        nums.remove(n)   # skips elements\nprint(nums)              # [1, 3] sometimes wrong for other inputs\n\nnums = [n for n in nums if n % 2]      # build a new list\n```\n\nIterate over a **copy** (`for n in nums[:]`) or, better, build a new list with a\ncomprehension\u002F`filter`.\n",{"id":612,"difficulty":162,"q":613,"a":614},"del-statement","What does the del statement do?","`del` **unbinds a name** (or removes an item\u002Fslice\u002Fattribute) — it doesn't directly\n\"delete\" the object. The object is garbage-collected only when its **reference\ncount hits zero**.\n\n```python\na = [1, 2, 3]\nb = a\ndel a            # unbinds 'a'; the list still lives (b references it)\nprint(b)         # [1, 2, 3]\n\ndel b[0]         # removes an item -> [2, 3]\n```\n\nSo `del a` removes the *name*, not necessarily the value. For container items it\nmutates the container. After `del a`, referencing `a` raises `NameError`.\n",{"id":616,"difficulty":162,"q":617,"a":618},"rebind-vs-mutate","What is the difference between rebinding and mutating?","**Rebinding** points a name at a **new** object (`x = [...]`); it doesn't affect\nother names pointing at the old object. **Mutating** changes an object **in\nplace** (`x.append(...)`); all names referencing it see the change.\n\n```python\na = [1, 2]\nb = a\na.append(3)   # mutate -> b sees it; b == [1, 2, 3]\na = [9]       # rebind -> b unchanged; b == [1, 2, 3]\n```\n\nThis distinction explains most \"why did my other variable change?\" confusion.\nAliases share mutations but not rebinding.\n",{"id":620,"difficulty":150,"q":621,"a":622},"class-mutable-attr","Why is a mutable class attribute shared across instances?","A mutable value assigned at **class level** (not in `__init__`) is **one object\nshared by every instance**. Mutating it through one instance affects all of them —\na common bug.\n\n```python\nclass Cart:\n    items = []          # shared by ALL instances\n    def add(self, x): self.items.append(x)\n\na, b = Cart(), Cart()\na.add(\"apple\")\nb.items                 # ['apple'] — leaked into b!\n\nclass Cart:\n    def __init__(self):\n        self.items = []  # per-instance\n```\n\nInitialize mutable attributes in `__init__` so each instance gets its own.\n",{"id":624,"difficulty":150,"q":258,"a":625},"list-mult-trap","List multiplication copies the **references**, not the objects — so `[[]] * 3`\ncreates three references to the **same** inner list. Mutating one mutates all.\n\n```python\ngrid = [[]] * 3\ngrid[0].append(1)\nprint(grid)          # [[1], [1], [1]] — all share one list!\n\ngrid = [[] for _ in range(3)]  # three distinct lists\ngrid[0].append(1)\nprint(grid)          # [[1], [], []]\n```\n\nThe same applies to `[0] * 3` (fine for immutable ints) vs `[[]] * 3` (broken for\nmutables). Use a comprehension to get independent inner objects.\n",{"id":627,"difficulty":162,"q":628,"a":629},"namedtuple","What is a namedtuple and when do you use it?","`collections.namedtuple` (or `typing.NamedTuple`) creates an **immutable**\ntuple subclass with **named fields** — readable, hashable, lightweight records.\n\n```python\nfrom collections import namedtuple\nPoint = namedtuple(\"Point\", [\"x\", \"y\"])\np = Point(3, 4)\np.x          # 3  — named access\np[0]         # 3  — still index-accessible\np.x = 9      # immutable\n```\n\nIt's great for returning multiple values with clear names while keeping tuple\nsemantics. For more features (defaults, methods, mutability options), a\n`@dataclass` is the modern alternative.\n",{"id":631,"difficulty":150,"q":632,"a":633},"global-nonlocal","What do the global and nonlocal keywords do?","They let you **rebind** a name from an outer scope. `global` targets module-level\nnames; `nonlocal` targets the nearest enclosing function scope. Without them,\nassignment inside a function creates a **new local** instead.\n\n```python\ncount = 0\ndef inc():\n    global count\n    count += 1        # rebinds the module-level count\n\ndef outer():\n    x = 1\n    def inner():\n        nonlocal x\n        x = 2          # rebinds outer's x\n    inner()\n    return x           # 2\n```\n\nNote you only need them to **reassign** — you can *mutate* an outer mutable object\n(e.g. `list.append`) without `global`\u002F`nonlocal`.\n",{"id":635,"difficulty":253,"q":636,"a":637},"copy-methods","What are the ways to copy a list or dict?","Several produce a **shallow** copy; `copy.deepcopy` is the only deep one.\n\n```python\na = [1, 2, 3]\na[:]            # slice copy\na.copy()        # method\nlist(a)         # constructor\n\nd = {\"x\": 1}\nd.copy()        # method\ndict(d)         # constructor\n{**d}           # unpacking\n\nimport copy\ncopy.deepcopy(a)  # deep — independent nested objects\n```\n\nAll the shallow methods share nested mutable elements; choose `deepcopy` when you\nneed full independence (at a performance cost).\n",{"id":639,"difficulty":253,"q":640,"a":641},"none-identity","Why check for None with `is` rather than ==?","`None` is a **singleton** — there's exactly one `None` object — so `is None` is the\ncorrect, fast, and idiomatic identity check. `== None` can be **overridden** by a\ncustom `__eq__`, giving wrong or surprising results.\n\n```python\nif x is None:        # idiomatic, can't be fooled\n    ...\n\nclass Weird:\n    def __eq__(self, other): return True\nWeird() == None      # True  misleading\nWeird() is None      # False\n```\n\nPEP 8 explicitly recommends `is`\u002F`is not` for `None` comparisons.\n",{"id":643,"difficulty":162,"q":644,"a":645},"set-hashable-elements","Why must set elements be hashable?","Like dict keys, set members are stored by **hash** for O(1) membership tests, so\nthey must be **hashable** (and thus effectively immutable). Lists, dicts, and sets\ncan't be set elements; tuples and frozensets can.\n\n```python\n{1, 2, 3}            #\n{[1], [2]}           # TypeError: unhashable type: 'list'\n{(1, 2), (3, 4)}     # tuples are hashable\n{frozenset({1, 2})}  #\n```\n\nIf you need a set of sets, use `frozenset` for the inner ones. The hashability\nrequirement is the same reason lists can't be dict keys.\n",{"id":647,"difficulty":162,"q":648,"a":649},"comprehension-scope","Do comprehensions leak their loop variable?","In **Python 3**, comprehensions have their **own scope**, so the loop variable does\n**not** leak into the surrounding scope (unlike a regular `for` loop, and unlike\nPython 2).\n\n```python\nsquares = [i * i for i in range(5)]\nprint(i)        # NameError — i is local to the comprehension\n\nfor j in range(5):\n    pass\nprint(j)        # 4 — a normal for loop DOES leak\n```\n\nThis avoids accidental variable clobbering. The same isolation applies to set,\ndict, and generator comprehensions.\n",{"id":651,"difficulty":253,"q":652,"a":653},"tuple-unpacking-swap","How does tuple unpacking enable swapping variables?","Python evaluates the **right side first** into a tuple, then unpacks it into the\nleft-side names — so you can swap without a temporary variable.\n\n```python\na, b = 1, 2\na, b = b, a        # builds (2, 1), then unpacks -> a=2, b=1\n\n# also works for multiple\u002Fextended unpacking:\nfirst, *rest = [1, 2, 3, 4]   # first=1, rest=[2, 3, 4]\n```\n\nThe right-hand tuple is fully created before any assignment, which is why the swap\nis atomic and needs no temp. This is immutability of the intermediate tuple at\nwork.\n",{"id":655,"difficulty":162,"q":656,"a":657},"shallow-nested-trap","What is the trap with shallow-copying nested structures?","A shallow copy duplicates the outer container but **shares the nested objects**, so\nmutating a nested element changes both the original and the copy — a subtle\naliasing bug.\n\n```python\nimport copy\noriginal = {\"users\": [\"ada\"]}\nshallow = copy.copy(original)\nshallow[\"users\"].append(\"grace\")\noriginal[\"users\"]    # ['ada', 'grace'] — shared nested list!\n\ndeep = copy.deepcopy(original)\ndeep[\"users\"].append(\"hopper\")\noriginal[\"users\"]    # unchanged\n```\n\nUse `deepcopy` whenever you copy a structure with nested mutables you intend to\nmodify independently.\n",{"id":659,"difficulty":162,"q":660,"a":661},"frozenset","What is a frozenset?","A `frozenset` is an **immutable** version of `set` — same operations\n(union, intersection, membership) but no `add`\u002F`remove`. Because it's immutable,\nit's **hashable**, so it can be a dict key or an element of another set.\n\n```python\nfs = frozenset([1, 2, 3])\nfs.add(4)              # AttributeError — immutable\n{fs: \"a set\"}          # hashable key\n{frozenset({1}), frozenset({2})}  # set of sets\n```\n\nUse it for constant sets and whenever you need a set-like value that must be\nhashable.\n",{"id":663,"difficulty":150,"q":664,"a":665},"eq-hash-contract","What is the relationship between __eq__ and __hash__?","They must stay **consistent**: if `a == b`, then `hash(a) == hash(b)`. Otherwise\nhash-based containers (dict, set) misbehave — an object you stored becomes\nunfindable.\n\n```python\nclass Money:\n    def __init__(self, cents): self.cents = cents\n    def __eq__(self, o): return self.cents == o.cents\n    # defined __eq__ but not __hash__ -> unhashable\n{Money(100)}   # TypeError: unhashable type: 'Money'\n```\n\nDefining `__eq__` sets `__hash__` to `None` automatically (making instances\nunhashable) unless you also define `__hash__`. Hash only on fields that never\nchange after creation, or make the object immutable.\n",33,{"description":148},"Python interview questions on mutable vs immutable types, the mutable default argument trap, is vs ==, and shallow vs deep copy.","python\u002Ffundamentals\u002Fmutability","Mutability & Data Types","2026-06-17","YTto0t6-cyDWT7KyZi85sVFf15mozs7-qABsDx3-GzA",{"id":674,"title":675,"body":676,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":680,"navigation":153,"order":13,"path":681,"questions":682,"questionsCount":217,"related":218,"seo":742,"seoDescription":743,"stem":744,"subtopic":745,"topic":135,"topicSlug":137,"updated":223,"__hash__":746},"qa\u002Fpython\u002Fidioms\u002Feafp-lbyl.md","Eafp Lbyl",{"type":145,"value":677,"toc":678},[],{"title":148,"searchDepth":29,"depth":29,"links":679},[],{},"\u002Fpython\u002Fidioms\u002Feafp-lbyl",[683,687,691,695,699,703,707,711,714,718,722,726,730,734,738],{"id":684,"difficulty":253,"q":685,"a":686},"eafp-lbyl-meaning","What do EAFP and LBYL mean?","**EAFP** = \"**Easier to Ask Forgiveness than Permission**\": just **attempt**\nthe operation and handle the exception if it fails. **LBYL** = \"**Look Before\nYou Leap**\": **check** the preconditions first, then act only if the checks\npass. They are two styles for handling things that *might* go wrong.\n\n```python\n# LBYL — check first\nif \"key\" in config:\n    value = config[\"key\"]\n\n# EAFP — try and handle failure\ntry:\n    value = config[\"key\"]\nexcept KeyError:\n    value = default\n```\n\nEAFP is the **Pythonic** default — it reads naturally and avoids redundant\nchecks. Rule of thumb: in Python, **try the operation and catch the specific\nexception** rather than pre-validating every condition.\n",{"id":688,"difficulty":162,"q":689,"a":690},"why-eafp-preferred","Why is EAFP preferred in Python?","EAFP fits Python's design: exceptions are **cheap** and **idiomatic**, and the\nstyle avoids **duplicating logic**. With LBYL you often check a condition and\nthen perform the same lookup\u002Foperation again, doing the work twice. EAFP also\nstays correct when an object merely **behaves like** the expected type\n(duck typing) rather than passing an explicit type check.\n\n```python\n# LBYL duplicates the access and breaks on duck-typed objects\nif hasattr(obj, \"read\") and callable(obj.read):\n    data = obj.read()\n\n# EAFP — just use it; let the exception surface real problems\ntry:\n    data = obj.read()\nexcept AttributeError:\n    data = None\n```\n\nThe happy path stays uncluttered and the error handling is explicit. Rule of\nthumb: write the **common case** as straight-line code and catch the\n**specific** exception for the rare failure.\n",{"id":692,"difficulty":150,"q":693,"a":694},"lbyl-race-condition","What race condition does LBYL invite?","LBYL introduces a **TOCTOU** bug — \"**Time Of Check to Time Of Use**\". Between\nthe moment you **check** a condition and the moment you **act** on it, another\nthread or process can change the state, so the check is **stale** and the\naction fails or corrupts data. EAFP avoids the gap by acting atomically and\nhandling failure.\n\n```python\nimport os\n# LBYL — file can be deleted between the check and the open (race!)\nif os.path.exists(path):\n    with open(path) as f:    # may still raise FileNotFoundError\n        data = f.read()\n\n# EAFP — no window; the open either works or raises\ntry:\n    with open(path) as f:\n        data = f.read()\nexcept FileNotFoundError:\n    data = None\n```\n\nThe check-then-act pattern is unsafe in concurrent or filesystem contexts.\nRule of thumb: for files, sockets, and shared state, **attempt the operation**\nand handle the exception rather than checking first.\n",{"id":696,"difficulty":162,"q":697,"a":698},"dict-get-vs-check","When should you use dict.get versus checking for a key?","`dict.get(key, default)` returns the value if present and the **default**\n(`None` if unspecified) otherwise — a clean one-liner that avoids both the\n`if key in d` check **and** a `try\u002Fexcept KeyError`. Use a membership check or\n`try\u002Fexcept` only when you must **distinguish a missing key from a stored\n`None`**, or run different logic in each branch.\n\n```python\ncounts = {\"a\": 1}\ncounts.get(\"b\")          # None — no KeyError\ncounts.get(\"b\", 0)       # 0    — supply a default\n\n# need to tell \"missing\" from \"stored None\"? then check explicitly\nif \"b\" in counts:\n    ...\n# or accumulate with setdefault \u002F defaultdict\ncounts.setdefault(\"c\", 0)\n```\n\nFor counting\u002Fgrouping, `collections.defaultdict` or `Counter` is even cleaner.\nRule of thumb: reach for `.get()` with a default for \"value or fallback\", and\nonly branch explicitly when missing-ness itself is meaningful.\n",{"id":700,"difficulty":150,"q":701,"a":702},"eafp-pitfalls","What are the pitfalls of EAFP, and how do you avoid them?","EAFP done carelessly causes two problems. First, a **too-broad `except`**\n(bare `except:` or `except Exception`) can **swallow unrelated bugs** —\ncatching a `KeyError` you didn't intend, or hiding a `NameError`. Second, the\n`try` block should wrap **only** the line that can fail, so you don't\naccidentally catch exceptions from surrounding code.\n\n```python\n# Bad — hides real errors and over-wide try block\ntry:\n    value = config[\"key\"]\n    result = expensive_call(value)   # its errors get caught too!\nexcept Exception:\n    value = default\n\n# Good — narrow exception, minimal try body\ntry:\n    value = config[\"key\"]\nexcept KeyError:\n    value = default\nresult = expensive_call(value)       # outside the try\n```\n\nAlways catch the **most specific** exception and keep the `try` body small.\nRule of thumb: EAFP means \"catch the *one* expected failure\", never \"catch\neverything and hope\".\n",{"id":704,"difficulty":162,"q":705,"a":706},"when-lbyl-better","When is LBYL actually the better choice?","LBYL wins when the **check is cheap and the failure is expensive or common**, when\nyou'd otherwise catch a too-broad exception, or when validating **user input** up\nfront gives clearer errors. If most attempts would fail, paying for exceptions each\ntime is wasteful.\n\n```python\n# LBYL is clearer here — validate before a costly operation\nif not isinstance(age, int) or age \u003C 0:\n    raise ValueError(\"age must be a non-negative int\")\nprocess(age)\n\n# LBYL avoids catching an over-broad exception you can't distinguish\nif denominator != 0:\n    result = numerator \u002F denominator\n```\n\nRule of thumb: prefer EAFP by default, but use LBYL for cheap pre-validation,\ninput checking, or when the \"failure\" isn't a clean single exception.\n",{"id":708,"difficulty":162,"q":709,"a":710},"getattr-eafp","How does `getattr` with a default express EAFP for attributes?","`getattr(obj, \"name\", default)` is the attribute analogue of `dict.get` — it returns\nthe attribute if present, else the default, **without** a `try\u002Fexcept AttributeError`\nor a separate `hasattr` check (which duplicates the lookup).\n\n```python\n# verbose LBYL\nif hasattr(obj, \"timeout\"):\n    t = obj.timeout\nelse:\n    t = 30\n\n# concise — single lookup, default fallback\nt = getattr(obj, \"timeout\", 30)\n```\n\nRule of thumb: use `getattr(obj, name, default)` for \"attribute or fallback\"; reserve\n`hasattr`\u002F`try` for when presence itself drives different logic.\n",{"id":336,"difficulty":162,"q":712,"a":713},"How does `contextlib.suppress` clean up EAFP code?","`contextlib.suppress(Exc)` is a context manager that **silently ignores** the named\nexception(s) — a tidy replacement for `try\u002Fexcept Exc: pass` when you genuinely want\nto skip a failure.\n\n```python\nfrom contextlib import suppress\n\n# instead of:\ntry:\n    os.remove(path)\nexcept FileNotFoundError:\n    pass\n\n# write:\nwith suppress(FileNotFoundError):\n    os.remove(path)\n```\n\nRule of thumb: use `suppress` for \"do this, ignore if it fails\" — but only for\nspecific exceptions you truly mean to discard, never a blanket `Exception`.\n",{"id":715,"difficulty":162,"q":716,"a":717},"eafp-duck-typing","How does EAFP support duck typing better than type checks?","EAFP **uses** an object and lets it work if it has the right behavior — so any\nduck-typed object qualifies. LBYL with `isinstance` rejects valid objects that aren't\nthe exact type but behave correctly, defeating duck typing.\n\n```python\n# LBYL — rejects anything not literally a list, even list-like objects\nif isinstance(x, list):\n    x.append(1)\n\n# EAFP — works for any object supporting append (list, deque, custom)\ntry:\n    x.append(1)\nexcept AttributeError:\n    ...\n```\n\nRule of thumb: EAFP asks \"can it do what I need?\" not \"is it the exact type?\" —\nkeeping code flexible across compatible types.\n",{"id":719,"difficulty":150,"q":720,"a":721},"eafp-performance","Is EAFP or LBYL faster?","It depends on the **failure rate**. Setting up a `try` is nearly free when no\nexception fires, so EAFP is **faster when failures are rare**. But **raising and\ncatching** an exception is relatively expensive, so LBYL wins when failures are\n**frequent**.\n\n```python\n# rare misses -> EAFP is faster (no exception in the common case)\ntry:\n    v = cache[key]\nexcept KeyError:\n    v = compute(key)        # only occasionally\n\n# frequent misses -> a membership check avoids many raised exceptions\nv = cache[key] if key in cache else compute(key)\n```\n\nRule of thumb: EAFP for the \"usually succeeds\" case; switch to LBYL only when\nprofiling shows exceptions are firing often enough to matter.\n",{"id":723,"difficulty":162,"q":724,"a":725},"else-clause-eafp","How does the `try\u002Fexcept\u002Felse` clause refine EAFP?","The **`else`** block runs only if the `try` body raised **no** exception, letting you\nkeep the `try` minimal (just the risky line) while putting the \"on success\" code\nwhere it can't be accidentally caught.\n\n```python\ntry:\n    value = config[\"key\"]\nexcept KeyError:\n    value = default\nelse:\n    # runs only if the lookup succeeded — not inside the try\n    log(f\"found {value}\")\n```\n\nRule of thumb: use `else` to separate \"the risky operation\" from \"what to do on\nsuccess\", keeping the `try` body as small as possible.\n",{"id":727,"difficulty":162,"q":728,"a":729},"defaultdict-vs-get","When is `defaultdict` better than `dict.get` for accumulation?","For **building up** collections (grouping, counting), `defaultdict` auto-creates the\nmissing value so you can mutate it directly — cleaner than `get`\u002F`setdefault` in a\nloop. `get` is for read-with-fallback, not in-place accumulation.\n\n```python\nfrom collections import defaultdict\n\ngroups = defaultdict(list)\nfor name in names:\n    groups[name[0]].append(name)   # no key-existence check needed\n\n# vs the clunkier get\u002Fsetdefault style:\n# groups.setdefault(name[0], []).append(name)\n```\n\nRule of thumb: `defaultdict(list\u002Fint\u002Fset)` for accumulation loops; `dict.get` for a\none-off \"value or default\" read.\n",{"id":731,"difficulty":162,"q":732,"a":733},"specific-exception-eafp","Why does EAFP depend on catching the right specific exception?","EAFP only works safely if you catch the **exact** exception the operation can raise.\nCatching too broadly hides bugs; catching the wrong type lets the real failure\nescape. Knowing each operation's exceptions is part of writing good EAFP.\n\n```python\n# int() raises ValueError, not KeyError — catch the right one\ntry:\n    n = int(user_input)\nexcept ValueError:\n    n = 0\n\n# list indexing raises IndexError; dict access raises KeyError\n```\n\nRule of thumb: match the `except` to the operation's documented exception\n(`ValueError`, `KeyError`, `IndexError`, `AttributeError`) — generic `Exception`\ndefeats the point.\n",{"id":735,"difficulty":162,"q":736,"a":737},"validation-at-boundaries","Where should LBYL-style validation live in an application?","Validate (LBYL) at the **boundaries** — where untrusted input enters (API requests,\nCLI args, file parsing) — then trust the data internally and use EAFP for the rare\nruntime failure. This concentrates checks and keeps core logic clean.\n\n```python\ndef handle_request(payload):\n    # boundary: validate up front, fail fast with clear errors\n    if \"user_id\" not in payload:\n        raise ValueError(\"user_id required\")\n    user_id = int(payload[\"user_id\"])\n    # internal code can now assume user_id is a valid int (EAFP for the rest)\n    return load_user(user_id)\n```\n\nRule of thumb: LBYL at the edges for clear input errors; EAFP in the interior where\ndata is already trusted.\n",{"id":739,"difficulty":150,"q":740,"a":741},"walrus-eafp-loop","How can the walrus operator support concise EAFP-style loops?","The walrus `:=` lets you **assign and test in one expression**, so you can act on a\nvalue only when it's present — a clean \"try to get it, use it if you got it\" pattern\nwithout a separate check line.\n\n```python\n# process queue items until exhausted\nwhile (item := queue.get()) is not None:\n    handle(item)\n\n# use a match only if found\nif (m := pattern.search(text)):\n    print(m.group())\n```\n\nRule of thumb: `:=` collapses \"fetch, then check the fetched value\" into one step —\nhandy for loop conditions and guard clauses without re-computing.\n",{"description":148},"Python interview questions on EAFP vs LBYL, why try\u002Fexcept is preferred, the race conditions LBYL invites, and dict.get versus checking for a key.","python\u002Fidioms\u002Feafp-lbyl","EAFP vs LBYL","bBegrXRhXS6-KMvk9WAVayJWs6DSBzS8XB-v-Ss2_U0",{"id":748,"title":749,"body":750,"description":148,"difficulty":150,"extension":151,"framework":10,"frameworkSlug":8,"meta":754,"navigation":153,"order":13,"path":755,"questions":756,"questionsCount":217,"related":218,"seo":817,"seoDescription":818,"stem":819,"subtopic":820,"topic":99,"topicSlug":101,"updated":223,"__hash__":821},"qa\u002Fpython\u002Finternals\u002Fgarbage-collection.md","Garbage Collection",{"type":145,"value":751,"toc":752},[],{"title":148,"searchDepth":29,"depth":29,"links":753},[],{},"\u002Fpython\u002Finternals\u002Fgarbage-collection",[757,761,765,769,773,777,781,785,789,793,797,801,805,809,813],{"id":758,"difficulty":162,"q":759,"a":760},"reference-counting","How does reference counting work in Python?","Every CPython object carries a **reference count** — the number of references\npointing at it. The count goes **up** when you bind a new name, append it to a\ncontainer, or pass it to a function, and **down** when a name is reassigned, goes\nout of scope, or is `del`'d. When the count hits **zero**, the object is\n**immediately deallocated**.\n\n```python\na = [1, 2, 3]    # the list's refcount is 1\nb = a            # 2 — another reference\ndel a            # 1 — still alive (b references it)\nb = None         # 0 — list is freed right away\n```\n\nThis is **deterministic** and prompt — memory is reclaimed the instant the last\nreference disappears, not at some later sweep.\n\nWhy it matters: reference counting handles the vast majority of garbage, but it\n**can't free reference cycles** on its own, which is why CPython adds a second\ncollector.\n",{"id":762,"difficulty":150,"q":763,"a":764},"cyclic-gc","Why does Python need a cyclic garbage collector?","Reference counting fails on **reference cycles** — objects that refer to each other,\nso their counts never reach zero even when nothing outside the cycle reaches them.\nWithout help they'd leak forever. CPython's **cyclic garbage collector** (the `gc`\nmodule) periodically finds and frees these unreachable cycles.\n\n```python\nimport gc\na, b = {}, {}\na['b'] = b        # a -> b\nb['a'] = a        # b -> a   (a cycle)\ndel a, b          # both refcounts stay at 1 — NOT freed by refcounting\ngc.collect()      # the cyclic collector reclaims the unreachable cycle\n```\n\nThe collector works by tracking **container** objects (lists, dicts, instances) and\ndetecting groups whose only references are internal to the group.\n\nRule of thumb: you rarely call `gc.collect()` manually — it runs automatically —\nbut it's worth knowing cycles are collected **later**, not immediately like refcounts.\n",{"id":766,"difficulty":162,"q":767,"a":768},"sys-getrefcount","What does sys.getrefcount tell you and why is it always higher than expected?","`sys.getrefcount(obj)` returns the object's current **reference count**. It almost\nalways reads **one higher** than you expect because **passing the object as the\nargument** creates a temporary reference for the duration of the call.\n\n```python\nimport sys\nx = []\nsys.getrefcount(x)   # 2 — one for `x`, one for the argument itself\n\ny = x\nsys.getrefcount(x)   # 3 — `x`, `y`, and the argument\n```\n\nSmall ints and interned strings show huge counts because they're **cached\nsingletons** shared across the whole interpreter.\n\nWhy it matters: it's a debugging\u002Fteaching tool for understanding aliasing and leaks\n— just remember to **subtract one** for the call's own reference, and don't rely on\nexact values across implementations.\n",{"id":770,"difficulty":150,"q":771,"a":772},"weak-references","What is a weak reference and when do you use one?","A **weak reference** (`weakref` module) points to an object **without increasing its\nreference count**, so it does **not** keep the object alive. When the last *strong*\nreference is gone, the object is collected and the weak reference returns `None`.\n\n```python\nimport weakref\nclass Node: pass\n\nn = Node()\nr = weakref.ref(n)   # weak — doesn't count toward n's lifetime\nr()                  # \u003CNode object> — still alive\ndel n                # last strong ref gone -> object freed\nr()                  # None — referent is dead\n```\n\nThey're ideal for **caches** and **observer\u002Fparent-child back-references** where you\nwant to reference an object but not prevent its cleanup — `weakref.WeakValueDictionary`\nis a common cache type.\n\nRule of thumb: use a weak reference to **break a cycle** or avoid a memory leak when\none object should not own the lifetime of another.\n",{"id":774,"difficulty":150,"q":775,"a":776},"del-pitfalls","What are the pitfalls of the __del__ method?","`__del__` is a **finalizer** called when an object is about to be destroyed — but\nits timing is **not guaranteed**. It runs only when the refcount hits zero (or later,\nvia the cyclic collector), so you can't rely on **when**, or even **whether**, it\nruns. Note `del obj` only decrements the refcount; it doesn't directly call `__del__`.\n\n```python\nclass Resource:\n    def __del__(self):\n        print(\"cleaning up\")   # may run late, or not at all on interpreter exit\n\nr = Resource()\nr2 = r\ndel r                          # nothing happens — r2 still references it\ndel r2                         # NOW refcount is 0 -> __del__ runs\n```\n\nObjects in a reference cycle that define `__del__` historically couldn't be\ncollected at all (improved in Python 3.4+ via PEP 442), and exceptions raised inside\n`__del__` are **ignored**.\n\nRule of thumb: don't use `__del__` for important cleanup — use **context managers\n(`with`)** or `try\u002Ffinally`, which give deterministic, explicit release.\n",{"id":778,"difficulty":150,"q":779,"a":780},"generational-gc","What is generational garbage collection?","The cyclic collector is **generational**: it sorts tracked objects into **three\ngenerations (0, 1, 2)** based on how many collections they've survived. New objects\nstart in **generation 0**, which is scanned **most frequently**; survivors are\n**promoted** to older generations that are scanned **less often**.\n\nThe idea is the **weak generational hypothesis**: most objects die young, so it's\nefficient to focus collection effort on the youngest generation and rarely re-scan\nlong-lived objects.\n\n```python\nimport gc\ngc.get_count()        # (gen0, gen1, gen2) allocation counters\ngc.get_threshold()    # (700, 10, 10) — triggers per generation\ngc.collect(0)         # collect only generation 0 (cheap, frequent)\n```\n\nA generation-0 collection is fast and common; full (generation-2) collections are\nrarer and more expensive.\n\nRule of thumb: this is mostly automatic, but for latency-sensitive code you can tune\nthresholds, call `gc.collect()` strategically, or `gc.disable()` it if you manage\nlifetimes carefully.\n",{"id":782,"difficulty":162,"q":783,"a":784},"gc-disable-performance","When might you disable the garbage collector?","Disabling the cyclic GC with `gc.disable()` can **reduce latency\u002Fpauses** in\nshort-lived batch jobs or programs that create huge numbers of objects without\ncycles. Reference counting still frees most garbage; you just skip the periodic cycle\nscans.\n\n```python\nimport gc\ngc.disable()           # no cyclic collection pauses\ntry:\n    run_batch_job()    # millions of temp objects, no cycles\nfinally:\n    gc.enable()\n```\n\nRule of thumb: disabling GC helps only when you avoid reference cycles and care about\npause time (or startup); otherwise you risk leaking cycles — re-enable when done.\n",{"id":786,"difficulty":150,"q":787,"a":788},"container-vs-atomic-tracking","Which objects does the cyclic collector track, and which does it ignore?","The GC only tracks **container** objects that can hold references to others (lists,\ndicts, sets, instances, tuples-of-containers). **Atomic** objects like ints, strings,\nand floats can't form cycles, so they're managed purely by reference counting and\nnever tracked.\n\n```python\nimport gc\ngc.is_tracked([])       # True — a container\ngc.is_tracked(42)       # False — atomic\ngc.is_tracked(\"hi\")     # False\ngc.is_tracked((1, 2))   # False — tuple of atomics may be untracked\n```\n\nRule of thumb: only containers participate in cyclic GC; scalars rely on refcounting\nalone, which is why they're freed instantly at refcount zero.\n",{"id":790,"difficulty":150,"q":791,"a":792},"refcount-cextension-bugs","How do reference-counting bugs manifest in C extensions?","C extensions must manually `Py_INCREF`\u002F`Py_DECREF`. **Forgetting an incref** can free\nan object still in use (crash\u002Fuse-after-free); **forgetting a decref** leaks memory.\nThese are a classic source of CPython extension bugs.\n\n```c\n\u002F\u002F missing Py_INCREF -> object freed while still referenced -> segfault\n\u002F\u002F missing Py_DECREF -> refcount never hits 0 -> memory leak\nPy_INCREF(obj);   \u002F\u002F claim a reference\n\u002F\u002F ... use obj ...\nPy_DECREF(obj);   \u002F\u002F release it\n```\n\nRule of thumb: in C extensions, balance every incref with a decref; in pure Python\nyou never manage counts manually — the interpreter does it for you.\n",{"id":794,"difficulty":162,"q":795,"a":796},"gc-callbacks-debug","How can you debug memory leaks with the `gc` module?","The `gc` module exposes introspection: `gc.get_objects()` lists tracked objects,\n`gc.get_referrers`\u002F`get_referents` walk the reference graph, and `gc.set_debug`\nreports uncollectable objects — useful for hunting leaks and lingering cycles.\n\n```python\nimport gc\ngc.set_debug(gc.DEBUG_LEAK)     # report objects that can't be collected\ngc.collect()                    # prints leaked\u002Funcollectable objects\n\ngc.garbage                      # list of uncollectable objects (e.g. cycles)\nlen(gc.get_objects())           # total tracked object count\n```\n\nRule of thumb: use `gc.get_referrers`\u002F`gc.garbage` to find what keeps an object\nalive; combine with `tracemalloc` to locate allocation sites of leaks.\n",{"id":798,"difficulty":162,"q":799,"a":800},"tracemalloc","What is `tracemalloc` used for?","`tracemalloc` traces **where memory was allocated**, letting you snapshot usage and\ndiff snapshots to find leaks and memory hot spots by source line — far more precise\nthan guessing from `gc`.\n\n```python\nimport tracemalloc\ntracemalloc.start()\nsnap1 = tracemalloc.take_snapshot()\nrun_workload()\nsnap2 = tracemalloc.take_snapshot()\nfor stat in snap2.compare_to(snap1, \"lineno\")[:5]:\n    print(stat)        # top growth by file:line\n```\n\nRule of thumb: use `tracemalloc` snapshots\u002Fdiffs to pinpoint the code lines\nresponsible for growing memory, then fix the retained references.\n",{"id":802,"difficulty":162,"q":803,"a":804},"object-overhead","Why does a small Python object use far more memory than its raw value?","Every object carries **per-object overhead**: a refcount, a type pointer, and (for\ncontainers) extra bookkeeping. So an `int` is ~28 bytes, an empty list ~56 bytes —\nthe value itself is tiny but the object header dominates.\n\n```python\nimport sys\nsys.getsizeof(0)       # ~24-28 bytes for a small int\nsys.getsizeof([])      # ~56 bytes empty list\nsys.getsizeof(\"\")      # ~49 bytes empty str\n\n# __slots__ or arrays\u002Fnumpy cut overhead for many small items\n```\n\nRule of thumb: Python objects have heavy headers — for millions of small items use\n`__slots__`, `array`, or NumPy to avoid per-object overhead.\n",{"id":806,"difficulty":162,"q":807,"a":808},"del-statement-vs-method","What exactly does the `del` statement do?","`del name` **removes the binding** and decrements the object's refcount — it does\n**not** directly call `__del__` or free memory unless that was the last reference.\n`del obj.attr` and `del lst[i]` remove an attribute or element.\n\n```python\na = [1, 2, 3]\nb = a\ndel a            # removes name 'a'; list still alive via b (refcount 1)\ndel b            # refcount 0 -> list freed now\n\nd = {\"k\": 1}\ndel d[\"k\"]       # removes the key, not the dict\n```\n\nRule of thumb: `del` unbinds a name (or removes an item\u002Fattr); deallocation happens\nonly when that drops the refcount to zero.\n",{"id":810,"difficulty":150,"q":811,"a":812},"weakref-callback","How can a weakref notify you when its referent dies?","`weakref.ref(obj, callback)` and `weakref.finalize(obj, func, ...)` register a\ncallback invoked **when the object is garbage-collected** — a safe alternative to\n`__del__` for cleanup tied to an object's death.\n\n```python\nimport weakref\nclass Conn: pass\n\nc = Conn()\ndef on_death(ref):\n    print(\"connection collected\")\nr = weakref.ref(c, on_death)\nfinalizer = weakref.finalize(c, print, \"cleanup ran\")\n\ndel c        # triggers on_death and the finalizer\n```\n\nRule of thumb: prefer `weakref.finalize` over `__del__` for cleanup hooks — it's\nexplicit, runs reliably, and avoids resurrecting objects in finalizers.\n",{"id":814,"difficulty":150,"q":815,"a":816},"memory-not-returned-os","Why might freeing objects not return memory to the operating system?","CPython manages small objects through its own **pymalloc arenas\u002Fpools**. Freed memory\noften returns to these pools for **reuse**, not to the OS, so process RSS can stay\nhigh after a spike. Large allocations are more likely to be returned.\n\n```python\n# after creating and deleting a huge list, process memory may not shrink:\nbig = [object() for _ in range(10_000_000)]\ndel big            # memory returns to pymalloc pools, not necessarily the OS\n```\n\nRule of thumb: don't expect RSS to drop after freeing many small objects — to truly\nreclaim memory for a one-off spike, isolate the work in a **subprocess** that exits.\n",{"description":148},"Python interview questions on memory management — reference counting, the cyclic garbage collector and reference cycles, sys.getrefcount, weak references, __del__ pitfalls, and generational GC.","python\u002Finternals\u002Fgarbage-collection","Garbage Collection & Reference Counting","zdw8-6XwCCRwd_LQ5WcoGKuXZxLh24gASC2Nxii--qo",{"id":823,"title":824,"body":825,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":829,"navigation":153,"order":13,"path":830,"questions":831,"questionsCount":217,"related":218,"seo":892,"seoDescription":893,"stem":894,"subtopic":895,"topic":37,"topicSlug":38,"updated":223,"__hash__":896},"qa\u002Fpython\u002Fiteration\u002Fgenerators.md","Generators",{"type":145,"value":826,"toc":827},[],{"title":148,"searchDepth":29,"depth":29,"links":828},[],{},"\u002Fpython\u002Fiteration\u002Fgenerators",[832,836,840,844,848,852,856,860,864,868,872,876,880,884,888],{"id":833,"difficulty":253,"q":834,"a":835},"what-is-generator","What is a generator and what does the yield keyword do?","A **generator** is a function that produces a **lazy sequence** of values one at\na time. Any function containing **`yield`** becomes a generator function: calling\nit doesn't run the body — it returns a **generator object** (an iterator). Each\ntime you call `next()` (or iterate), the body runs until the next `yield`, hands\nback that value, and **pauses**, preserving all local state.\n\n```python\ndef counter():\n    print(\"start\")\n    yield 1\n    yield 2          # execution pauses here between next() calls\n    yield 3\n\ng = counter()        # nothing printed yet — body hasn't run\nnext(g)              # prints \"start\", returns 1\nnext(g)              # returns 2 (resumes after first yield)\n```\n\nWhen the function returns (or falls off the end), a **`StopIteration`** is\nraised to signal exhaustion. Generators are the simplest way to write a custom\n**iterator** without manually implementing `__iter__`\u002F`__next__`.\n",{"id":837,"difficulty":162,"q":838,"a":839},"generator-vs-list-memory","How does a generator save memory compared to a list?","A list **materializes every element in memory at once**, so its footprint grows\nwith the number of items. A generator holds only its **current state** and\ncomputes each value **on demand**, so its memory use is roughly **constant**\nregardless of how many values it ultimately yields.\n\n```python\nimport sys\nnums = [n * n for n in range(1_000_000)]   # ~8 MB list, built eagerly\ngen  = (n * n for n in range(1_000_000))   # lazy — tiny, fixed size\n\nsys.getsizeof(nums)   # large\nsys.getsizeof(gen)    # ~100 bytes, regardless of range\n```\n\nThis makes generators ideal for **large or streaming data** — reading a\nmulti-gigabyte file line by line, or a pipeline of transformations — where you'd\nnever want the whole dataset in RAM. The trade-off: you can only iterate a\ngenerator **once**, and you can't index or `len()` it.\n",{"id":841,"difficulty":162,"q":842,"a":843},"genexp-vs-listcomp","What is the difference between a generator expression and a list comprehension?","They share syntax but differ in their brackets and behaviour. A **list\ncomprehension** uses `[...]` and builds the **entire list eagerly**. A\n**generator expression** uses `(...)` and produces a **lazy iterator** that\nyields values one at a time, computing nothing until consumed.\n\n```python\nlc = [x * 2 for x in range(5)]   # [0, 2, 4, 6, 8] — built now\nge = (x * 2 for x in range(5))   # \u003Cgenerator object> — built on demand\n\n# parentheses are optional when it's the sole argument:\ntotal = sum(x * 2 for x in range(5))   # streams — no temp list\n```\n\nPrefer a **generator expression** when feeding an aggregator like `sum`, `max`,\n`any`, or `join` over a large source — it avoids creating a throwaway list. Use a\n**list comprehension** when you need the full result repeatedly, want to index\nit, or need `len()`.\n",{"id":845,"difficulty":162,"q":846,"a":847},"yield-from","What does yield from do?","**`yield from \u003Citerable>`** **delegates** to a sub-iterator: it yields every\nvalue from the iterable as if you'd written a loop of `yield`s, but also\ntransparently forwards **`send`**, **`throw`**, and the sub-generator's\n**return value**. It's the clean way to **compose** or **flatten** generators.\n\n```python\ndef chain(*iterables):\n    for it in iterables:\n        yield from it          # vs: for x in it: yield x\n\nlist(chain([1, 2], (3, 4)))    # [1, 2, 3, 4]\n\ndef sub():\n    yield 1\n    return 99                  # captured by the delegator\ndef main():\n    result = yield from sub()  # result == 99\n```\n\nBeyond saving a loop, `yield from` is what makes generator **delegation** and\ncoroutine composition possible. Rule of thumb: use it whenever you want one\ngenerator to **fully drain another**.\n",{"id":849,"difficulty":150,"q":850,"a":851},"infinite-generators","How can a generator be infinite, and why doesn't it hang?","Because generators are **lazy**, the body only advances when a value is\nrequested — so an **unbounded loop** is fine: it never tries to produce all\nvalues at once. You control termination from the **consumer** side, by stopping\niteration whenever you've taken enough.\n\n```python\ndef naturals():\n    n = 0\n    while True:        # infinite — but harmless\n        yield n\n        n += 1\n\nfrom itertools import islice\nlist(islice(naturals(), 5))   # [0, 1, 2, 3, 4] — take just 5\n```\n\nThis underpins `itertools.count`, `cycle`, and `repeat`, and lets you model\nstreams elegantly. The danger is forgetting to bound the consumer:\n`list(naturals())` or `for x in naturals(): print(x)` **will** run forever — pair\ninfinite generators with `islice`, a `break`, or `takewhile`.\n",{"id":853,"difficulty":150,"q":854,"a":855},"generator-send","What does the generator `send()` method do?","`send(value)` resumes a paused generator and makes the **`yield` expression evaluate\nto `value`**, turning the generator into a two-way **coroutine**. You must first\n\"prime\" it (advance to the first `yield`) with `next()` or `send(None)`.\n\n```python\ndef accumulator():\n    total = 0\n    while True:\n        x = yield total      # receives value sent in\n        total += x\n\nacc = accumulator()\nnext(acc)            # prime -> yields 0\nacc.send(10)         # 10\nacc.send(5)          # 15\n```\n\nRule of thumb: use `send` for stateful coroutines that consume pushed values; always\nprime with `next()` before the first `send(non-None)`.\n",{"id":857,"difficulty":162,"q":858,"a":859},"generator-return-value","What happens when a generator uses `return`?","A `return` in a generator **stops iteration** and its value becomes the\n**`StopIteration.value`** — not yielded. You normally retrieve it via `yield from`,\nor by catching `StopIteration`.\n\n```python\ndef gen():\n    yield 1\n    yield 2\n    return \"done\"        # not yielded\n\ng = gen()\nnext(g); next(g)\ntry:\n    next(g)\nexcept StopIteration as e:\n    e.value              # 'done'\n\ndef wrapper():\n    result = yield from gen()   # result == 'done'\n```\n\nRule of thumb: `return x` in a generator sets `StopIteration.value`; capture it with\n`yield from`, not by expecting a yielded item.\n",{"id":861,"difficulty":150,"q":862,"a":863},"generator-close-throw","What do a generator's `close()` and `throw()` methods do?","`close()` raises **`GeneratorExit`** inside the paused generator so it can run\ncleanup (e.g. in a `finally`). `throw(exc)` injects an exception at the current\n`yield`, letting the generator handle or propagate it.\n\n```python\ndef worker():\n    try:\n        while True:\n            x = yield\n            print(\"got\", x)\n    finally:\n        print(\"cleanup\")     # runs on close()\n\nw = worker(); next(w)\nw.send(1)        # got 1\nw.close()        # raises GeneratorExit -> prints \"cleanup\"\n```\n\nRule of thumb: put resource cleanup in a `finally` so `close()` (and GC) can release\nit; `throw()` is for signaling errors into a coroutine.\n",{"id":865,"difficulty":162,"q":866,"a":867},"generator-pipeline","How do you build a data-processing pipeline with generators?","Chain generators so each stage **lazily consumes** the previous one — data flows\nitem-by-item with **constant memory**, never materializing intermediate lists. Great\nfor large files and streams.\n\n```python\ndef read(path):\n    with open(path) as f:\n        for line in f:\n            yield line.rstrip()\n\ndef grep(lines, term):\n    for line in lines:\n        if term in line:\n            yield line\n\ndef upper(lines):\n    for line in lines:\n        yield line.upper()\n\nfor line in upper(grep(read(\"log.txt\"), \"ERROR\")):\n    print(line)        # streamed end to end\n```\n\nRule of thumb: compose small generator stages for memory-efficient pipelines; each\nstage pulls one item at a time from the one before it.\n",{"id":869,"difficulty":162,"q":870,"a":871},"generator-is-iterator","Why can you only iterate a generator once?","A generator **is its own iterator** — it has no stored collection to restart from,\nonly a current execution position. Once exhausted (or partially consumed), there's\nno \"rewind\"; you must create a **new** generator to iterate again.\n\n```python\ng = (x for x in range(3))\nlist(g)        # [0, 1, 2]\nlist(g)        # [] — already exhausted\n\nsum_, max_ = sum(g), max(g)   # BUG if g is consumed by the first call\ndata = list(range(3))         # materialize if you need multiple passes\n```\n\nRule of thumb: generators are single-pass; if you need to iterate twice (or use both\n`sum` and `max`), store the values in a list first or rebuild the generator.\n",{"id":873,"difficulty":162,"q":874,"a":875},"lazy-evaluation-benefit","How does generator laziness improve responsiveness, not just memory?","Laziness means you get the **first result immediately** without computing the rest —\ntime-to-first-item is low and you can **stop early**. With an eager list you pay the\nfull cost upfront even if you only need one match.\n\n```python\ndef find_first(it, pred):\n    for x in it:\n        if pred(x):\n            return x        # stops as soon as found\n\n# generator computes only until the match:\nfind_first((expensive(n) for n in range(10**9)), lambda v: v > 100)\n```\n\nRule of thumb: generators let you short-circuit — combine with `next()`, `any`, or a\n`break` to avoid computing values you'll never use.\n",{"id":877,"difficulty":162,"q":878,"a":879},"generator-vs-iterator-class","Why write a generator instead of a class with `__iter__`\u002F`__next__`?","A generator function captures all the iteration state in **local variables and the\npause point** automatically — no manual `self.index` bookkeeping, no explicit\n`StopIteration`. It's far less code for the same iterator behavior.\n\n```python\n# verbose class iterator:\nclass Countdown:\n    def __init__(self, n): self.n = n\n    def __iter__(self): return self\n    def __next__(self):\n        if self.n \u003C= 0: raise StopIteration\n        self.n -= 1; return self.n + 1\n\n# equivalent generator:\ndef countdown(n):\n    while n > 0:\n        yield n\n        n -= 1\n```\n\nRule of thumb: reach for a generator function for almost all custom iteration; use a\nclass only when you need extra methods\u002Fattributes alongside iteration.\n",{"id":881,"difficulty":150,"q":882,"a":883},"generator-state-machine","How does a generator preserve local state between `yield`s?","A generator keeps its own **frame** (locals, instruction pointer) alive across\nsuspensions. When paused at a `yield`, all locals retain their values; the next\n`next()` resumes exactly where it left off — like a saved stack frame.\n\n```python\ndef running_total():\n    total = 0\n    for x in [10, 20, 30]:\n        total += x         # `total` survives across yields\n        yield total\n\nlist(running_total())      # [10, 30, 60]\n```\n\nRule of thumb: each generator instance has independent, persistent local state —\nthat's why two calls to the same generator function don't interfere.\n",{"id":885,"difficulty":150,"q":886,"a":887},"nested-yield-from-tree","How does `yield from` simplify recursive generators?","For recursive structures (trees, nested lists), `yield from` lets a generator\ndelegate to a recursive call cleanly, flattening the structure without manual loops\nat each level.\n\n```python\ndef flatten(items):\n    for item in items:\n        if isinstance(item, list):\n            yield from flatten(item)   # recurse and forward all values\n        else:\n            yield item\n\nlist(flatten([1, [2, [3, 4], 5], 6]))   # [1, 2, 3, 4, 5, 6]\n```\n\nRule of thumb: `yield from recursive_call(...)` is the idiom for recursively walking\nnested data lazily — no accumulator list needed.\n",{"id":889,"difficulty":162,"q":890,"a":891},"generator-exhaustion-pitfall","What's a common bug when passing a generator to multiple functions?","Passing one generator to two consumers means the **second sees nothing** — the first\ndrained it. This bites when you compute several aggregates over the same source.\n\n```python\ngen = (n for n in range(5))\ntotal = sum(gen)     # 10 — consumes gen\ncount = len(list(gen))   # 0! — already exhausted\navg = total \u002F count      # ZeroDivisionError\n\ndata = list(range(5))    # fix: materialize once\ntotal, count = sum(data), len(data)\n```\n\nRule of thumb: if you need multiple passes or several aggregates, convert the\ngenerator to a list first (or use `itertools.tee` for limited re-iteration).\n",{"description":148},"Python interview questions on generators and yield, lazy evaluation, generator expressions vs list comprehensions, yield from, and infinite generators.","python\u002Fiteration\u002Fgenerators","Generators & yield","5GCHriLBs8rm6dLCXpKy04ewEk1AzImuBdCjrnZNc1A",{"id":898,"title":899,"body":900,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":904,"navigation":153,"order":13,"path":905,"questions":906,"questionsCount":217,"related":218,"seo":967,"seoDescription":968,"stem":969,"subtopic":970,"topic":81,"topicSlug":83,"updated":223,"__hash__":971},"qa\u002Fpython\u002Fmodules\u002Fimports.md","Imports",{"type":145,"value":901,"toc":902},[],{"title":148,"searchDepth":29,"depth":29,"links":903},[],{},"\u002Fpython\u002Fmodules\u002Fimports",[907,911,915,919,923,927,931,935,939,943,947,951,955,959,963],{"id":908,"difficulty":253,"q":909,"a":910},"module-vs-package","What is the difference between a module and a package?","A **module** is a single `.py` file — a namespace of functions, classes, and\nvariables you can `import`. A **package** is a **directory of modules**; it\ngroups related modules under one dotted namespace (`mypkg.utils`).\n\nHistorically a package needed an `__init__.py` file to be recognized; that file\nruns when the package is first imported and can expose a curated public API.\nSince Python 3.3, a directory without `__init__.py` can still be a **namespace\npackage**, but a regular package with `__init__.py` is the common, explicit choice.\n\n```python\n# file layout\n# mypkg\u002F\n#   __init__.py      \u003C- makes it a regular package\n#   utils.py         \u003C- a module inside the package\n\nimport mypkg.utils          # import a module from a package\nfrom mypkg import utils     # same module, bound as `utils`\n```\n\nWhy it matters: modules are the **unit of code reuse**, packages are the **unit of\norganization** — both are objects at runtime (`module.__file__`, `package.__path__`).\n",{"id":912,"difficulty":162,"q":913,"a":914},"absolute-vs-relative-imports","What is the difference between absolute and relative imports?","An **absolute import** spells out the full path from a top-level package\n(`from mypkg.utils import helper`). A **relative import** uses leading dots to\nnavigate relative to the **current module's package** — one dot for the current\npackage, two for the parent.\n\n```python\n# inside mypkg\u002Fsub\u002Fthing.py\nfrom mypkg.utils import helper   # absolute — explicit, unambiguous\nfrom ..utils import helper       # relative — '..' = mypkg, then .utils\nfrom . import sibling            # relative — same package\n```\n\nRelative imports only work **inside a package** and only when the module is run as\npart of that package — running the file directly with `python thing.py` breaks them\n(`ImportError: attempted relative import with no known parent package`).\n\nRule of thumb: PEP 8 **prefers absolute imports** for clarity; reach for relative\nimports inside large packages to avoid repeating a long package prefix.\n",{"id":916,"difficulty":253,"q":917,"a":918},"name-main","What does `if __name__ == \"__main__\"` do?","Every module has a `__name__` variable. When a file is **run directly**, Python\nsets its `__name__` to the string `\"__main__\"`. When the same file is **imported**,\n`__name__` is set to the **module's name** instead. The guard therefore runs code\n**only when the file is executed as a script**, not when it's imported.\n\n```python\ndef main():\n    print(\"running as a program\")\n\nif __name__ == \"__main__\":   # True only via `python myfile.py`\n    main()                   # skipped when `import myfile`\n```\n\nThis lets a file double as both an importable library and a runnable program: tests\nand other modules can import its functions without triggering the script logic.\n\nRule of thumb: put **executable entry-point code** under the guard so importing the\nmodule stays free of side effects.\n",{"id":920,"difficulty":162,"q":921,"a":922},"sys-path-resolution","How does Python find the module you import?","On `import x`, Python searches a list of **finders** and, for file-based modules,\nwalks **`sys.path`** — a list of directories — in order, using the **first match**.\n`sys.path` is built from the script's directory (or cwd), the `PYTHONPATH`\nenvironment variable, and the installation's standard library \u002F `site-packages`.\n\n```python\nimport sys\nprint(sys.path)        # ['', '\u002Fusr\u002Flib\u002Fpython3.12', '...\u002Fsite-packages', ...]\n# '' (or the script dir) is searched first — local files can SHADOW stdlib!\n\n# a file named random.py in your folder will hide the real `random` module\n```\n\nBuilt-in modules and frozen modules are found before `sys.path` is consulted, which\nis why you can't shadow `sys` itself.\n\nWhy it matters: a local file named like a stdlib module (`queue.py`, `email.py`)\nsilently **shadows** the real one — a classic, confusing import bug.\n",{"id":924,"difficulty":150,"q":925,"a":926},"import-caching-circular","What is `sys.modules` and how does it relate to circular imports?","`sys.modules` is a **cache** mapping module names to already-imported module\nobjects. The **first** import of a module executes its code top to bottom and stores\nthe result there; every later `import` of the same name just returns the cached\nobject — so module code runs **once** per interpreter session.\n\nA **circular import** is when module A imports B while B imports A. Because the\nimporting module is added to `sys.modules` **before** its body finishes running, the\nsecond import gets a **partially-initialized** module — names defined later in the\nfile aren't there yet.\n\n```python\n# a.py\nimport b                 # starts importing b...\ndef helper(): ...\n\n# b.py\nimport a                 # a is in sys.modules but only HALF-defined\nprint(a.helper)          # AttributeError — helper isn't bound yet\n```\n\nFixes: **move the import inside the function** that needs it (deferred until call\ntime), restructure to remove the cycle, or import the module object rather than\nnames from it. Rule of thumb: circular imports usually signal that two modules\nshould share a third.\n",{"id":928,"difficulty":162,"q":929,"a":930},"import-star-and-all","What does `from module import *` do and how does `__all__` control it?","`import *` binds all of a module's **public** names (those not starting with `_`)\ninto the current namespace. If the module defines **`__all__`** (a list of names),\nonly those are imported — giving the author control over the public API.\n\n```python\n# mymod.py\n__all__ = [\"public_fn\"]      # restricts what `import *` exposes\ndef public_fn(): ...\ndef _private(): ...\nhelper = 1\n\n# elsewhere:\nfrom mymod import *          # only public_fn is imported\n```\n\nRule of thumb: avoid `import *` in real code (it pollutes the namespace and hides\norigins); define `__all__` to document a package's public surface.\n",{"id":932,"difficulty":253,"q":933,"a":934},"import-vs-from-import","What's the practical difference between `import x` and `from x import y`?","`import x` binds the **module object** (`x.y` to access members). `from x import y`\nbinds **`y` directly**. The latter is a one-time snapshot — if `x` later rebinds `y`,\nyour imported `y` won't see the change.\n\n```python\nimport math\nmath.pi              # access via the module\n\nfrom math import pi\npi                   # bound directly\n\n# snapshot pitfall:\nfrom mod import counter   # captures the value now\nmod.counter = 99          # your `counter` is unchanged\n```\n\nRule of thumb: use `import x` to keep names traceable and see later changes; use\n`from x import y` for frequently-used names, accepting the snapshot semantics.\n",{"id":936,"difficulty":162,"q":937,"a":938},"lazy-import","Why and how would you import a module lazily inside a function?","Importing **inside a function** defers the cost until the function is called — useful\nfor **heavy\u002Foptional dependencies**, faster startup, and breaking **circular\nimports**. The module is still cached after the first call.\n\n```python\ndef export_pdf(data):\n    import reportlab      # only loaded if this feature is used\n    ...\n\ndef needs_sibling():\n    from . import other   # avoids a top-level circular import\n    return other.func()\n```\n\nRule of thumb: keep imports at module top by default; move them into functions only\nfor optional heavy deps, startup speed, or to break import cycles.\n",{"id":940,"difficulty":150,"q":941,"a":942},"reload-module","How do you reload a module, and why is it rarely the right tool?","`importlib.reload(mod)` re-executes a module's code and updates the **existing**\nmodule object in place. But objects already imported elsewhere or instances of old\nclasses **keep referencing the old code**, leading to confusing inconsistencies.\n\n```python\nimport importlib, mymod\nimportlib.reload(mymod)     # re-runs mymod's top-level code\n\n# gotcha: existing `from mymod import f` bindings still point to the OLD f,\n# and old instances aren't migrated to the reloaded class\n```\n\nRule of thumb: `reload` is for interactive\u002FREPL tinkering only; in real apps restart\nthe process rather than rely on reload's partial, error-prone updates.\n",{"id":944,"difficulty":162,"q":945,"a":946},"package-init-role","What is the role of `__init__.py` in a package?","`__init__.py` runs when the package is **first imported**. It marks a regular package\nand is the place to **expose a curated API** (re-export submodule names), set package\n`__all__`, or run package-level setup. It can be empty.\n\n```python\n# mypkg\u002F__init__.py\nfrom .core import main_function     # expose at package level\nfrom .utils import helper\n__all__ = [\"main_function\", \"helper\"]\n\n# now users can do:\nfrom mypkg import main_function     # instead of mypkg.core.main_function\n```\n\nRule of thumb: keep `__init__.py` light; use it to flatten\u002Fcurate the public API, not\nfor heavy work that slows every import of the package.\n",{"id":948,"difficulty":162,"q":949,"a":950},"conditional-import-fallback","How do you handle an optional dependency with try\u002Fexcept imports?","Wrap the import in **`try\u002Fexcept ImportError`** and fall back to an alternative or a\nflag. This is the standard pattern for optional features and library compatibility\nshims.\n\n```python\ntry:\n    import ujson as json      # fast optional lib\nexcept ImportError:\n    import json               # stdlib fallback\n\ntry:\n    import numpy\n    HAS_NUMPY = True\nexcept ImportError:\n    HAS_NUMPY = False\n```\n\nRule of thumb: use `try\u002Fexcept ImportError` to degrade gracefully when an optional\npackage is missing — provide a fallback or a capability flag.\n",{"id":952,"difficulty":162,"q":953,"a":954},"running-package-m","What does `python -m package` do and why use it?","`python -m mypkg` runs the package's **`__main__.py`** as a script while keeping\n**proper package context**, so relative imports work. `-m mod` runs a module by its\nimport name rather than file path — the recommended way to run package entry points.\n\n```bash\npython -m http.server        # run a stdlib module as a tool\npython -m mypkg              # runs mypkg\u002F__main__.py with package context\npython myfile.py             # runs as top-level script — relative imports break\n```\n\nRule of thumb: launch package code with `python -m pkg` (not `python pkg\u002Ffile.py`) so\n`__package__` is set and relative imports resolve correctly.\n",{"id":956,"difficulty":150,"q":957,"a":958},"namespace-packages","What is a namespace package and when is it useful?","A **namespace package** (PEP 420) is a package **without `__init__.py`** whose\ncontents can be **spread across multiple directories** on `sys.path`. Python merges\nthem into one logical package — handy for splitting a large namespace across separate\ndistributions.\n\n```text\n# site-packages\u002Facme\u002Ftools\u002F...      (from package acme-tools)\n# site-packages\u002Facme\u002Fdata\u002F...       (from package acme-data)\n# both contribute to the single `acme` namespace, no __init__.py needed\n```\n\n```python\nimport acme.tools\nimport acme.data      # both resolve under one `acme` namespace\n```\n\nRule of thumb: use namespace packages to let independently-shipped subpackages share\na common top-level name; use a regular `__init__.py` package otherwise.\n",{"id":960,"difficulty":253,"q":961,"a":962},"module-attributes","What useful attributes does a module object have?","Modules expose dunder attributes: **`__name__`** (import name or `\"__main__\"`),\n**`__file__`** (source path), **`__doc__`** (module docstring), **`__dict__`** (its\nnamespace), and for packages **`__path__`** (search locations).\n\n```python\nimport os\nos.__name__        # 'os'\nos.__file__        # '\u002Fusr\u002Flib\u002Fpython3.12\u002Fos.py'\nos.__doc__         # the module docstring\nimport json\njson.__path__      # package search paths (packages only)\n```\n\nRule of thumb: `__name__`, `__file__`, and `__doc__` are the everyday module\nattributes — useful for introspection, logging, and locating resources.\n",{"id":964,"difficulty":162,"q":965,"a":966},"pycache-bytecode","What is the `__pycache__` directory and the `.pyc` files in it?","Python compiles each module to **bytecode** and caches it as a `.pyc` file in\n`__pycache__` to **skip recompilation** on later imports. It checks the source's\ntimestamp\u002Fhash and recompiles only when the `.py` changes. It's purely a speed\noptimization.\n\n```text\nmymod.py\n__pycache__\u002F\n    mymod.cpython-312.pyc   # cached bytecode, tagged by interpreter version\n```\n\nRule of thumb: `__pycache__`\u002F`.pyc` are auto-managed caches — safe to delete, safe to\ngitignore; they speed up imports, not execution of the code itself.\n",{"description":148},"Python interview questions on the import system — modules vs packages, absolute vs relative imports, __main__, sys.path module resolution, import caching, and circular imports.","python\u002Fmodules\u002Fimports","The Import System","i5NdV_GEygUJvO7ARN8DSCn4kVsd4feEOOv38gSMwo4",{"id":973,"title":974,"body":975,"description":148,"difficulty":150,"extension":151,"framework":10,"frameworkSlug":8,"meta":979,"navigation":153,"order":13,"path":980,"questions":981,"questionsCount":217,"related":218,"seo":1042,"seoDescription":1043,"stem":1044,"subtopic":1045,"topic":54,"topicSlug":56,"updated":223,"__hash__":1046},"qa\u002Fpython\u002Foop\u002Finheritance.md","Inheritance",{"type":145,"value":976,"toc":977},[],{"title":148,"searchDepth":29,"depth":29,"links":978},[],{},"\u002Fpython\u002Foop\u002Finheritance",[982,986,990,994,998,1002,1006,1010,1014,1018,1022,1026,1030,1034,1038],{"id":983,"difficulty":253,"q":984,"a":985},"single-vs-multiple","What is the difference between single and multiple inheritance?","**Single inheritance** means a class derives from exactly one parent; **multiple\ninheritance** means it lists more than one base class. Python supports both —\nmultiple inheritance is what lets you compose behaviour from several sources at\nonce.\n\n```python\nclass Animal:\n    def eat(self): print(\"eating\")\n\nclass Dog(Animal):          # single inheritance\n    def bark(self): print(\"woof\")\n\nclass Swimmer: ...\nclass Flyer: ...\nclass Duck(Animal, Swimmer, Flyer):  # multiple inheritance\n    pass\n```\n\nMultiple inheritance is powerful but can create ambiguity about *which* parent's\nmethod wins — that ambiguity is resolved by the **MRO**. Rule of thumb: prefer\nsingle inheritance plus small **mixins** over deep, wide hierarchies.\n",{"id":987,"difficulty":150,"q":988,"a":989},"what-is-mro","What is the MRO and how is it computed?","The **MRO (Method Resolution Order)** is the linear, ordered list of classes\nPython searches when looking up an attribute or method on an instance. CPython\nbuilds it with the **C3 linearization** algorithm, which guarantees a consistent\norder that respects each class's own order of bases and never places a parent\nbefore its child.\n\n```python\nclass A: ...\nclass B(A): ...\nclass C(A): ...\nclass D(B, C): ...\n\nD.__mro__            # (D, B, C, A, object)\nD.mro()             # same, as a list\n```\n\nC3 produces a single deterministic order (or raises `TypeError` if no consistent\norder exists). Attribute lookup walks this list left to right and stops at the\nfirst match. Rule of thumb: read `Cls.__mro__` whenever multiple inheritance\nsurprises you — it tells you exactly who wins.\n",{"id":991,"difficulty":150,"q":992,"a":993},"how-super-works","How does super() actually work?","`super()` does **not** simply call \"the parent class\" — it calls the **next class\nin the instance's MRO**, starting after the current class. That cooperative\nbehaviour is what makes multiple inheritance work correctly: each class delegates\nto whatever comes next, regardless of the static hierarchy.\n\n```python\nclass A:\n    def greet(self): print(\"A\")\nclass B(A):\n    def greet(self): print(\"B\"); super().greet()\nclass C(A):\n    def greet(self): print(\"C\"); super().greet()\nclass D(B, C):\n    def greet(self): print(\"D\"); super().greet()\n\nD().greet()   # D, B, C, A  — follows D.__mro__, not B's parent\n```\n\nNote `super().greet()` inside `B` calls `C`, not `A`, because the MRO of a `D`\ninstance puts `C` after `B`. Rule of thumb: in a cooperative hierarchy every\noverride should call `super()` so the whole chain runs exactly once.\n",{"id":995,"difficulty":150,"q":996,"a":997},"diamond-problem","What is the diamond problem and how does Python solve it?","The **diamond problem** arises when two classes (`B`, `C`) inherit from a common\nbase (`A`), and a fourth class (`D`) inherits from both. The question is: when\n`D` calls an inherited method, is `A`'s code run **once or twice**? Naive\nlanguages run it twice; Python's **C3 MRO** guarantees `A` appears **exactly\nonce**, so cooperative `super()` calls run it a single time.\n\n```python\nclass A:\n    def __init__(self): print(\"A\"); \nclass B(A):\n    def __init__(self): print(\"B\"); super().__init__()\nclass C(A):\n    def __init__(self): print(\"C\"); super().__init__()\nclass D(B, C):\n    def __init__(self): print(\"D\"); super().__init__()\n\nD()   # D, B, C, A  — A's __init__ runs ONCE\n```\n\nThe MRO `(D, B, C, A, object)` linearizes the diamond into a clean chain. Rule of\nthumb: the diamond is only safe when every class in it uses `super()` consistently\n— mixing `super()` with hard-coded `Base.__init__(self)` calls breaks the\nguarantee.\n",{"id":999,"difficulty":162,"q":1000,"a":1001},"mixins-and-abc","What are mixins and abstract base classes?","A **mixin** is a small class that provides a focused slice of behaviour meant to be\ncombined into other classes via multiple inheritance — it isn't useful on its own\nand usually has no `__init__`. An **abstract base class (ABC)**, from the `abc`\nmodule, defines an interface with `@abstractmethod`s and **cannot be instantiated**\nuntil every abstract method is overridden.\n\n```python\nfrom abc import ABC, abstractmethod\n\nclass JsonMixin:                 # mixin: adds one capability\n    def to_json(self): import json; return json.dumps(self.__dict__)\n\nclass Shape(ABC):                # abstract base: defines a contract\n    @abstractmethod\n    def area(self): ...\n\nclass Circle(Shape, JsonMixin):\n    def __init__(self, r): self.r = r\n    def area(self): return 3.14159 * self.r ** 2\n\nShape()    # TypeError: can't instantiate abstract class\n```\n\nUse mixins to share reusable behaviour and ABCs to enforce that subclasses\nimplement a required interface (`isinstance` checks also work against ABCs). Rule\nof thumb: mixins say \"you *can* do this\", ABCs say \"you *must* do this\".\n",{"id":1003,"difficulty":150,"q":1004,"a":1005},"super-init-args","How should `__init__` cooperate with `super()` across a multiple-inheritance chain?","In cooperative hierarchies, each `__init__` should **accept `**kwargs`** and pass\nalong what it doesn't consume via `super().__init__(**kwargs)`. This lets every class\nin the MRO pull out its own arguments without breaking the chain.\n\n```python\nclass Base:\n    def __init__(self, **kwargs):\n        super().__init__(**kwargs)      # ends at object()\n\nclass Named(Base):\n    def __init__(self, name, **kwargs):\n        self.name = name\n        super().__init__(**kwargs)      # forward the rest\n\nclass Aged(Base):\n    def __init__(self, age, **kwargs):\n        self.age = age\n        super().__init__(**kwargs)\n\nclass Person(Named, Aged):\n    pass\np = Person(name=\"Ada\", age=36)          # both inits run\n```\n\nRule of thumb: cooperative `__init__`s take\u002Fforward `**kwargs` and always call\n`super().__init__` so each class consumes its own args and the chain completes.\n",{"id":1007,"difficulty":162,"q":1008,"a":1009},"isinstance-vs-type","Why prefer `isinstance` over `type(x) == SomeClass`?","**`isinstance`** respects inheritance — it's `True` for subclasses too — while\n`type(x) == C` demands an **exact** match and rejects subclasses. `isinstance` also\naccepts a tuple of types.\n\n```python\nclass Animal: pass\nclass Dog(Animal): pass\nd = Dog()\n\nisinstance(d, Animal)        # True — subclass counts\ntype(d) == Animal            # False — exact type only\nisinstance(d, (Dog, Animal)) # True — tuple of options\n```\n\nRule of thumb: use `isinstance` for \"is this usable as an X?\" (the normal case);\nreserve exact `type(x) is C` for the rare time you must reject subclasses.\n",{"id":1011,"difficulty":253,"q":1012,"a":1013},"method-override-and-extend","How do you override a method while still using the parent's version?","Define the method in the subclass to **override** it; call **`super().method()`**\ninside to **extend** rather than fully replace the parent behaviour. This is the\nstandard \"do the base thing, then add to it\" pattern.\n\n```python\nclass Logger:\n    def log(self, msg):\n        print(f\"[LOG] {msg}\")\n\nclass TimestampLogger(Logger):\n    def log(self, msg):\n        import datetime\n        stamp = datetime.datetime.now().isoformat()\n        super().log(f\"{stamp} {msg}\")   # reuse parent, add timestamp\n\nTimestampLogger().log(\"hi\")\n```\n\nRule of thumb: call `super().method()` when you want to augment the parent's logic;\nomit it only when you intend to completely replace it.\n",{"id":1015,"difficulty":162,"q":1016,"a":1017},"composition-vs-inheritance","When should you favor composition over inheritance?","Use **inheritance** for a true **\"is-a\"** relationship and shared interface; use\n**composition** (holding another object as an attribute) for **\"has-a\"** and to\navoid fragile deep hierarchies. Composition is more flexible and easier to test.\n\n```python\n# Inheritance — Car IS A Vehicle:\nclass Vehicle: ...\nclass Car(Vehicle): ...\n\n# Composition — Car HAS AN Engine:\nclass Engine:\n    def start(self): return \"vroom\"\nclass Car:\n    def __init__(self):\n        self.engine = Engine()     # delegate to a part\n    def start(self):\n        return self.engine.start()\n```\n\nRule of thumb: reach for composition by default (\"has-a\", swappable parts); use\ninheritance when subclasses genuinely substitute for the base (\"is-a\").\n",{"id":1019,"difficulty":150,"q":1020,"a":1021},"subclass-hooks","What does `__init_subclass__` let a base class do?","`__init_subclass__` is a hook that runs **whenever a subclass is defined** (not when\ninstantiated). The base class can use it to register subclasses, validate them, or\ninject defaults — a lighter alternative to a metaclass.\n\n```python\nclass Plugin:\n    registry = []\n    def __init_subclass__(cls, **kwargs):\n        super().__init_subclass__(**kwargs)\n        Plugin.registry.append(cls)      # auto-register every subclass\n\nclass Audio(Plugin): pass\nclass Video(Plugin): pass\nPlugin.registry        # [Audio, Video]\n```\n\nRule of thumb: use `__init_subclass__` for subclass registration\u002Fvalidation; it\ncovers most cases people once reached for metaclasses to do.\n",{"id":1023,"difficulty":162,"q":1024,"a":1025},"name-resolution-attributes","How does attribute lookup use the MRO for class attributes?","Reading `instance.attr` checks the **instance `__dict__` first**, then walks the\n**class's MRO** left to right, returning the first match. So a subclass attribute\nshadows a base one, and the MRO decides ties in multiple inheritance.\n\n```python\nclass A:\n    x = \"A\"\nclass B(A):\n    x = \"B\"\nclass C(A):\n    x = \"C\"\nclass D(B, C):\n    pass\n\nD.x          # 'B' — first in MRO (D, B, C, A)\nd = D(); d.x = \"instance\"\nd.x          # 'instance' — instance dict wins over class\n```\n\nRule of thumb: lookup order is instance → MRO classes (left to right); check\n`Cls.__mro__` to predict which class attribute a name resolves to.\n",{"id":1027,"difficulty":150,"q":1028,"a":1029},"super-with-args","What is the difference between `super()` and `super(Class, self)`?","In Python 3, the **zero-argument `super()`** inside a method is sugar for\n`super(CurrentClass, self)` — the compiler injects the class and instance. The\nexplicit two-arg form is needed **outside** a normal method body (e.g. at module\nlevel, in some metaclass code, or to start the search from a different class).\n\n```python\nclass A:\n    def f(self): return \"A\"\nclass B(A):\n    def f(self):\n        return super().f()             # = super(B, self).f()\n\n# explicit form, e.g. to skip B in the MRO:\nb = B()\nsuper(B, b).f()                         # 'A' — starts after B\n```\n\nRule of thumb: use bare `super()` inside methods; the explicit `super(Class, obj)`\nform only when there's no enclosing method context or you must control the start.\n",{"id":1031,"difficulty":162,"q":1032,"a":1033},"overriding-vs-overloading","Does Python support method overloading like Java?","No — Python has **no signature-based overloading**. A later `def` with the same name\nsimply **replaces** the earlier one. You achieve \"overloading\" with default args,\n`*args`\u002F`**kwargs`, or `functools.singledispatch` for type-based dispatch.\n\n```python\nclass C:\n    def f(self, x): return \"one\"\n    def f(self, x, y): return \"two\"   # this REPLACES the first f\n\nC().f(1)        # TypeError — only the 2-arg f exists\n\nfrom functools import singledispatchmethod\nclass D:\n    @singledispatchmethod\n    def f(self, x): return \"default\"\n    @f.register\n    def _(self, x: int): return \"int\"\n```\n\nRule of thumb: there's one method per name — use default\u002Fvariadic args or\n`singledispatch` instead of expecting Java-style overloads.\n",{"id":1035,"difficulty":162,"q":1036,"a":1037},"abstract-cannot-instantiate","Can you partially implement an abstract base class in an intermediate subclass?","Yes. An intermediate subclass can implement **some** abstract methods and stay\nabstract; only a class that implements **all** of them becomes instantiable. This\nenables layered hierarchies that share partial implementations.\n\n```python\nfrom abc import ABC, abstractmethod\n\nclass Repo(ABC):\n    @abstractmethod\n    def get(self, id): ...\n    @abstractmethod\n    def save(self, obj): ...\n\nclass ReadOnlyRepo(Repo):\n    def get(self, id): return ...        # implements one\n    # still abstract: save() missing\n\nReadOnlyRepo()    # TypeError — save still abstract\n```\n\nRule of thumb: abstractness propagates until every abstract method is implemented;\nintermediate classes can fill in part of the contract.\n",{"id":1039,"difficulty":150,"q":1040,"a":1041},"super-in-classmethod","How does `super()` behave inside a classmethod?","Inside a `@classmethod`, `super()` resolves relative to the **`cls`** passed in, so\nit correctly follows the MRO of the **actual subclass** — making cooperative\nalternative constructors work across inheritance.\n\n```python\nclass Base:\n    @classmethod\n    def create(cls):\n        print(f\"Base.create for {cls.__name__}\")\n        return cls()\n\nclass Child(Base):\n    @classmethod\n    def create(cls):\n        print(\"Child.create\")\n        return super().create()     # cls is still Child\n\nChild.create()    # prints Child.create, then Base.create for Child\n```\n\nRule of thumb: `super()` in a classmethod uses the real `cls`, so factory methods\nbuild instances of the correct subclass while still chaining to the base.\n",{"description":148},"Python interview questions on single vs multiple inheritance, the MRO and C3 linearization, super(), the diamond problem, mixins, and abstract base classes.","python\u002Foop\u002Finheritance","Inheritance & the MRO","jg5bwlyfVJdBgjmpagBH3qKhxv6Co2AqxNdrczFJTPY",{"id":1048,"title":1049,"body":1050,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":1054,"navigation":153,"order":13,"path":1055,"questions":1056,"questionsCount":217,"related":218,"seo":1117,"seoDescription":1118,"stem":1119,"subtopic":1120,"topic":117,"topicSlug":119,"updated":223,"__hash__":1121},"qa\u002Fpython\u002Fstdlib\u002Fregex.md","Regex",{"type":145,"value":1051,"toc":1052},[],{"title":148,"searchDepth":29,"depth":29,"links":1053},[],{},"\u002Fpython\u002Fstdlib\u002Fregex",[1057,1061,1065,1069,1073,1077,1081,1085,1089,1093,1097,1101,1105,1109,1113],{"id":1058,"difficulty":253,"q":1059,"a":1060},"match-search-fullmatch","What is the difference between re.match, re.search, and re.fullmatch?","They differ in **where** the pattern must match. `re.match` anchors at the\n**start** of the string (but not the end). `re.search` scans for the pattern\n**anywhere** in the string. `re.fullmatch` requires the pattern to match the\n**entire** string. All return a `Match` object on success or `None` on failure.\n\n```python\nimport re\nre.match(\"ab\", \"abcd\")      # match — starts with 'ab'\nre.match(\"cd\", \"abcd\")      # None  — not at the start\nre.search(\"cd\", \"abcd\")     # match — found anywhere\nre.fullmatch(\"ab\", \"abcd\")  # None  — must match the whole string\nre.fullmatch(\"abcd\", \"abcd\")# match\n```\n\nA common bug is using `match` expecting whole-string validation — it only\nanchors the start. Rule of thumb: use `search` to find, `fullmatch` to\nvalidate, and `match` only when you specifically mean \"begins with\".\n",{"id":1062,"difficulty":162,"q":1063,"a":1064},"groups-named-groups","How do capture groups and named groups work?","Parentheses `( )` create a **capture group** you retrieve by **number**\n(1-based; group 0 is the whole match). `(?P\u003Cname>...)` creates a **named\ngroup** you retrieve by name — far more readable. `(?:...)` groups **without\ncapturing** when you only need it for grouping\u002Falternation.\n\n```python\nimport re\nm = re.search(r\"(\\d{4})-(\\d{2})\", \"2026-06\")\nm.group(0)   # '2026-06'  — whole match\nm.group(1)   # '2026'     — first group\nm.groups()   # ('2026', '06')\n\nm = re.search(r\"(?P\u003Cyear>\\d{4})-(?P\u003Cmonth>\\d{2})\", \"2026-06\")\nm.group(\"year\")   # '2026'\nm.groupdict()     # {'year': '2026', 'month': '06'}\n```\n\nNamed groups make patterns self-documenting and resilient to reordering.\nRule of thumb: use `(?P\u003Cname>...)` for anything you'll extract, and\n`(?:...)` when grouping is structural only.\n",{"id":1066,"difficulty":162,"q":1067,"a":1068},"re-compile-why","What does re.compile do, and why would you use it?","`re.compile(pattern)` builds a **reusable pattern object** once, then you call\nmethods (`.search`, `.match`, `.findall`, `.sub`) on it. The module-level\nfunctions actually compile internally and **cache** recent patterns, so the\nmain win is **clarity and reuse** — plus a small speedup when a pattern is\nused **many times in a loop**.\n\n```python\nimport re\nDATE = re.compile(r\"(?P\u003Cyear>\\d{4})-(?P\u003Cmonth>\\d{2})\")  # compile once\n\nfor line in lines:\n    m = DATE.search(line)   # reuse the compiled object\n    if m:\n        print(m.group(\"year\"))\n```\n\nIt also lets you attach **flags** (e.g. `re.IGNORECASE`, `re.VERBOSE`) in one\nplace. Rule of thumb: compile patterns used repeatedly or shared across a\nmodule; for one-off use the module functions are fine.\n",{"id":1070,"difficulty":150,"q":1071,"a":1072},"greedy-vs-lazy","What is the difference between greedy and non-greedy matching?","By default quantifiers (`*`, `+`, `?`, `{m,n}`) are **greedy** — they match as\n**much** as possible, then backtrack. Adding a trailing `?` makes them\n**non-greedy** (lazy) — they match as **little** as possible. This matters\nhugely when a delimiter can appear multiple times.\n\n```python\nimport re\ntext = \"\u003Ca>\u003Cb>\"\nre.search(r\"\u003C.*>\", text).group()    # '\u003Ca>\u003Cb>'  — greedy, grabs everything\nre.search(r\"\u003C.*?>\", text).group()   # '\u003Ca>'     — lazy, stops at first '>'\n```\n\nGreedy patterns over-matching is a classic \"regex ate too much\" bug. Rule of\nthumb: when matching content **between delimiters**, reach for the lazy `*?`\n\u002F `+?` (or a negated character class like `[^>]*`).\n",{"id":1074,"difficulty":162,"q":1075,"a":1076},"re-sub-raw-strings","How does re.sub work, and why use raw strings for patterns?","`re.sub(pattern, repl, string)` returns a **new** string with all matches\nreplaced. The replacement can reference captured groups with `\\1` or\n`\\g\u003Cname>`, or be a **function** that receives each `Match` for dynamic\nreplacement. You should write patterns as **raw strings** (`r\"...\"`) so that\nbackslash escapes like `\\d` and `\\b` reach the regex engine instead of being\ninterpreted by Python first.\n\n```python\nimport re\nre.sub(r\"\\s+\", \" \", \"a   b\\tc\")          # 'a b c'  — collapse whitespace\nre.sub(r\"(\\d{4})-(\\d{2})\", r\"\\2\u002F\\1\", \"2026-06\")  # '06\u002F2026' — reorder groups\nre.sub(r\"\\d+\", lambda m: f\"[{m.group()}]\", \"x9\")  # 'x[9]' — function repl\n\n\"\\d\"     # in a normal string this is an invalid escape (warns)\nr\"\\d\"    # raw string — passes \\d straight to the engine\n```\n\nWithout `r\"\"`, `\"\\b\"` becomes a backspace character, not a word boundary —\na subtle, hard-to-spot bug. Rule of thumb: **always** prefix regex patterns\nwith `r`.\n",{"id":1078,"difficulty":162,"q":1079,"a":1080},"findall-vs-finditer","What is the difference between `findall` and `finditer`?","**`findall`** returns a **list** of matches (strings, or tuples if there are multiple\ngroups). **`finditer`** returns a **lazy iterator of `Match` objects**, giving you\npositions and group access — better for large inputs or when you need match details.\n\n```python\nimport re\nre.findall(r\"\\d+\", \"a1b22c333\")          # ['1', '22', '333']\nre.findall(r\"(\\w)(\\d)\", \"a1b2\")          # [('a','1'), ('b','2')] — tuples!\n\nfor m in re.finditer(r\"\\d+\", \"a1b22\"):\n    print(m.group(), m.start(), m.end())  # value + position\n```\n\nRule of thumb: `findall` for a quick list of values; `finditer` when you need spans,\ngroups per match, or memory-efficient iteration. Watch findall's tuple behavior with\nmultiple groups.\n",{"id":1082,"difficulty":162,"q":1083,"a":1084},"common-flags","What do the common regex flags do (IGNORECASE, MULTILINE, DOTALL, VERBOSE)?","**`re.IGNORECASE`** case-insensitive; **`re.MULTILINE`** makes `^`\u002F`$` match at each\nline; **`re.DOTALL`** lets `.` match newlines; **`re.VERBOSE`** allows whitespace and\ncomments in the pattern. Combine with `|`.\n\n```python\nimport re\nre.findall(r\"^\\w+\", \"foo\\nbar\", re.MULTILINE)   # ['foo', 'bar']\nre.search(r\"a.b\", \"a\\nb\", re.DOTALL)            # matches across newline\nre.findall(r\"cat\", \"Cat CAT\", re.IGNORECASE)    # ['Cat', 'CAT']\n\npat = re.compile(r\"\"\"\n    \\d{4}   # year\n    -\\d{2}  # month\n\"\"\", re.VERBOSE)\n```\n\nRule of thumb: `MULTILINE` for line-based `^`\u002F`$`, `DOTALL` for `.`-spans-newlines,\n`VERBOSE` for readable complex patterns; combine via `re.A | re.B`.\n",{"id":1086,"difficulty":162,"q":1087,"a":1088},"anchors-boundaries","What do `^`, `$`, `\\b`, and `\\B` anchor?","`^` matches **start** of string (or line in MULTILINE), `$` matches **end**. `\\b` is\na **word boundary** (between word\u002Fnon-word chars); `\\B` is a non-boundary. Anchors\nmatch positions, not characters.\n\n```python\nimport re\nre.search(r\"\\bcat\\b\", \"the cat sat\")     # matches 'cat' as a whole word\nre.search(r\"\\bcat\\b\", \"category\")        # None — 'cat' not a full word\nre.findall(r\"^\\d\", \"5x\", )               # ['5'] — at start\nre.sub(r\"\\s+$\", \"\", \"trim   \")           # 'trim' — trailing whitespace\n```\n\nRule of thumb: use `\\b` to match whole words (avoiding partial matches), and `^`\u002F`$`\nto anchor to string\u002Fline edges.\n",{"id":1090,"difficulty":150,"q":1091,"a":1092},"backreferences","How do backreferences work in a pattern?","A **backreference** (`\\1`, or `(?P=name)`) matches the **same text** a prior group\ncaptured — useful for finding repeats or matched pairs. They make the regex\ncontext-sensitive within a single match.\n\n```python\nimport re\nre.search(r\"(\\w+) \\1\", \"the the end\")        # matches 'the the' — repeated word\nre.search(r\"\u003C(\\w+)>.*\u003C\u002F\\1>\", \"\u003Cb>hi\u003C\u002Fb>\")    # matched open\u002Fclose tag\nre.search(r\"(?P\u003Cq>['\\\"]).*?(?P=q)\", \"'hi'\")  # same quote char on both ends\n```\n\nRule of thumb: backreferences match previously-captured text (`\\1`\u002F`(?P=name)`) —\nideal for duplicate detection and matching paired delimiters.\n",{"id":1094,"difficulty":162,"q":1095,"a":1096},"split-with-regex","How does `re.split` differ from `str.split`?","**`re.split(pattern, s)`** splits on a **regex**, not a fixed substring — so you can\nsplit on variable delimiters. If the pattern has **capturing groups**, the delimiters\nare **included** in the result.\n\n```python\nimport re\nre.split(r\"\\s*,\\s*\", \"a, b ,c\")        # ['a', 'b', 'c'] — flexible spacing\nre.split(r\"[;,]\", \"a,b;c\")             # ['a', 'b', 'c'] — multiple delimiters\nre.split(r\"(\\d)\", \"a1b2\")              # ['a', '1', 'b', '2', ''] — keeps captures\n```\n\nRule of thumb: use `re.split` for variable\u002Fmulti-character delimiters; capturing\ngroups in the pattern keep the separators in the output.\n",{"id":1098,"difficulty":150,"q":1099,"a":1100},"catastrophic-backtracking","What is catastrophic backtracking and how do you avoid it?","Nested or overlapping quantifiers (e.g. `(a+)+`) can make the engine try an\n**exponential** number of paths on non-matching input — freezing your program (a ReDoS\nrisk). Avoid ambiguous nesting and prefer specific character classes.\n\n```python\nimport re\n# DANGEROUS: (a+)+ on \"aaaaaaaaaaaaaaaaX\" backtracks catastrophically\n# re.match(r\"(a+)+$\", \"a\" * 30 + \"X\")   # may hang\n\n# safer: unambiguous, no nested quantifier\nre.match(r\"a+$\", \"a\" * 30 + \"X\")        # fails fast\n```\n\nRule of thumb: avoid nested quantifiers over overlapping patterns; use atomic groups\u002F\npossessive quantifiers (3.11+ `(?>...)`, `a++`) or precise classes to prevent ReDoS.\n",{"id":1102,"difficulty":150,"q":1103,"a":1104},"lookahead-lookbehind","What are lookahead and lookbehind assertions?","**Lookarounds** match a position based on what follows\u002Fprecedes **without consuming**\ncharacters: `(?=...)` positive lookahead, `(?!...)` negative lookahead, `(?\u003C=...)`\npositive lookbehind, `(?\u003C!...)` negative lookbehind.\n\n```python\nimport re\nre.findall(r\"\\d+(?= dollars)\", \"5 dollars 10 euros\")   # ['5'] — followed by 'dollars'\nre.findall(r\"(?\u003C=\\$)\\d+\", \"$5 and $10\")                # ['5', '10'] — preceded by $\nre.sub(r\"(?\u003C!^)(?=(\\d{3})+$)\", \",\", \"1234567\")          # '1,234,567' — thousands\n```\n\nRule of thumb: use lookarounds to assert context (what's around a match) without\nincluding it in the result; lookbehind must be fixed-width in Python's `re`.\n",{"id":1106,"difficulty":253,"q":1107,"a":1108},"match-object-methods","What information does a Match object provide?","A `Match` exposes `.group()`\u002F`.groups()`\u002F`.groupdict()` for captured text, and\n`.start()`\u002F`.end()`\u002F`.span()` for positions. `.group(0)` is the whole match. These\nlet you locate and extract in one pass.\n\n```python\nimport re\nm = re.search(r\"(\\w+)@(\\w+)\", \"to bob@acme now\")\nm.group()        # 'bob@acme'\nm.groups()       # ('bob', 'acme')\nm.start(), m.end()   # (3, 11)\nm.span(1)        # (3, 6) — span of group 1\n```\n\nRule of thumb: a Match carries both the captured text (groups) and where it occurred\n(span\u002Fstart\u002Fend) — use it instead of re-searching for positions.\n",{"id":1110,"difficulty":162,"q":1111,"a":1112},"sub-count-and-subn","How do you limit replacements or count them with `re.sub`?","`re.sub(pat, repl, s, count=N)` replaces only the first **N** matches.\n**`re.subn`** returns a tuple `(new_string, number_of_substitutions)` so you know how\nmany replacements happened.\n\n```python\nimport re\nre.sub(r\"o\", \"0\", \"foo boo\", count=1)    # 'f0o boo' — only first\nre.subn(r\"o\", \"0\", \"foo boo\")            # ('f00 b00', 4)\n\nnew, n = re.subn(r\"\\bthe\\b\", \"a\", \"the cat the dog\")\nn                                         # 2\n```\n\nRule of thumb: use `count=` to cap replacements and `subn` when you need the\nreplacement count (e.g. to detect whether anything changed).\n",{"id":1114,"difficulty":162,"q":1115,"a":1116},"escaping-special-chars","How do you match a literal string that may contain regex metacharacters?","Use **`re.escape()`** to backslash-escape all special characters, so user input or a\nvariable is matched **literally** rather than interpreted as a pattern — preventing\nboth bugs and injection.\n\n```python\nimport re\nuser = \"a.b+c\"\nre.search(re.escape(user), \"xa.b+cy\")    # matches the literal 'a.b+c'\n# without escape, '.' and '+' would be metacharacters\n\npattern = re.compile(re.escape(delimiter))\n```\n\nRule of thumb: wrap any dynamic\u002Fliteral text in `re.escape()` before embedding it in\na pattern — never trust raw input to be regex-safe.\n",{"description":148},"Python interview questions on the re module — match vs search vs fullmatch, capture and named groups, re.compile, greedy vs non-greedy, re.sub, and raw strings.","python\u002Fstdlib\u002Fregex","Regular Expressions","hcdJTRsS7ggEfcZYPo3ad7ItAjWjWjx63rpNqjMEYpM",{"id":1123,"title":1124,"body":1125,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":1129,"navigation":153,"order":13,"path":1130,"questions":1131,"questionsCount":217,"related":218,"seo":1192,"seoDescription":1193,"stem":1194,"subtopic":1195,"topic":126,"topicSlug":128,"updated":223,"__hash__":1196},"qa\u002Fpython\u002Ftesting\u002Fpytest.md","Pytest",{"type":145,"value":1126,"toc":1127},[],{"title":148,"searchDepth":29,"depth":29,"links":1128},[],{},"\u002Fpython\u002Ftesting\u002Fpytest",[1132,1136,1140,1144,1148,1152,1156,1160,1164,1168,1172,1176,1180,1184,1188],{"id":1133,"difficulty":253,"q":1134,"a":1135},"pytest-vs-unittest","What is the difference between pytest and unittest?","**`unittest`** is the **standard-library** framework, modeled on xUnit: tests\nare **methods on a `TestCase` subclass** and you use `self.assertEqual`,\n`self.assertTrue`, etc. **`pytest`** is a **third-party** framework that runs\nplain functions using the bare `assert` statement, with rich failure\nintrospection, fixtures, and `parametrize`.\n\n```python\n# unittest\nimport unittest\nclass TestMath(unittest.TestCase):\n    def test_add(self):\n        self.assertEqual(1 + 1, 2)\n\n# pytest — just a function and assert\ndef test_add():\n    assert 1 + 1 == 2\n```\n\npytest can also **run existing unittest tests**, so adopting it is low-risk.\nRule of thumb: prefer pytest for new code — less boilerplate and better\noutput — while unittest is fine when you must avoid dependencies.\n",{"id":1137,"difficulty":162,"q":1138,"a":1139},"fixtures-scope","What are pytest fixtures, and what does scope control?","A **fixture** is a function decorated with `@pytest.fixture` that provides\n**setup (and teardown) for tests**. A test requests it simply by naming it as\na **parameter**, and pytest injects the return value. Using `yield` lets code\nafter the `yield` run as **teardown**. The **`scope`** controls how often the\nfixture is created: `function` (default), `class`, `module`, or `session`.\n\n```python\nimport pytest\n\n@pytest.fixture(scope=\"module\")   # created once per module\ndef db():\n    conn = connect()\n    yield conn                    # value handed to tests\n    conn.close()                  # teardown after tests finish\n\ndef test_query(db):               # db injected by name\n    assert db.ping()\n```\n\nWider scopes share expensive resources (DB connections, servers) across many\ntests for speed; narrower scopes give better **isolation**. Rule of thumb:\ndefault to `function` scope and only widen when setup is costly.\n",{"id":1141,"difficulty":162,"q":1142,"a":1143},"parametrize","How does @pytest.mark.parametrize work?","`@pytest.mark.parametrize` runs the **same test function multiple times** with\ndifferent arguments, generating **one separate test case per row**. Each case\npasses or fails independently, so a single bad input doesn't hide the others —\nfar cleaner than a loop inside one test.\n\n```python\nimport pytest\n\n@pytest.mark.parametrize(\"value, expected\", [\n    (2, 4),\n    (3, 9),\n    (4, 16),\n])\ndef test_square(value, expected):\n    assert value ** 2 == expected\n```\n\npytest reports each as `test_square[2-4]`, `test_square[3-9]`, etc., and you\ncan stack decorators to get the **cross product** of inputs. Rule of thumb:\nuse `parametrize` for the same logic across many inputs instead of copy-pasted\ntests or in-test loops.\n",{"id":1145,"difficulty":150,"q":1146,"a":1147},"mocking-monkeypatch","How do you mock with monkeypatch versus unittest.mock?","The built-in **`monkeypatch`** fixture **replaces attributes, dict items, or\nenv vars** for the duration of a test and **auto-restores** them afterward —\nideal for swapping out a function or setting `os.environ`. **`unittest.mock`**\n(`Mock`, `patch`) creates **mock objects** that record calls and let you set\nreturn values or side effects — best when you need to **assert how** a\ndependency was called.\n\n```python\nimport requests, mymodule\n\ndef test_with_monkeypatch(monkeypatch):\n    monkeypatch.setattr(requests, \"get\", lambda url: {\"ok\": True})\n    assert mymodule.fetch() == {\"ok\": True}\n\nfrom unittest.mock import patch\ndef test_with_mock():\n    with patch(\"mymodule.requests.get\") as mock_get:\n        mock_get.return_value = {\"ok\": True}\n        mymodule.fetch()\n        mock_get.assert_called_once()   # verify the interaction\n```\n\nA key gotcha: **patch where the name is looked up** (`mymodule.requests.get`),\nnot where it's defined. Rule of thumb: `monkeypatch` for simple replacements,\n`unittest.mock` when you need to **inspect calls**.\n",{"id":1149,"difficulty":162,"q":1150,"a":1151},"pytest-raises","How do you assert that code raises an exception?","Use the **`pytest.raises`** context manager: the test **passes only if** the\nexpected exception is raised inside the `with` block, and **fails** if no\nexception (or the wrong one) occurs. You can capture the exception via\n`as excinfo` to assert on its message, and use `match=` for a regex check.\n\n```python\nimport pytest\n\ndef test_divide_by_zero():\n    with pytest.raises(ZeroDivisionError):\n        1 \u002F 0\n\ndef test_message():\n    with pytest.raises(ValueError, match=\"invalid\"):\n        int(\"not a number\")   # ValueError: invalid literal...\n    # or: assert \"invalid\" in str(excinfo.value)\n```\n\nIt cleanly replaces a `try\u002Fexcept\u002Fpytest.fail` dance. Rule of thumb: always\nassert on the **specific** exception type (and ideally the message) so the\ntest can't pass for the wrong reason.\n",{"id":1153,"difficulty":162,"q":1154,"a":1155},"conftest","What is conftest.py used for?","**`conftest.py`** is a special pytest file for **shared fixtures and hooks**.\npytest **auto-discovers** it — no import needed — and any fixture defined\nthere is **available to every test** in that directory and its subdirectories.\nIt's the standard place to put fixtures used across multiple test files.\n\n```python\n# tests\u002Fconftest.py\nimport pytest\n\n@pytest.fixture\ndef client():\n    return create_test_client()\n\n# tests\u002Ftest_users.py — no import required\ndef test_login(client):           # 'client' resolved from conftest\n    assert client.login(\"ada\")\n```\n\nYou can also register plugins, define hooks like `pytest_addoption`, and place\na `conftest.py` at multiple levels for **scoped** sharing. Rule of thumb: put\na fixture in `conftest.py` once more than one test file needs it, instead of\nimporting it around.\n",{"id":1157,"difficulty":162,"q":1158,"a":1159},"fixture-yield-teardown","How do you do setup and teardown in a pytest fixture?","Use **`yield`**: code before `yield` is **setup**, the yielded value is injected, and\ncode after `yield` is **teardown** (runs even if the test fails). This replaces\nunittest's `setUp`\u002F`tearDown` with a single cohesive function.\n\n```python\nimport pytest\n\n@pytest.fixture\ndef temp_file(tmp_path):\n    p = tmp_path \u002F \"data.txt\"\n    p.write_text(\"hi\")        # setup\n    yield p                   # test runs here\n    p.unlink()                # teardown — always runs\n\ndef test_read(temp_file):\n    assert temp_file.read_text() == \"hi\"\n```\n\nRule of thumb: `yield` fixtures keep setup and matching teardown together; teardown\nruns on failure too, so resources are always released.\n",{"id":1161,"difficulty":253,"q":1162,"a":1163},"builtin-fixtures","What are some useful built-in pytest fixtures?","pytest ships fixtures you request by name: **`tmp_path`** (unique temp `Path`),\n**`monkeypatch`** (patch\u002Frestore), **`capsys`** (capture stdout\u002Fstderr), **`caplog`**\n(capture log records), and **`request`** (test metadata).\n\n```python\ndef test_output(capsys):\n    print(\"hello\")\n    assert capsys.readouterr().out == \"hello\\n\"\n\ndef test_file(tmp_path):\n    (tmp_path \u002F \"f.txt\").write_text(\"x\")   # auto-cleaned temp dir\n\ndef test_logs(caplog):\n    logging.warning(\"oops\")\n    assert \"oops\" in caplog.text\n```\n\nRule of thumb: reach for built-in fixtures (`tmp_path`, `capsys`, `caplog`,\n`monkeypatch`) before writing your own — they handle cleanup for you.\n",{"id":1165,"difficulty":162,"q":1166,"a":1167},"markers-skip-xfail","What do `skip`, `skipif`, and `xfail` markers do?","**`@pytest.mark.skip`** always skips; **`skipif(cond)`** skips conditionally (e.g. by\nplatform\u002Fversion); **`xfail`** marks a test **expected to fail** (a known bug) — it's\nreported separately and doesn't fail the suite.\n\n```python\nimport sys, pytest\n\n@pytest.mark.skip(reason=\"not implemented yet\")\ndef test_future(): ...\n\n@pytest.mark.skipif(sys.version_info \u003C (3, 11), reason=\"needs 3.11+\")\ndef test_new_feature(): ...\n\n@pytest.mark.xfail(reason=\"known bug #123\")\ndef test_buggy():\n    assert broken() == 1\n```\n\nRule of thumb: `skip`\u002F`skipif` for tests that shouldn't run in this environment;\n`xfail` for known failures you want tracked without breaking CI.\n",{"id":1169,"difficulty":150,"q":1170,"a":1171},"parametrize-ids-fixtures","How do you give parametrized cases readable IDs or parametrize a fixture?","Pass **`ids=`** to label cases (instead of auto-generated names), and parametrize a\n**fixture** with `params=` so every test using it runs once per value.\n\n```python\nimport pytest\n\n@pytest.mark.parametrize(\"n, expected\", [(2, 4), (3, 9)],\n                         ids=[\"two\", \"three\"])\ndef test_sq(n, expected):\n    assert n ** 2 == expected      # test_sq[two], test_sq[three]\n\n@pytest.fixture(params=[\"sqlite\", \"postgres\"])\ndef db(request):\n    return connect(request.param)  # tests run against both backends\n```\n\nRule of thumb: use `ids=` for clear test names and parametrized **fixtures** to run\nthe same tests across multiple configurations\u002Fbackends.\n",{"id":1173,"difficulty":162,"q":1174,"a":1175},"fixture-dependencies","How can fixtures depend on other fixtures?","A fixture **requests other fixtures as parameters**, just like a test does — pytest\nresolves the dependency graph and builds them in order. This lets you compose small\nfixtures into larger setups.\n\n```python\nimport pytest\n\n@pytest.fixture\ndef db():\n    return connect()\n\n@pytest.fixture\ndef user(db):                 # depends on db\n    return db.create_user(\"ada\")\n\ndef test_profile(user):       # gets a user (and transitively a db)\n    assert user.name == \"ada\"\n```\n\nRule of thumb: build complex setups by composing fixtures (one requesting another)\nrather than one giant fixture — clearer and more reusable.\n",{"id":1177,"difficulty":162,"q":1178,"a":1179},"assert-introspection","How does pytest give detailed output from a plain `assert`?","pytest **rewrites assert statements** at import time to capture operand values, so a\nfailing `assert a == b` shows the actual values and a diff — no need for\n`assertEqual`. Add an optional message after a comma for context.\n\n```python\ndef test_lists():\n    result = [1, 2, 4]\n    assert result == [1, 2, 3]\n    # pytest shows:\n    #   assert [1, 2, 4] == [1, 2, 3]\n    #     At index 2 diff: 4 != 3\n\nassert total > 0, f\"expected positive, got {total}\"\n```\n\nRule of thumb: just use plain `assert` in pytest — its rewriting gives rich diffs;\nreserve a trailing message for extra context the values alone don't convey.\n",{"id":1181,"difficulty":253,"q":1182,"a":1183},"approx-floats","How do you assert floating-point equality in pytest?","Use **`pytest.approx`**, which compares with a tolerance — avoiding the `0.1 + 0.2 !=\n0.3` trap. It works for numbers, and for lists\u002Fdicts of numbers.\n\n```python\nimport pytest\n\nassert 0.1 + 0.2 == pytest.approx(0.3)\nassert [0.1 + 0.2, 1.0] == pytest.approx([0.3, 1.0])\nassert 100.5 == pytest.approx(100, rel=0.01)   # 1% relative tolerance\n```\n\nRule of thumb: never assert raw `==` on floats — wrap the expected value in\n`pytest.approx` (tune with `rel=`\u002F`abs=`).\n",{"id":1185,"difficulty":162,"q":1186,"a":1187},"test-discovery","How does pytest discover which tests to run?","By default pytest collects files matching **`test_*.py`** \u002F `*_test.py`, **functions\u002F\nmethods prefixed `test_`**, and **classes prefixed `Test`** (with no `__init__`). You\ncan run subsets by node id, keyword (`-k`), or marker (`-m`).\n\n```bash\npytest                       # discover & run everything\npytest tests\u002Ftest_api.py::test_login   # a single test by node id\npytest -k \"login and not slow\"          # match by name expression\npytest -m \"smoke\"                        # run tests marked @pytest.mark.smoke\n```\n\nRule of thumb: follow the `test_*`\u002F`Test*` naming so discovery finds your tests; use\n`-k`\u002F`-m`\u002Fnode ids to run focused subsets during development.\n",{"id":1189,"difficulty":162,"q":1190,"a":1191},"autouse-fixtures","What is an autouse fixture, and when should you use one?","An **autouse** fixture (`@pytest.fixture(autouse=True)`) runs **automatically** for\nevery test in its scope **without being requested** as a parameter. It's meant for\n**cross-cutting setup\u002Fteardown** — resetting global state, seeding a clock, clearing a\ncache — that every test needs but none should have to name explicitly.\n\n```python\nimport pytest\n\n@pytest.fixture(autouse=True)\ndef reset_registry():\n    registry.clear()      # setup runs before each test\n    yield\n    registry.clear()      # teardown runs after each test\n\ndef test_a():             # no parameter, yet reset still runs\n    register(\"x\")\n    assert registry.size() == 1\n```\n\nUse it sparingly: because it fires invisibly, **overusing autouse hides dependencies**\nand makes tests harder to reason about. Rule of thumb: reach for `autouse` only for\ngenuinely universal setup; otherwise request fixtures explicitly so each test's needs\nare visible.\n",{"description":148},"Python interview questions on pytest — pytest vs unittest, fixtures and scope, parametrize, mocking with monkeypatch and unittest.mock, pytest.raises, and conftest.py.","python\u002Ftesting\u002Fpytest","pytest Essentials","vPaugZN5XrVnPBlzoNjeME4Tt2Qd3KkXJ2pVmyKJOo8",{"id":1198,"title":1199,"body":1200,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":1204,"navigation":153,"order":13,"path":1205,"questions":1206,"questionsCount":217,"related":218,"seo":1267,"seoDescription":1268,"stem":1269,"subtopic":1270,"topic":108,"topicSlug":110,"updated":223,"__hash__":1271},"qa\u002Fpython\u002Ftyping\u002Ftype-hints.md","Type Hints",{"type":145,"value":1201,"toc":1202},[],{"title":148,"searchDepth":29,"depth":29,"links":1203},[],{},"\u002Fpython\u002Ftyping\u002Ftype-hints",[1207,1211,1215,1219,1223,1227,1231,1235,1239,1243,1247,1251,1255,1259,1263],{"id":1208,"difficulty":253,"q":1209,"a":1210},"hints-runtime-enforced","Are type hints enforced at runtime?","**No.** Type hints are **annotations**, not constraints — the interpreter\nstores them (in `__annotations__`) but **never checks them**. You can pass\na `str` where an `int` is annotated and Python runs it happily; enforcement\nis the job of an **external static type checker** like **mypy** or **pyright**.\n\n```python\ndef double(n: int) -> int:\n    return n * 2\n\ndouble(\"ab\")          # runs fine -> \"abab\", no TypeError\ndouble.__annotations__  # {'n': \u003Cclass 'int'>, 'return': \u003Cclass 'int'>}\n```\n\nIf you want runtime validation you opt in explicitly — e.g. **pydantic**,\n`typing.get_type_hints`, or manual `isinstance` checks. Rule of thumb: hints\ndocument and enable tooling; they are **not** a runtime guard.\n",{"id":1212,"difficulty":162,"q":1213,"a":1214},"optional-union","What is the difference between Optional, Union, and the `|` operator?","`Union[A, B]` means \"**A or B**\". `Optional[X]` is just shorthand for\n`Union[X, None]` — a value that may be `X` **or** `None`. It does **not**\nmean \"optional argument\"; it means \"could be None\". Since Python 3.10 you\ncan write unions with the **`|` operator** instead of importing from `typing`.\n\n```python\nfrom typing import Optional, Union\n\ndef find(id: int) -> Optional[str]: ...     # str or None\ndef parse(x: Union[int, str]) -> int: ...   # int or str\n\n# Python 3.10+ equivalent, no imports:\ndef find(id: int) -> str | None: ...\ndef parse(x: int | str) -> int: ...\n```\n\nPrefer the modern `X | None` syntax on 3.10+. Reach for `Optional`\u002F`Union`\nfrom `typing` only when supporting older versions. Rule of thumb: `Optional`\nis about **nullability**, never about whether a parameter has a default.\n",{"id":1216,"difficulty":162,"q":1217,"a":1218},"list-vs-generics","What is the difference between `list` and `List`, and how do generics work?","Both annotate a list, but **`List` comes from `typing`** while **`list`** is\nthe built-in. Since **Python 3.9** the built-in containers are themselves\n**subscriptable** (`list[int]`, `dict[str, int]`), so `typing.List`,\n`typing.Dict`, etc. are **deprecated** — use the lowercase built-ins. A bare\n`list` means \"list of anything\"; the **generic** form pins the element type.\n\n```python\nfrom typing import List          # legacy\nnames: List[str] = []\n\nnames: list[str] = []            # modern (3.9+), preferred\nscores: dict[str, int] = {}\npair: tuple[int, str] = (1, \"a\")\n```\n\nGenerics let a checker verify element access and method calls. Rule of thumb:\non 3.9+ always parameterize the **built-in** (`list[str]`), and only import\nfrom `typing` for things with no built-in equivalent (e.g. `Callable`).\n",{"id":1220,"difficulty":150,"q":1221,"a":1222},"any-vs-object","What is the difference between `typing.Any` and `object`?","Both accept any value, but they are **opposites to a type checker**. `object`\nis the real **base of every class** — you can assign anything to it, but you\ncan only do `object`-level operations on it. `Any` is an **escape hatch**: it\nis compatible with **everything in both directions**, so the checker stops\nchecking — any attribute or call is allowed.\n\n```python\ndef f(x: object) -> None:\n    x.upper()        # type error: object has no 'upper'\n\ndef g(x: Any) -> None:\n    x.upper()        # OK — Any disables checking\n    x + 1            # also OK, no complaints\n```\n\nUse `object` when you genuinely accept anything but want to **keep type\nsafety** (forcing you to narrow with `isinstance` first). Use `Any` only to\ndeliberately **opt out** of checking. Rule of thumb: `Any` is contagious and\nhides bugs — prefer `object` or a precise type.\n",{"id":1224,"difficulty":162,"q":1225,"a":1226},"what-mypy-does","What does mypy do, and how is it different from Protocol-based typing?","**mypy** is a **static type checker**: it reads your annotations and flags\ntype mismatches **before you run the code** — no execution, no runtime cost.\nBy default it checks types **nominally** (by inheritance). `typing.Protocol`\nadds **structural typing** (a.k.a. duck typing): a class matches a Protocol\nif it has the right **methods\u002Fattributes**, even without inheriting from it.\n\n```python\nfrom typing import Protocol\n\nclass Closable(Protocol):\n    def close(self) -> None: ...\n\ndef shutdown(r: Closable) -> None:\n    r.close()\n\nclass File:                 # never imports\u002Finherits Closable\n    def close(self) -> None: ...\n\nshutdown(File())            # OK — File structurally matches\n```\n\nSo mypy verifies correctness, and `Protocol` lets it accept **anything with\nthe right shape** rather than a specific base class. Rule of thumb: use\nProtocols to type \"**anything that behaves like X**\" without forcing a\ncommon base class.\n",{"id":1228,"difficulty":162,"q":1229,"a":1230},"callable-annotation","How do you annotate a function or callable parameter?","Use **`Callable[[ArgTypes], ReturnType]`** from `typing` (or `collections.abc`). The\nfirst element is the **list of argument types**, the second the **return type**. Use\n`...` for \"any arguments.\"\n\n```python\nfrom typing import Callable\n\ndef apply(fn: Callable[[int, int], int], a: int, b: int) -> int:\n    return fn(a, b)\n\nhandler: Callable[[str], None]          # takes str, returns None\nany_callable: Callable[..., int]        # any args, returns int\n```\n\nRule of thumb: `Callable[[args], ret]` types higher-order functions\u002Fcallbacks; use\n`...` for the arg list when signatures vary or don't matter.\n",{"id":1232,"difficulty":162,"q":1233,"a":1234},"type-aliases","How do you create a type alias?","Assign a type to a name for reuse and readability; Python 3.12 adds the explicit\n**`type` statement**, and `typing.TypeAlias` annotates one pre-3.12. Aliases make\ncomplex types self-documenting.\n\n```python\nfrom typing import TypeAlias\n\nVector: TypeAlias = list[float]          # pre-3.12 explicit alias\nMatrix = list[list[float]]               # simple assignment also works\n\n# Python 3.12+:\ntype UserId = int\ntype Json = dict[str, \"Json\"] | list[\"Json\"] | str | int | bool | None\n```\n\nRule of thumb: alias complex\u002Frepeated types (`Vector`, `Json`) for clarity; use the\n3.12 `type` statement where available, else `X: TypeAlias = ...`.\n",{"id":1236,"difficulty":162,"q":1237,"a":1238},"type-vs-instance-annotation","What's the difference between annotating with `MyClass` and `type[MyClass]`?","**`x: MyClass`** means an **instance** of the class; **`x: type[MyClass]`** means the\n**class object itself** (or a subclass) — used when you pass classes around, e.g. for\nfactories.\n\n```python\nclass Animal: ...\nclass Dog(Animal): ...\n\ndef feed(a: Animal) -> None: ...        # takes an instance\ndef make(cls: type[Animal]) -> Animal:  # takes the class\n    return cls()\n\nmake(Dog)        # OK — Dog is a type[Animal]\nfeed(Dog())      # OK — an instance\n```\n\nRule of thumb: `type[X]` for \"the class (or subclass) of X\" (factories, registries);\nbare `X` for \"an instance of X.\"\n",{"id":1240,"difficulty":150,"q":1241,"a":1242},"literal-final","What do `Literal` and `Final` express?","**`Literal[\"a\", \"b\"]`** restricts a value to specific **constants** (great for modes\u002F\nflags). **`Final`** marks a name that **must not be reassigned** (a constant), enforced\nby the type checker.\n\n```python\nfrom typing import Literal, Final\n\ndef open_mode(mode: Literal[\"r\", \"w\", \"a\"]) -> None: ...\nopen_mode(\"r\")          # OK\nopen_mode(\"x\")          # type error — not an allowed literal\n\nMAX_SIZE: Final = 100\nMAX_SIZE = 200          # type error — can't reassign a Final\n```\n\nRule of thumb: `Literal` to constrain to an exact set of values; `Final` to declare\nconstants the checker will protect from reassignment.\n",{"id":1244,"difficulty":150,"q":1245,"a":1246},"typeddict","What is `TypedDict` and when do you use it?","**`TypedDict`** types a **dict with a fixed set of string keys and per-key value\ntypes** — ideal for JSON-like records where you want a dict (not a class) but still\nwant type checking on keys\u002Fvalues.\n\n```python\nfrom typing import TypedDict\n\nclass User(TypedDict):\n    name: str\n    age: int\n\nu: User = {\"name\": \"Ada\", \"age\": 36}     # checked\nu2: User = {\"name\": \"Bob\"}               # error — missing 'age'\n\nclass Partial(TypedDict, total=False):    # all keys optional\n    nickname: str\n```\n\nRule of thumb: use `TypedDict` to type structured dicts (API payloads, config) while\nkeeping plain-dict ergonomics; use `total=False` for optional keys.\n",{"id":1248,"difficulty":162,"q":1249,"a":1250},"annotating-self-class","How do you annotate a method that returns its own class?","Use **`typing.Self`** (3.11+) for methods returning the instance's own type — cleaner\nthan a string forward reference and correct under subclassing. Before 3.11, use a\nstring literal (`\"MyClass\"`) or a `TypeVar` bound to the class.\n\n```python\nfrom typing import Self\n\nclass Builder:\n    def add(self, x) -> Self:    # returns the same (sub)class\n        ...\n        return self\n\nclass SubBuilder(Builder): ...\nSubBuilder().add(1)              # inferred as SubBuilder, not Builder\n```\n\nRule of thumb: return `Self` for fluent\u002Fbuilder methods and alternative constructors\nso subclasses get the correct return type.\n",{"id":1252,"difficulty":150,"q":1253,"a":1254},"forward-references","What are forward references and `from __future__ import annotations`?","A **forward reference** annotates a type **not yet defined** (e.g. a class referring\nto itself) using a **string**. `from __future__ import annotations` makes **all**\nannotations strings (lazy), so you can drop the quotes and avoid definition-order\nissues.\n\n```python\nclass Node:\n    def __init__(self, next: \"Node | None\" = None):  # quoted forward ref\n        self.next = next\n\n# or, at the top of the file:\nfrom __future__ import annotations\nclass Node:\n    def __init__(self, next: Node | None = None):    # no quotes needed\n        self.next = next\n```\n\nRule of thumb: quote self\u002Fforward references, or use `from __future__ import\nannotations` to make all annotations lazy strings (then read them via\n`typing.get_type_hints`).\n",{"id":1256,"difficulty":162,"q":1257,"a":1258},"annotating-args-kwargs","How do you annotate `*args` and `**kwargs`?","Annotate the **element type**: `*args: int` means each positional arg is an `int`\n(args is a `tuple[int, ...]`), and `**kwargs: str` means each keyword value is a `str`\n(kwargs is a `dict[str, str]`).\n\n```python\ndef f(*args: int, **kwargs: str) -> None:\n    # args: tuple[int, ...], kwargs: dict[str, str]\n    ...\n\nf(1, 2, 3, name=\"ada\")        # ints positionally, str values by keyword\nf(1, \"x\")                     # type error — \"x\" isn't int\n```\n\nRule of thumb: annotate `*args`\u002F`**kwargs` with the type of **each item**, not the\ntuple\u002Fdict — the checker infers the container.\n",{"id":1260,"difficulty":253,"q":1261,"a":1262},"none-return-annotation","How do you annotate functions that return nothing or never return?","Use **`-> None`** for functions that return nothing (procedures), and **`-> NoReturn`**\n(or `Never` in 3.11+) for functions that **never return normally** — they always\nraise or loop forever.\n\n```python\nfrom typing import NoReturn\n\ndef log(msg: str) -> None:        # returns nothing useful\n    print(msg)\n\ndef fail(msg: str) -> NoReturn:   # always raises\n    raise RuntimeError(msg)\n```\n\nRule of thumb: `-> None` for \"no return value\"; `-> NoReturn`\u002F`Never` for functions\nthat always raise or never terminate (helps the checker's flow analysis).\n",{"id":1264,"difficulty":162,"q":1265,"a":1266},"gradual-typing","What is gradual typing and how do you adopt hints incrementally?","**Gradual typing** lets you add hints **piecemeal** — unannotated code is treated as\n`Any` and not checked, so you can type the most critical modules first. Tools like\nmypy support per-module strictness to ratchet up coverage.\n\n```python\n# type the public API first, leave internals untyped for now\ndef public_api(user_id: int) -> str:\n    return _helper(user_id)        # _helper untyped -> treated as Any\n\ndef _helper(x):                    # no hints yet — not checked\n    return str(x)\n```\n\n```ini\n# mypy.ini — enforce strictness only where ready\n[mypy-myapp.api.*]\ndisallow_untyped_defs = True\n```\n\nRule of thumb: adopt types gradually from the boundaries inward; use per-module mypy\nconfig to enforce strictness only where the code is fully annotated.\n",{"description":148},"Python interview questions on type hints, Optional and Union, generics with list vs List, typing.Any vs object, mypy, and Protocol structural typing.","python\u002Ftyping\u002Ftype-hints","Type Hints & Annotations","wNS5bmz5bQT0DyYR9NoX4QLtVRm7ylSoLqQjO3MXADE",{"id":1273,"title":1274,"body":1275,"description":148,"difficulty":150,"extension":151,"framework":10,"frameworkSlug":8,"meta":1279,"navigation":153,"order":29,"path":1280,"questions":1281,"questionsCount":1346,"related":218,"seo":1347,"seoDescription":1348,"stem":1349,"subtopic":1274,"topic":90,"topicSlug":92,"updated":223,"__hash__":1350},"qa\u002Fpython\u002Fconcurrency\u002Fmultiprocessing.md","Multiprocessing",{"type":145,"value":1276,"toc":1277},[],{"title":148,"searchDepth":29,"depth":29,"links":1278},[],{},"\u002Fpython\u002Fconcurrency\u002Fmultiprocessing",[1282,1286,1290,1294,1298,1302,1306,1310,1314,1318,1322,1326,1330,1334,1338,1342],{"id":1283,"difficulty":150,"q":1284,"a":1285},"multiprocessing-vs-threading","How does multiprocessing sidestep the GIL, and how is it different from threading?","**Threading** runs multiple threads inside **one process** that share one\ninterpreter — and therefore one **GIL** (Global Interpreter Lock), which lets\nonly one thread execute Python bytecode at a time. **Multiprocessing** spawns\n**separate OS processes**, each with its **own** interpreter and **own GIL**, so\nthey can run Python code in **true parallel** on multiple CPU cores.\n\n```python\nfrom multiprocessing import Process\nimport os\n\ndef work():\n    print(f\"running in pid {os.getpid()}\")  # a distinct process each time\n\nif __name__ == \"__main__\":            # required guard on Windows\u002Fspawn\n    ps = [Process(target=work) for _ in range(4)]\n    for p in ps: p.start()\n    for p in ps: p.join()\n```\n\nThe tradeoff: processes don't share memory, so passing data costs\n**serialization (pickling) and IPC overhead**, and each process has higher\nstartup cost than a thread. Rule of thumb: reach for multiprocessing when you\nneed real CPU parallelism, not just concurrency.\n",{"id":1287,"difficulty":162,"q":1288,"a":1289},"process-vs-pool","What is the difference between Process and Pool?","A **`Process`** represents a **single** child process you start and join\nmanually — good when you have a fixed, small number of distinct tasks. A\n**`Pool`** manages a **reusable group of worker processes** and hands out work\nto them, which is far more convenient for **many homogeneous tasks** over a\ndataset.\n\n```python\nfrom multiprocessing import Pool\n\ndef square(n):\n    return n * n\n\nif __name__ == \"__main__\":\n    with Pool(processes=4) as pool:\n        results = pool.map(square, range(10))   # distributed across 4 workers\n    print(results)   # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]\n```\n\n`Pool` reuses workers (amortizing startup cost) and offers `map`, `imap`,\n`apply_async`, etc. Use `Process` for a few long-lived distinct jobs; use `Pool`\nwhen you're fanning the same function over many inputs.\n",{"id":1291,"difficulty":150,"q":1292,"a":1293},"ipc-queue-pipe","How do processes communicate (Queue vs Pipe)?","Because processes don't share memory, they communicate through **IPC**\nprimitives. A **`Queue`** is a **multi-producer\u002Fmulti-consumer**, thread- and\nprocess-safe FIFO — the general-purpose choice. A **`Pipe`** is a faster but\nlower-level **two-endpoint** connection, best for communication between exactly\n**two** processes.\n\n```python\nfrom multiprocessing import Process, Queue\n\ndef producer(q):\n    q.put(\"result\")          # values are pickled across the boundary\n\nif __name__ == \"__main__\":\n    q = Queue()\n    p = Process(target=producer, args=(q,))\n    p.start()\n    print(q.get())           # \"result\"\n    p.join()\n```\n\nBoth serialize objects under the hood, so only **picklable** data flows through\nthem. Use `Queue` for fan-in\u002Ffan-out among many workers; use `Pipe` for a tight\none-to-one channel where you want the lower overhead.\n",{"id":1295,"difficulty":150,"q":1296,"a":1297},"pickling-overhead","What is the pickling overhead, and what can't be pickled?","Every argument and return value crossing a process boundary must be\n**serialized with `pickle`**, sent, then **deserialized** on the other side.\nFor large objects this **copying cost can dwarf the parallelism gains**, and\nsome objects simply **can't be pickled**.\n\n```python\nimport pickle\n\npickle.dumps(lambda x: x)   # PicklingError — lambdas aren't picklable\n# Also unpicklable: open file handles, sockets, locks, db connections,\n# local\u002Fnested functions, and generators.\n```\n\nPicklable things include module-level functions and classes, and basic\ncontainers of picklable values. The practical implications: pass **small,\npicklable** payloads, define worker functions at **module top level**, and\navoid shipping huge data structures between processes. Minimizing what crosses\nthe boundary is the key to multiprocessing performance.\n",{"id":1299,"difficulty":150,"q":1300,"a":1301},"shared-state","How do you share state between processes?","Since each process has its own memory, you need explicit shared-state tools.\n**`Value`** and **`Array`** put simple data in **shared memory** (fast, but\nlimited types and you must guard with a lock). A **`Manager`** hosts richer\nshared objects (`dict`, `list`, etc.) via a **server process** — more flexible\nbut slower because access is proxied.\n\n```python\nfrom multiprocessing import Process, Value, Lock\n\ndef inc(counter, lock):\n    for _ in range(1000):\n        with lock:               # protect the shared value\n            counter.value += 1\n\nif __name__ == \"__main__\":\n    counter = Value(\"i\", 0)      # shared int in shared memory\n    lock = Lock()\n    ps = [Process(target=inc, args=(counter, lock)) for _ in range(4)]\n    for p in ps: p.start()\n    for p in ps: p.join()\n    print(counter.value)         # 4000\n```\n\nPrefer **message passing** (Queue\u002FPipe) over shared state when you can — it's\neasier to reason about. Reach for `Value`\u002F`Array` for hot, simple counters and a\n`Manager` only when you genuinely need shared complex objects.\n",{"id":1303,"difficulty":162,"q":1304,"a":1305},"when-multiprocessing","When should you use multiprocessing instead of threads or asyncio?","Use multiprocessing for **CPU-bound** work — number crunching, image\nprocessing, data transforms — where you need to **saturate multiple cores** with\nPython code. Threads and asyncio can't do that because the **GIL** serializes\nbytecode execution; only separate processes get separate GILs.\n\n```python\nfrom multiprocessing import Pool\n\ndef heavy(n):                      # pure-Python CPU work\n    return sum(i * i for i in range(n))\n\nif __name__ == \"__main__\":\n    with Pool() as pool:           # defaults to os.cpu_count() workers\n        print(pool.map(heavy, [10_000_000] * 8))   # runs in parallel\n```\n\nFor **I\u002FO-bound** work (network, disk, DB), threads or asyncio are usually\nbetter — they're cheaper and the GIL is released during I\u002FO anyway. Rule of\nthumb: CPU-bound -> multiprocessing; I\u002FO-bound -> threads\u002Fasyncio.\n",{"id":1307,"difficulty":162,"q":1308,"a":1309},"main-guard","Why is `if __name__ == \"__main__\"` required with multiprocessing?","On **spawn** start (default on Windows and macOS), each child process\n**re-imports your module** to rebuild the target function. Without the guard,\nthe child would re-run the code that **starts more processes**, causing\ninfinite recursion \u002F a crash. The guard ensures process-launching code runs\n**only in the parent**.\n\n```python\nfrom multiprocessing import Process\n\ndef work(): ...\n\nif __name__ == \"__main__\":     # children import the module but skip this\n    p = Process(target=work)\n    p.start(); p.join()\n```\n\nRule of thumb: always put process creation behind the `__main__` guard, or\nspawn-based multiprocessing will misbehave.\n",{"id":1311,"difficulty":150,"q":1312,"a":1313},"start-methods","What are the spawn, fork, and forkserver start methods?","They control how child processes are created. **fork** (Linux default\nhistorically) copies the parent via `os.fork` — fast, inherits state, but\nunsafe with threads. **spawn** starts a fresh interpreter and re-imports the\nmodule — slower but safe and consistent (default on Win\u002FmacOS, and Linux from\n3.14). **forkserver** forks from a clean server process — a middle ground.\n\n```python\nimport multiprocessing as mp\nmp.set_start_method(\"spawn\")     # set once, at program start\n```\n\nRule of thumb: prefer **spawn** for predictable cross-platform behavior,\nespecially in programs that also use threads.\n",{"id":1315,"difficulty":162,"q":1316,"a":1317},"pool-map-variants","What is the difference between Pool.map, imap, and apply_async?","**`map`** blocks and returns all results as a list (order preserved).\n**`imap`** returns a **lazy iterator** that yields results as they're ready in\norder — better for large inputs. **`apply_async`** submits a **single** call\nand returns an `AsyncResult` you `.get()` later, optionally with a callback.\n\n```python\nwith Pool() as p:\n    p.map(f, data)                       # eager, full list\n    for r in p.imap(f, data): ...        # lazy, streamed\n    res = p.apply_async(f, (x,))         # one call, non-blocking\n    res.get(timeout=10)\n```\n\nRule of thumb: `map` for simple batches, `imap`\u002F`imap_unordered` to stream\nlarge inputs, `apply_async` for individual fire-and-forget calls.\n",{"id":1319,"difficulty":150,"q":1320,"a":1321},"shared-memory-class","What does multiprocessing.shared_memory offer over Queue\u002FPipe?","`Queue`\u002F`Pipe` **copy** data by pickling it across processes. **`shared_memory`**\n(3.8+) exposes a **raw memory block** multiple processes map directly — no\ncopy — ideal for large NumPy arrays. You trade convenience for speed and must\nmanage lifetime (`close()`\u002F`unlink()`) and synchronization yourself.\n\n```python\nfrom multiprocessing import shared_memory\nshm = shared_memory.SharedMemory(create=True, size=1024)\nshm.buf[:4] = b\"data\"            # other procs attach by name\nshm.close(); shm.unlink()\n```\n\nRule of thumb: use shared_memory for big buffers where copying dominates;\nstick with Queue\u002FPipe for ordinary message passing.\n",{"id":1323,"difficulty":162,"q":1324,"a":1325},"pool-vs-processpoolexecutor","How does multiprocessing.Pool differ from ProcessPoolExecutor?","They overlap heavily — both run work across a pool of processes.\n**`Pool`** (older) has a richer API: `map`, `imap`, `imap_unordered`,\n`starmap`, `apply_async` with callbacks. **`ProcessPoolExecutor`** (from\n`concurrent.futures`) has a smaller, unified `submit`\u002F`map` Future-based API\nshared with the thread executor, so it's easy to swap.\n\n```python\nfrom concurrent.futures import ProcessPoolExecutor\nwith ProcessPoolExecutor() as ex:\n    list(ex.map(f, data))        # same code works with ThreadPoolExecutor\n```\n\nRule of thumb: use `ProcessPoolExecutor` for a clean, swappable API; reach\nfor `Pool` when you need its extra methods like `imap_unordered`\u002F`starmap`.\n",{"id":1327,"difficulty":162,"q":1328,"a":1329},"manager","What is a multiprocessing.Manager?","A **Manager** runs a **server process** that hosts shared Python objects —\n`list`, `dict`, `Namespace`, etc. — and hands other processes **proxies** to\nthem. Mutations go through the server (via IPC), so all processes see updates.\nIt's convenient but **slower** than `Value`\u002F`Array` because every access is a\nmessage.\n\n```python\nfrom multiprocessing import Manager, Process\n\nwith Manager() as m:\n    shared = m.dict()\n    shared[\"count\"] = 0\n    # processes given `shared` all see the same dict\n```\n\nRule of thumb: use a Manager for shared high-level objects when convenience\nmatters; use `Value`\u002F`Array`\u002Fshared_memory when speed matters.\n",{"id":1331,"difficulty":162,"q":1332,"a":1333},"value-array","How do Value and Array share state between processes?","**`Value`** and **`Array`** allocate data in **shared memory** holding C\ntypes, so children created via fork\u002Fspawn can read and write the same\nunderlying bytes — much faster than a Manager. Because access isn't atomic for\ncompound updates, they come with a lock you should use (`with val.get_lock()`).\n\n```python\nfrom multiprocessing import Value, Process\n\ncounter = Value(\"i\", 0)          # shared C int\ndef inc():\n    with counter.get_lock():     # guard the read-modify-write\n        counter.value += 1\n```\n\nRule of thumb: use `Value`\u002F`Array` for small fixed shared C data, and always\nguard read-modify-write with the built-in lock.\n",{"id":1335,"difficulty":162,"q":1336,"a":1337},"zombie-join","What happens if you don't join child processes?","Finished children become **zombie\u002Fdefunct** entries until the parent reaps\nthem, and the parent may exit while children still run, leaving **orphans**.\n`join()` waits for completion and cleans up; for pools, the `with` block (or\n`pool.close(); pool.join()`) does this for you.\n\n```python\np = Process(target=work)\np.start()\np.join()                         # reap it; without this -> zombie risk\nprint(p.exitcode)                # 0 = clean exit\n```\n\nRule of thumb: every `start()` should be paired with a `join()` (or use a\npool's context manager) so processes are cleaned up properly.\n",{"id":1339,"difficulty":162,"q":1340,"a":1341},"process-overhead","What is the overhead of multiprocessing?","Each process has **startup cost** (spawning an interpreter, re-importing),\nits **own memory** (no sharing by default), and **IPC cost** to pickle\narguments and results. For small or short tasks this overhead can **exceed**\nthe parallelism gain, making it slower than a simple loop.\n\n```python\n# bad: tiny tasks, huge per-task pickling\u002Fstartup overhead\nwith Pool() as p:\n    p.map(lambda x: x + 1, range(1_000_000))   # also: lambdas aren't picklable!\n```\n\nRule of thumb: multiprocessing pays off only when each task is **CPU-heavy\nenough** to dwarf process startup and data-transfer overhead.\n",{"id":1343,"difficulty":150,"q":1344,"a":1345},"keyboardinterrupt-pool","Why can Ctrl-C behave oddly with multiprocessing pools?","`KeyboardInterrupt` is delivered to **all** processes, and a `pool.map()`\nblocked in the parent may not surface it cleanly, leaving workers hanging. A\ncommon fix is to use **`map_async(...).get(timeout)`** so the main thread stays\ninterruptible, and to ignore SIGINT in workers via an `initializer`.\n\n```python\nimport signal\nfrom multiprocessing import Pool\n\ndef init():\n    signal.signal(signal.SIGINT, signal.SIG_IGN)   # workers ignore Ctrl-C\n\nwith Pool(initializer=init) as p:\n    p.map_async(work, data).get(timeout=999999)    # parent handles Ctrl-C\n```\n\nRule of thumb: for interruptible pools, let workers ignore SIGINT and use the\nasync `.get(timeout=...)` pattern in the parent.\n",16,{"description":148},"Python interview questions on the multiprocessing module: how it sidesteps the GIL, Process vs Pool, IPC with Queue\u002FPipe, pickling limits, and shared state.","python\u002Fconcurrency\u002Fmultiprocessing","35e13D-s9uBqp6huOPp-ADtT6NmYB3yTgLSZuBQRAbM",{"id":1352,"title":1353,"body":1354,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":1358,"navigation":153,"order":29,"path":1359,"questions":1360,"questionsCount":217,"related":218,"seo":1418,"seoDescription":1419,"stem":1420,"subtopic":1421,"topic":28,"topicSlug":30,"updated":223,"__hash__":1422},"qa\u002Fpython\u002Fdata-structures\u002Ftuples.md","Tuples",{"type":145,"value":1355,"toc":1356},[],{"title":148,"searchDepth":29,"depth":29,"links":1357},[],{},"\u002Fpython\u002Fdata-structures\u002Ftuples",[1361,1364,1368,1372,1375,1379,1383,1387,1391,1395,1399,1402,1406,1410,1414],{"id":604,"difficulty":253,"q":1362,"a":1363},"What is the difference between a tuple and a list, and when should you use each?","A **`list`** is **mutable** and variable-length — use it for a **homogeneous,\nchanging** collection you add to, remove from, or reorder. A **`tuple`** is\n**immutable** and fixed — use it for a **heterogeneous, fixed record** whose\nshape won't change, like a coordinate or a database row.\n\n```python\nscores = [90, 85, 70]   # changing collection -> list\nscores.append(60)       # fine\n\npoint = (3, 4)          # fixed record -> tuple\npoint[0] = 9            # TypeError — tuples are immutable\n```\n\nTuples are also slightly **faster and more memory-efficient**, and because\nthey're **hashable** they can serve as dict keys or set members (a list can't).\nRule of thumb: reach for a tuple when the data is a fixed bundle that shouldn't\nchange, and a list when you need to mutate the collection.\n",{"id":1365,"difficulty":253,"q":1366,"a":1367},"packing-unpacking","What are tuple packing and unpacking, and how do you write a single-element tuple?","**Packing** collects several values into one tuple — the parentheses are often\noptional. **Unpacking** spreads a tuple's items into separate names in one\nassignment, which is how you return and receive multiple values cleanly.\n\n```python\nt = 1, 2, 3          # packing (parentheses optional)\na, b, c = t          # unpacking -> a=1, b=2, c=3\nfirst, *rest = t     # extended unpacking -> first=1, rest=[2, 3]\n\none = (5)            # NOT a tuple — just int 5 in parentheses\none = (5,)           # a 1-element tuple — the trailing comma makes it\n```\n\nThe key gotcha is the **single-element tuple**: it's the **trailing comma**, not\nthe parentheses, that creates a tuple — `(5)` is just the integer `5`, while\n`(5,)` is a one-tuple. When in doubt, the comma is what defines a tuple.\n",{"id":1369,"difficulty":162,"q":1370,"a":1371},"shallow-immutability","Is a tuple's immutability deep? Can a tuple contain mutable objects?","A tuple's **immutability is shallow**. You can't reassign or resize its slots,\nbut each slot just holds a **reference** — and if that reference points to a\nmutable object (like a list), that object can still be changed in place.\n\n```python\nt = (1, [2, 3])\nt[1].append(4)     # allowed — mutating the list inside the tuple\nprint(t)           # (1, [2, 3, 4])\n\nt[1] = [9]         # TypeError — can't reassign a tuple slot\n```\n\nA consequence interviewers love: a tuple that contains a list is **not\nhashable**, because hashability requires every element to be immutable too —\nso `hash((1, [2]))` raises `TypeError`. Bottom line: the tuple's structure is\nfrozen, but the objects it points to may not be.\n",{"id":627,"difficulty":162,"q":1373,"a":1374},"What is a namedtuple and how does collections.namedtuple compare to typing.NamedTuple?","A **namedtuple** is an **immutable** tuple subclass with **named fields** —\ngiving you readable, hashable, lightweight records that still behave like\ntuples (index access, unpacking, comparison). There are two ways to define one.\n\n```python\nfrom collections import namedtuple\nPoint = namedtuple(\"Point\", [\"x\", \"y\"])\np = Point(3, 4)\np.x, p[0]        # 3, 3  — named AND index access\np.x = 9          # AttributeError — immutable\n\nfrom typing import NamedTuple\nclass Point(NamedTuple):     # class syntax with type hints + defaults\n    x: int\n    y: int = 0\n```\n\nThe `collections` form is concise; the `typing.NamedTuple` class form adds\n**type annotations, defaults, and the ability to add methods**. Use a namedtuple\nfor small fixed records when you want clarity over a bare tuple; reach for a\n`@dataclass` when you need mutability or richer behavior.\n",{"id":1376,"difficulty":162,"q":1377,"a":1378},"tuple-dict-key","Why can a tuple be used as a dictionary key when a list cannot?","Dict keys and set members must be **hashable**, which in practice means\n**immutable** with a stable hash for the object's lifetime. A tuple of immutable\nvalues is hashable, so it works as a key; a list is mutable and **unhashable**,\nso it doesn't.\n\n```python\ngrid = {}\ngrid[(0, 0)] = \"origin\"   # tuple key — works\ngrid[(1, 2)] = \"point\"\n\ngrid[[3, 4]] = \"x\"        # TypeError: unhashable type: 'list'\n```\n\nThis makes tuples ideal for **composite \u002F multi-part keys** — like `(row, col)`\ncoordinates or `(lat, lon)` pairs. The one caveat: a tuple is only hashable if\n**all its contents are** too, so `(1, [2])` still fails. Use an immutable tuple\nwhenever you need a multi-value key.\n",{"id":1380,"difficulty":253,"q":1381,"a":1382},"tuple-no-parens","Do tuples require parentheses?","**No** — it's the **commas** that make a tuple, not the parentheses. `1, 2, 3`\nis a tuple; the parens just group for clarity or precedence. That's why\nfunctions \"return multiple values\" — they return one tuple built by commas.\n\n```python\nt = 1, 2, 3          # (1, 2, 3)\ndef f(): return 1, 2 # returns the tuple (1, 2)\nx = (5)              # int 5 !  -> need (5,) for a 1-tuple\n```\n\nRule of thumb: the comma creates the tuple; add parentheses for readability or\nwhen a bare comma would be ambiguous.\n",{"id":1384,"difficulty":162,"q":1385,"a":1386},"tuple-performance","Are tuples faster or lighter than lists?","Yes, modestly. Tuples use **less memory** and construct slightly faster\nbecause they're **fixed-size and immutable** — CPython can even cache small\ntuple objects and store constant tuples directly in bytecode. For large\nhot-path data this adds up.\n\n```python\nimport sys\nsys.getsizeof((1, 2, 3))     # smaller\nsys.getsizeof([1, 2, 3])     # larger (over-allocates for growth)\n```\n\nRule of thumb: use a tuple for fixed, read-only groups of values; the\nimmutability buys safety and a small efficiency win.\n",{"id":1388,"difficulty":253,"q":1389,"a":1390},"tuple-methods","What methods do tuples have?","Only two: **`count(x)`** and **`index(x)`**. Tuples are immutable, so there's\nno `append`, `sort`, `remove`, etc. To \"change\" a tuple you build a new one\n(e.g. via concatenation or `sorted()`, which returns a list).\n\n```python\nt = (1, 2, 2, 3)\nt.count(2)           # 2\nt.index(3)           # 3\nsorted(t)            # [1, 2, 2, 3]  -> a new LIST, not a tuple\n```\n\nRule of thumb: tuples are for fixed data; if you need mutation methods, you\nwant a list.\n",{"id":1392,"difficulty":162,"q":1393,"a":1394},"namedtuple-fields","What extra features does a namedtuple add?","It adds **named field access** (`p.x`) on top of normal tuple behavior, plus\nhelpers: **`._fields`**, **`._asdict()`**, **`._replace(...)`** (returns a new\none), and `._make(iterable)`. It stays a real tuple — indexable, unpackable,\nand hashable.\n\n```python\nfrom collections import namedtuple\nPoint = namedtuple(\"Point\", \"x y\")\np = Point(1, 2)\np.x, p[0]            # 1, 1\np._replace(y=9)      # Point(x=1, y=9)  -> new tuple\np._asdict()          # {'x': 1, 'y': 2}\n```\n\nRule of thumb: use a namedtuple for lightweight, immutable records when you\nwant self-documenting field names without writing a class.\n",{"id":1396,"difficulty":162,"q":1397,"a":1398},"tuple-vs-dataclass","When do you choose a namedtuple vs a dataclass?","Use a **namedtuple** when you want an **immutable, tuple-like** record that's\nindexable\u002Funpackable and memory-light. Use a **dataclass** when you need\n**mutability, methods, defaults with logic, type-checked fields**, or\ninheritance. `frozen=True` dataclasses overlap with namedtuples but aren't\ntuples.\n\n```python\nfrom dataclasses import dataclass\n@dataclass\nclass Point:\n    x: int\n    y: int\n    def dist(self): return (self.x**2 + self.y**2) ** 0.5\n```\n\nRule of thumb: namedtuple for simple immutable value records; dataclass when\nyou need behavior, mutability, or richer typing.\n",{"id":651,"difficulty":253,"q":1400,"a":1401},"How does tuple unpacking enable swapping without a temp variable?","`a, b = b, a` works because the right side is **evaluated to a tuple first**,\nthen unpacked into the targets. No temporary variable is needed — Python\nbuilds `(b, a)` and assigns both names at once.\n\n```python\na, b = 1, 2\na, b = b, a          # a=2, b=1\nx, y, z = z, x, y    # rotate three values\n```\n\nRule of thumb: use tuple unpacking for swaps and multi-assignment; it's\nclearer and avoids a scratch variable.\n",{"id":1403,"difficulty":162,"q":1404,"a":1405},"tuple-in-functions","How do *args and tuples relate?","A function's **`*args`** collects extra positional arguments into a **tuple**.\nConversely, a tuple can be **splatted** back into positional arguments with\n`*`. This is the same packing\u002Funpacking machinery tuples use everywhere.\n\n```python\ndef f(*args):\n    print(type(args))     # \u003Cclass 'tuple'>\n\nnums = (1, 2, 3)\nf(*nums)                  # unpack tuple -> f(1, 2, 3)\n```\n\nRule of thumb: `*args` is always a tuple; use `*tuple` to feed its items as\nseparate positional arguments.\n",{"id":1407,"difficulty":162,"q":1408,"a":1409},"tuple-concatenation","What happens when you concatenate or multiply tuples?","`+` and `*` produce **new tuples** (the originals are unchanged, since tuples\nare immutable). This means repeated concatenation in a loop is **O(n²)** —\nbuild a list and convert once if you're accumulating.\n\n```python\n(1, 2) + (3,)            # (1, 2, 3)  new tuple\n(0,) * 3                 # (0, 0, 0)\n# avoid: t = (); for x in xs: t += (x,)   # quadratic\n```\n\nRule of thumb: tuple `+`\u002F`*` is fine for one-offs; accumulate in a list and\nconvert to a tuple at the end.\n",{"id":1411,"difficulty":162,"q":1412,"a":1413},"tuple-comparison","How are tuples compared?","**Lexicographically**, element by element: compare the first items, and only\nif equal move to the next. This makes tuples a natural **multi-key sort key**\nand lets you compare versions or coordinates directly.\n\n```python\n(1, 2) \u003C (1, 3)          # True  -> first equal, 2 \u003C 3\n(2, 0) \u003C (1, 9)          # False -> 2 > 1 decides immediately\nsorted(people, key=lambda p: (p.last, p.first))\n```\n\nRule of thumb: return a tuple as a sort `key` to sort by multiple fields in\npriority order.\n",{"id":1415,"difficulty":253,"q":1416,"a":1417},"tuple-single-element","Why is (5) not a tuple but (5,) is?","Because parentheses are just **grouping**; without a comma `(5)` is the\ninteger `5`. The **trailing comma** is what creates a one-element tuple. This\ntrips people up with single-item tuples and function calls.\n\n```python\ntype((5))        # \u003Cclass 'int'>\ntype((5,))       # \u003Cclass 'tuple'>\ntype(5,)         # also a tuple -> (5,)\n```\n\nRule of thumb: always add the trailing comma for single-element tuples; the\ncomma — not the parens — is the tuple.\n",{"description":148},"Python interview questions on tuples vs lists, packing and unpacking, single-element tuples, shallow immutability, namedtuples, and tuples as dict keys.","python\u002Fdata-structures\u002Ftuples","Tuples & Named Tuples","Qow_EdCDqEbUedIk5HQZvaMuLsMf6rlosxJDASmY7Xg",{"id":1424,"title":1425,"body":1426,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":1430,"navigation":153,"order":29,"path":1431,"questions":1432,"questionsCount":217,"related":218,"seo":1493,"seoDescription":1494,"stem":1495,"subtopic":1496,"topic":63,"topicSlug":65,"updated":223,"__hash__":1497},"qa\u002Fpython\u002Fexceptions\u002Ftry-except.md","Try Except",{"type":145,"value":1427,"toc":1428},[],{"title":148,"searchDepth":29,"depth":29,"links":1429},[],{},"\u002Fpython\u002Fexceptions\u002Ftry-except",[1433,1437,1441,1445,1449,1453,1457,1461,1465,1469,1473,1477,1481,1485,1489],{"id":1434,"difficulty":162,"q":1435,"a":1436},"four-clauses","What do the try, except, else, and finally clauses do and when does each run?","A full statement has four parts. **`try`** holds the code that might fail.\n**`except`** runs only if a matching exception was raised in `try`. **`else`**\nruns only if `try` finished **without** raising — it's for the \"success path\"\ncode you don't want wrapped in the `try`. **`finally`** runs **always**, whether\nthere was an exception or not, and even if you `return`\u002F`break` — it's for cleanup.\n\n```python\ntry:\n    conn = open_db()          # might raise\nexcept DBError as e:\n    log(e)                    # runs only on failure\nelse:\n    conn.commit()             # runs only on success\nfinally:\n    conn.close()              # always runs — cleanup\n```\n\nPutting the success-only code in `else` (instead of at the bottom of `try`) keeps\nthe `try` block narrow, so `except` only catches errors from the risky line.\n",{"id":1438,"difficulty":162,"q":1439,"a":1440},"catch-multiple","How do you catch multiple exception types, and what does `as e` give you?","Group related types in a **tuple** after `except`, or use separate `except`\nclauses when each needs different handling. The `as e` binds the **exception\ninstance**, letting you inspect its message, args, or attributes.\n\n```python\ntry:\n    value = int(raw)\nexcept (ValueError, TypeError) as e:   # tuple -> one handler for both\n    print(f\"bad input: {e}\")           # e is the exception object\nexcept KeyError:                       # separate handler, different logic\n    print(\"missing key\")\n```\n\nClauses are checked **top to bottom**, and the **first match wins** — so order\nspecific exceptions before their base classes. Use the tuple form when several\ntypes share handling; use separate clauses when they don't.\n",{"id":1442,"difficulty":162,"q":1443,"a":1444},"bare-except","Why is a bare `except:` considered bad practice?","A bare `except:` (or `except BaseException:`) catches **everything** — including\n`KeyboardInterrupt` and `SystemExit`, which are how the user Ctrl-Cs or the\nprogram exits. It also **swallows bugs** like `NameError` or `TypeError` that you'd\nrather see crash loudly, making problems invisible.\n\n```python\ntry:\n    do_work()\nexcept:                 # too broad — even Ctrl-C is caught\n    pass                # silent failure — hides real bugs\n\ntry:\n    do_work()\nexcept Exception as e:  # catches errors, not Ctrl-C \u002F SystemExit\n    log.exception(e)    # at least record what happened\n```\n\nCatch the **narrowest** exception you can actually handle. If you must catch\nbroadly, use `except Exception` (not bare), and always log rather than silently\n`pass`.\n",{"id":1446,"difficulty":150,"q":1447,"a":1448},"finally-return","What happens when `finally` contains a `return`?","`finally` always runs, even when `try` or `except` already has a `return`. If\n`finally` itself **returns** (or raises), it **overrides** the pending return or\nexception — the value from `try`\u002F`except` is silently discarded. This is a classic\ngotcha.\n\n```python\ndef f():\n    try:\n        return 1        # value queued...\n    finally:\n        return 2        # ...but finally's return wins\nf()                     # 2, not 1\n\ndef g():\n    try:\n        raise ValueError\n    finally:\n        return \"swallowed\"   # finally return suppresses the exception!\ng()                     # \"swallowed\" — exception vanishes\n```\n\nAvoid `return`\u002F`break` inside `finally`: it masks both return values and\nexceptions. Keep `finally` for cleanup only.\n",{"id":1450,"difficulty":150,"q":1451,"a":1452},"reraise-chaining","How do you re-raise an exception and what is exception chaining?","A bare **`raise`** (no argument) inside an `except` block **re-raises the current\nexception**, preserving its original traceback — useful for logging then letting it\npropagate. To translate one error into another while keeping the cause, use\n**`raise NewError from original`**, which sets `__cause__` and shows both in the\ntraceback.\n\n```python\ntry:\n    parse(config)\nexcept KeyError as e:\n    log.error(\"config invalid\")\n    raise                       # re-raise same exception, original traceback\n\ntry:\n    value = data[\"port\"]\nexcept KeyError as e:\n    raise ConfigError(\"missing port\") from e   # explicit chaining\n```\n\nWhen an exception is raised **inside** an `except` block without `from`, Python\nstill links it implicitly via `__context__` (\"During handling of the above...\"). Use\n`from e` to make the cause explicit, or `from None` to suppress the chain.\n",{"id":1454,"difficulty":162,"q":1455,"a":1456},"else-clause-purpose","What is the point of the else clause on try?","The **`else`** block runs only if the `try` body **raised nothing**, and —\ncrucially — it is **not** protected by the `except`. This lets you keep the\nrisky line minimal in `try` and put the \"on success\" follow-up code in `else`,\nso you don't accidentally catch exceptions from the wrong place.\n\n```python\ntry:\n    f = open(path)\nexcept OSError:\n    handle()\nelse:\n    # runs only if open() succeeded; its errors aren't swallowed above\n    data = f.read()\n    f.close()\n```\n\nRule of thumb: put only the operation that can fail in `try`, and the success\ncontinuation in `else`.\n",{"id":1458,"difficulty":150,"q":1459,"a":1460},"exception-as-scope","Why is the `as e` variable deleted after the except block?","To break a **reference cycle**: the exception holds a traceback that\nreferences the frame, which references `e`. Python **deletes `e` at the end of\nthe except block** to let it be collected promptly. Accessing `e` afterward\nraises `NameError`; copy what you need into another variable first.\n\n```python\ntry:\n    risky()\nexcept ValueError as e:\n    err = str(e)        # capture before the block ends\nprint(e)                # NameError: name 'e' is not defined\nprint(err)              # fine\n```\n\nRule of thumb: don't use the `as` variable after its except block — save any\nneeded data into a separate name.\n",{"id":1462,"difficulty":162,"q":1463,"a":1464},"exception-order","Does the order of except clauses matter?","**Yes.** Clauses are tried **top to bottom**, and the **first matching** one\nwins. A broad parent (`Exception`) before a specific child means the child\nclause is **unreachable**. Always list specific exceptions first, broad ones\nlast.\n\n```python\ntry:\n    parse()\nexcept ValueError:           # specific first\n    ...\nexcept Exception:            # general fallback last\n    ...\n```\n\nRule of thumb: order except clauses from most specific to most general.\n",{"id":1466,"difficulty":253,"q":1467,"a":1468},"catch-tuple","How do you handle several exception types the same way?","Pass a **tuple** of types to a single `except`. They share one handler, and\n`as e` still binds whichever occurred. Don't forget the parentheses — `except\nA, B:` is a syntax error in Python 3.\n\n```python\ntry:\n    load()\nexcept (ValueError, KeyError, TypeError) as e:\n    log.warning(\"bad input: %s\", e)\n```\n\nRule of thumb: group exceptions you handle identically into one tuple-based\nexcept clause.\n",{"id":1470,"difficulty":162,"q":1471,"a":1472},"raise-from","What does `raise X from Y` do?","It sets the new exception's **`__cause__`** to `Y`, producing a \"**The above\nexception was the direct cause...**\" chained traceback. `from None`\n**suppresses** the chain (`__suppress_context__`), useful when the original\nerror is noise. Without `from`, Python still shows the implicit context.\n\n```python\ntry:\n    int(x)\nexcept ValueError as e:\n    raise ConfigError(\"invalid port\") from e   # explicit cause\n\nraise ConfigError(\"invalid port\") from None     # hide the original\n```\n\nRule of thumb: use `from e` when wrapping to preserve the real cause; `from\nNone` to hide an irrelevant internal error.\n",{"id":1474,"difficulty":162,"q":1475,"a":1476},"exception-cost","Are exceptions expensive in Python?","**Raising\u002Fhandling** an exception costs more than a normal return, but\n**setting up** a `try` block is nearly free (especially with the zero-cost\ntry in 3.11+). So EAFP is cheap when failures are **rare**; it only loses to\nLBYL when exceptions fire **frequently** in a hot loop.\n\n```python\n# great when misses are rare:\ntry: return cache[k]\nexcept KeyError: return slow(k)\n\n# if misses are common, a membership check may be faster\n```\n\nRule of thumb: don't fear try\u002Fexcept for the common-success path; reconsider\nonly when the exception path is the frequent one.\n",{"id":1478,"difficulty":150,"q":1479,"a":1480},"try-finally-return-override","What happens when both try and finally return?","The **`finally` return wins** — it overrides any return (or exception) from\nthe `try`\u002F`except`. A `return` (or `break`) in `finally` even **swallows a\npending exception**, silently discarding it. This is a notorious bug source.\n\n```python\ndef f():\n    try:\n        return 1\n    finally:\n        return 2        # f() returns 2, the 1 is lost\n\ndef g():\n    try:\n        raise ValueError\n    finally:\n        return 0        # swallows the ValueError!\n```\n\nRule of thumb: never `return`\u002F`break` from `finally` — it hides results and\nexceptions.\n",{"id":1482,"difficulty":162,"q":1483,"a":1484},"nested-try","How does exception propagation work through nested try blocks?","An exception propagates **outward** through the call stack until a matching\n`except` catches it; unmatched, it keeps bubbling up. Inner `finally` clauses\nrun as the exception passes through them. If nothing catches it, it reaches\nthe top and prints a traceback.\n\n```python\ndef inner():\n    try:\n        raise ValueError\n    finally:\n        print(\"inner cleanup\")   # runs as it propagates\n\ndef outer():\n    try:\n        inner()\n    except ValueError:\n        print(\"caught in outer\")\n```\n\nRule of thumb: catch exceptions at the level that can actually handle them;\nlet `finally` handle cleanup at each layer in between.\n",{"id":1486,"difficulty":162,"q":1487,"a":1488},"assert-vs-raise","When should you use assert instead of raising an exception?","Use **`assert`** only for **internal invariants \u002F debugging checks** that\nshould never fail in correct code — they document assumptions. Don't use them\nto validate **external input or enforce logic**, because `assert` is\n**stripped when Python runs with `-O`**. Validate real conditions with an\nexplicit `raise`.\n\n```python\nassert n >= 0, \"internal: count went negative\"   # invariant\n\nif not user_input:                                # external validation\n    raise ValueError(\"input required\")           # never an assert here\n```\n\nRule of thumb: `assert` for \"this can't happen\" sanity checks; `raise` for\nanything that depends on user input or runtime conditions.\n",{"id":1490,"difficulty":162,"q":1491,"a":1492},"logging-exceptions","How do you log an exception with its traceback?","Inside an `except` block use **`logging.exception(msg)`** (or\n`logger.error(msg, exc_info=True)`), which records the message **plus the full\ntraceback**. Plain `logging.error(str(e))` loses the traceback, making\ndebugging harder.\n\n```python\nimport logging\ntry:\n    process()\nexcept Exception:\n    logging.exception(\"processing failed\")   # message + traceback\n    raise\n```\n\nRule of thumb: use `logging.exception` inside except blocks so the traceback\nis captured, not just the message.\n",{"description":148},"Python interview questions on try\u002Fexcept\u002Felse\u002Ffinally, catching multiple exception types, why bare except is bad, finally with return, and exception chaining with raise from.","python\u002Fexceptions\u002Ftry-except","try \u002F except \u002F else \u002F finally","vOnVElVOsDLd6htSw5FFyKJBcq6LiLBszkLkmrSmFRE",{"id":1499,"title":1500,"body":1501,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":1505,"navigation":153,"order":29,"path":1506,"questions":1507,"questionsCount":1346,"related":218,"seo":1571,"seoDescription":1572,"stem":1573,"subtopic":1574,"topic":72,"topicSlug":74,"updated":223,"__hash__":1575},"qa\u002Fpython\u002Ffunctional\u002Fmap-filter-reduce.md","Map Filter Reduce",{"type":145,"value":1502,"toc":1503},[],{"title":148,"searchDepth":29,"depth":29,"links":1504},[],{},"\u002Fpython\u002Ffunctional\u002Fmap-filter-reduce",[1508,1512,1516,1520,1523,1527,1531,1535,1539,1543,1547,1551,1555,1559,1563,1567],{"id":1509,"difficulty":253,"q":1510,"a":1511},"map-basics","What does map do and is it lazy?","`map(func, iterable)` applies `func` to each item, returning a **lazy iterator** in\nPython 3 — nothing is computed until you iterate it (or wrap it in `list()`). This\nsaves memory on large inputs because results are produced one at a time.\n\n```python\nnums = [1, 2, 3]\nresult = map(lambda x: x * 2, nums)\nprint(result)          # \u003Cmap object ...> — not evaluated yet\nprint(list(result))    # [2, 4, 6] — now it runs\n```\n\nBecause it's lazy, `map` never builds the full result in memory unless you ask for\nit. In Python 2 `map` returned a list — a common gotcha when porting code.\n",{"id":1513,"difficulty":162,"q":1514,"a":1515},"map-multiple-iterables","How does map work with multiple iterables?","Pass several iterables and `map` calls `func` with **one item from each, in\nparallel** — `func` must accept that many arguments. It **stops at the shortest**\niterable, so mismatched lengths simply truncate.\n\n```python\na = [1, 2, 3]\nb = [10, 20, 30]\nlist(map(lambda x, y: x + y, a, b))   # [11, 22, 33]\n\nlist(map(pow, [2, 3, 4], [10, 11]))   # [1024, 177147] — stops at 2 items\n```\n\nThis is handy for element-wise combining without an explicit `zip`. If you need the\n**longest** length (padding the gaps), use `itertools.zip_longest` instead.\n",{"id":1517,"difficulty":253,"q":1518,"a":1519},"filter","What does filter do?","`filter(predicate, iterable)` keeps only the items for which `predicate` returns\ntruthy — also a **lazy iterator** in Python 3. As a special case, passing `None` as\nthe predicate keeps the items that are **truthy themselves**.\n\n```python\nnums = [0, 1, 2, 0, 3]\nlist(filter(lambda x: x > 1, nums))   # [2, 3]\nlist(filter(None, nums))              # [1, 2, 3] — drops falsy values\n```\n\nUse it to drop elements by a condition. For complex conditions, a comprehension\nwith `if` is usually more readable than `filter(lambda ...)`.\n",{"id":399,"difficulty":162,"q":1521,"a":1522},"What is functools.reduce and why isn't it a built-in anymore?","`functools.reduce(func, iterable, initial)` **folds** an iterable into a single\nvalue by repeatedly applying a two-argument function, carrying an accumulator. In\nPython 3 it was **moved out of the builtins into `functools`** — Guido considered it\nless readable than an explicit loop, so it was demoted to discourage casual use.\n\n```python\nfrom functools import reduce\n\nreduce(lambda acc, x: acc + x, [1, 2, 3, 4], 0)   # 10\n# step by step: 0+1=1, 1+2=3, 3+3=6, 6+4=10\n```\n\nFor common reductions, prefer the dedicated built-ins — `sum`, `max`, `min`,\n`math.prod`, `any`, `all` — which are clearer and faster. Reach for `reduce` only\nwhen no built-in expresses the fold.\n",{"id":1524,"difficulty":162,"q":1525,"a":1526},"vs-comprehensions","When should you use map\u002Ffilter versus a comprehension?","A **list\u002Fgenerator comprehension** is the more Pythonic choice when you'd otherwise\nwrite a `lambda`, because it's more readable and avoids the function-call overhead\nper item. `map`\u002F`filter` shine when you can pass an **existing named function**\n(no lambda) and want a lazy iterator with minimal syntax.\n\n```python\nnums = [1, 2, 3, 4]\n\n[x * 2 for x in nums if x % 2]     # comprehension — clearest with a condition\nlist(map(str, nums))               # map with a built-in — clean, no lambda\nlist(map(lambda x: x * 2, nums))   # lambda makes map less readable\n```\n\nRule of thumb: if it needs a `lambda`, use a comprehension; if you're mapping an\nalready-named function, `map` reads fine. For laziness on huge data, a **generator\nexpression** `(... for ...)` gives both.\n",{"id":1528,"difficulty":162,"q":1529,"a":1530},"consume-once","Why can a map or filter object only be consumed once?","`map`\u002F`filter` (and generators) are **one-shot iterators**: iterating them advances\nan internal position that is never reset. Once exhausted, they yield nothing on a\nsecond pass — a frequent source of \"my second loop is empty\" bugs.\n\n```python\ndoubled = map(lambda x: x * 2, [1, 2, 3])\nlist(doubled)    # [2, 4, 6]\nlist(doubled)    # [] — already consumed!\n\ndoubled = list(map(lambda x: x * 2, [1, 2, 3]))  # materialize once\nsum(doubled); max(doubled)   # reuse freely\n```\n\nIf you need to iterate more than once, **convert to a list** (or other concrete\ncollection) up front. Use the lazy iterator directly only when a single pass is\nenough.\n",{"id":1532,"difficulty":162,"q":1533,"a":1534},"map-vs-genexpr","Is map(f, xs) the same as (f(x) for x in xs)?","Functionally yes — both are lazy one-pass iterators. `map` is slightly faster\nwhen `f` is an existing function (no per-item Python-level call setup), but a\n**generator expression wins for readability** when you'd otherwise need a\nlambda. With a lambda, `map` offers no speed benefit.\n\n```python\nmap(str, nums)                    # clean: existing function\n(x * 2 for x in nums)             # clearer than map(lambda x: x*2, nums)\n```\n\nRule of thumb: use `map` with a named function; use a genexpr\u002Fcomprehension\nwhen the transform is an expression or needs a lambda.\n",{"id":1536,"difficulty":162,"q":1537,"a":1538},"map-none-zip","How does map with multiple iterables relate to zip?","`map(f, a, b)` pulls one item from each iterable per call and stops at the\n**shortest** — like `zip` then `starmap`. The function receives one argument\nper iterable.\n\n```python\nlist(map(lambda x, y: x + y, [1, 2, 3], [10, 20]))   # [11, 22] -> stops short\n# equivalent to:\nfrom itertools import starmap\nlist(starmap(lambda x, y: x + y, zip([1,2,3], [10,20])))\n```\n\nRule of thumb: multi-iterable `map` is \"zip + apply\", truncating to the\nshortest input.\n",{"id":1540,"difficulty":253,"q":1541,"a":1542},"filter-none","What does filter(None, iterable) do?","Passing **`None`** as the function makes `filter` keep only **truthy** items —\ndropping `0`, `''`, `None`, empty containers, `False`. It's a quick way to\nremove \"empty\" values.\n\n```python\nlist(filter(None, [0, 1, \"\", \"a\", None, [], [2]]))   # [1, 'a', [2]]\n```\n\nRule of thumb: `filter(None, xs)` is shorthand for \"keep the truthy ones\".\n",{"id":1544,"difficulty":162,"q":1545,"a":1546},"comprehension-over-map-filter","Why do PEP 8 and idiomatic Python often prefer comprehensions?","A comprehension expresses **map + filter together** in one readable\nexpression, avoids lambdas, and returns a concrete list\u002Fset\u002Fdict directly.\n`map`\u002F`filter` chained with lambdas get nested and hard to read. Reserve\n`map`\u002F`filter` for passing **existing** functions.\n\n```python\n[x * 2 for x in nums if x > 0]                   # clear\nlist(map(lambda x: x*2, filter(lambda x: x>0, nums)))   # noisy\n```\n\nRule of thumb: comprehension for transform-and-filter logic; `map`\u002F`filter`\nonly when handing off a named function.\n",{"id":1548,"difficulty":162,"q":1549,"a":1550},"lazy-eval-pitfall","What is the pitfall of map\u002Ffilter being lazy?","They return **one-shot iterators**, not lists. They do **no work until\niterated**, can be **fully consumed once**, and have **no `len()`** or\nindexing. Forgetting this leads to \"empty on the second pass\" bugs and\n`TypeError` on `len`.\n\n```python\nm = map(str, range(3))\nlist(m)            # ['0', '1', '2']\nlist(m)            # []  -> already exhausted\nlen(m)             # TypeError\n```\n\nRule of thumb: wrap in `list()` if you need to reuse, index, or measure the\nresult.\n",{"id":1552,"difficulty":162,"q":1553,"a":1554},"reduce-readability","Why did Guido move reduce out of builtins?","Because for most real uses a **named built-in or a loop is clearer**: `sum`,\n`max`, `min`, `math.prod`, `any`, `all`, `\"\".join`. `reduce` forces the reader\nto mentally execute a fold, which is error-prone. It now lives in `functools`\nto signal \"use sparingly\".\n\n```python\nfrom functools import reduce\nreduce(lambda a, b: a + b, nums)     # prefer sum(nums)\n```\n\nRule of thumb: reach for a specific built-in; use `reduce` only for genuinely\ncustom accumulations.\n",{"id":1556,"difficulty":162,"q":1557,"a":1558},"map-side-effects","Should you use map just to call a function for its side effects?","**No.** Because `map` is lazy, `map(print, items)` does **nothing** until\nconsumed, and using it only for side effects is unidiomatic and confusing.\nUse a plain `for` loop when you want effects, not a transformed result.\n\n```python\nmap(send, messages)              # bug: nothing sent (never iterated)\nfor m in messages:               # correct\n    send(m)\n```\n\nRule of thumb: `map`\u002Fcomprehensions are for producing values; use a `for`\nloop for side effects.\n",{"id":1560,"difficulty":162,"q":1561,"a":1562},"chaining-map-filter","How do you efficiently chain transformations on a large stream?","Chain **lazy** operations — generator expressions or `map`\u002F`filter` — so data\nflows through one item at a time without building intermediate lists. This\nkeeps memory flat even for huge or infinite sources.\n\n```python\nlines = (l.strip() for l in open(\"big.log\"))\nerrors = (l for l in lines if \"ERROR\" in l)\ncodes = map(parse_code, errors)        # nothing materialized yet\nfirst10 = list(islice(codes, 10))      # only now does work happen\n```\n\nRule of thumb: keep the pipeline lazy and materialize only at the end (or with\n`islice`) to process large data with constant memory.\n",{"id":1564,"difficulty":162,"q":1565,"a":1566},"starmap-vs-map","When do you need starmap over map?","When your iterable already contains **argument tuples** and you want them\n**unpacked** into the function. `map` would pass each tuple as a single\nargument; `itertools.starmap` calls `f(*tuple)`.\n\n```python\nfrom itertools import starmap\npoints = [(0, 0), (3, 4)]\nlist(starmap(math.hypot, points))      # [0.0, 5.0]\n```\n\nRule of thumb: `starmap` when elements are pre-packed arg tuples; `map` for\nsingle-argument calls.\n",{"id":1568,"difficulty":253,"q":1569,"a":1570},"dict-from-map","How do you build a dict from map\u002Fzip results?","Feed `(key, value)` pairs into **`dict()`**. Combine `zip` to pair keys with\nvalues, or `map` to compute one side. A dict comprehension is the most\nreadable for non-trivial logic.\n\n```python\nkeys, vals = [\"a\", \"b\"], [1, 2]\ndict(zip(keys, vals))                       # {'a': 1, 'b': 2}\ndict((k, len(k)) for k in words)            # via genexpr\n{k: len(k) for k in words}                  # comprehension, clearest\n```\n\nRule of thumb: `dict(zip(...))` to align two sequences; a dict comprehension\nwhen the value needs computing.\n",{"description":148},"Python interview questions on map laziness and multiple iterables, filter, functools.reduce, map\u002Ffilter vs comprehensions, and consuming lazy iterators only once.","python\u002Ffunctional\u002Fmap-filter-reduce","map, filter & reduce","2w4JeeTKv0T55Bwkdfod2OgGD-GuUTIXWD-ADwrppKg",{"id":1577,"title":1578,"body":1579,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":1583,"navigation":153,"order":29,"path":1584,"questions":1585,"questionsCount":1346,"related":218,"seo":1649,"seoDescription":1650,"stem":1651,"subtopic":1652,"topic":45,"topicSlug":47,"updated":223,"__hash__":1653},"qa\u002Fpython\u002Ffunctions\u002Farguments.md","Arguments",{"type":145,"value":1580,"toc":1581},[],{"title":148,"searchDepth":29,"depth":29,"links":1582},[],{},"\u002Fpython\u002Ffunctions\u002Farguments",[1586,1590,1594,1598,1602,1606,1610,1614,1618,1621,1625,1629,1633,1637,1641,1645],{"id":1587,"difficulty":162,"q":1588,"a":1589},"args-kwargs","What do *args and **kwargs do?","In a function signature, **`*args`** collects extra **positional** arguments\ninto a tuple, and **`**kwargs`** collects extra **keyword** arguments into a\ndict. They let a function accept a variable number of arguments. The names\nare convention — only the `*`\u002F`**` matter.\n\n```python\ndef log(level, *args, **kwargs):\n    print(level, args, kwargs)\n\nlog(\"INFO\", 1, 2, user=\"ada\", id=7)\n# INFO (1, 2) {'user': 'ada', 'id': 7}\n```\n\n`args` is always a `tuple` and `kwargs` always a `dict`. They're essential\nfor writing wrappers\u002Fdecorators that forward arbitrary arguments through to\nanother callable.\n",{"id":1591,"difficulty":253,"q":1592,"a":1593},"positional-vs-keyword","What is the difference between positional and keyword arguments?","**Positional arguments** are matched to parameters by their **order**.\n**Keyword arguments** are matched by **name** (`param=value`), so order\ndoesn't matter among them. At the call site you can mix the two, but every\npositional argument must come **before** any keyword argument.\n\n```python\ndef greet(name, greeting): ...\n\ngreet(\"Ada\", \"Hello\")              # both positional\ngreet(name=\"Ada\", greeting=\"Hi\")   # both keyword (order free)\ngreet(\"Ada\", greeting=\"Hi\")        # mix: positional first\ngreet(name=\"Ada\", \"Hi\")            # SyntaxError — kw before positional\n```\n\nKeyword arguments make calls self-documenting and let you skip over earlier\nparameters that have defaults. Use them for clarity on boolean flags and\nlong argument lists.\n",{"id":1595,"difficulty":253,"q":1596,"a":1597},"default-args","How do default argument values work?","A parameter with `name=value` is **optional** — if the caller omits it, the\ndefault is used. Defaults are evaluated **once**, when the `def` runs, so\nusing a **mutable** default (`[]`, `{}`) is a classic trap: the same object\npersists across calls.\n\n```python\ndef connect(host, port=5432, timeout=30):\n    ...\nconnect(\"db\")                 # uses port=5432, timeout=30\nconnect(\"db\", timeout=5)      # override one by keyword\n\ndef bad(item, bucket=[]):     # DON'T — shared list\n    bucket.append(item); return bucket\n```\n\nThe safe pattern for a mutable default is `bucket=None` plus\n`if bucket is None: bucket = []` inside the body. Parameters with defaults\nmust come after those without.\n",{"id":1599,"difficulty":162,"q":1600,"a":1601},"keyword-only","What are keyword-only arguments?","Any parameter listed **after a bare `*`** (or after `*args`) is\n**keyword-only** — it can never be passed positionally and must be named at\nthe call site. This forces clearer calls and prevents accidental\npositional mistakes.\n\n```python\ndef make_request(url, *, timeout=30, verify=True):\n    ...\n\nmake_request(\"http:\u002F\u002Fx\", timeout=5)     # OK\nmake_request(\"http:\u002F\u002Fx\", 5)             # TypeError — timeout is kw-only\n```\n\nKeyword-only parameters are great for optional flags whose meaning isn't\nobvious from position (especially booleans). The lone `*` is just a\nseparator; it doesn't collect anything.\n",{"id":1603,"difficulty":150,"q":1604,"a":1605},"positional-only","What are positional-only parameters?","Parameters listed **before a `\u002F`** in the signature are **positional-only**\n(Python 3.8+) — they cannot be passed by keyword. This is useful for APIs\nwhere the parameter name is an implementation detail you don't want callers\nto depend on.\n\n```python\ndef divide(a, b, \u002F):\n    return a \u002F b\n\ndivide(10, 2)          # OK\ndivide(a=10, b=2)      # TypeError — a, b are positional-only\n```\n\nIt also frees those names for use in `**kwargs`. Many built-ins (like\n`len`, `pow`) are positional-only. Combined with `*`, a signature can have\npositional-only, normal, and keyword-only sections.\n",{"id":1607,"difficulty":162,"q":1608,"a":1609},"param-ordering","What is the correct order of parameters in a signature?","A full signature follows a fixed order:\n**positional-only `\u002F`, then normal, then `*args`, then keyword-only, then\n`**kwargs`**. Within each group, parameters without defaults precede those\nwith defaults.\n\n```python\ndef f(pos_only, \u002F, normal, *args, kw_only, **kwargs):\n    ...\n\n# call-site unpacking mirrors this:\ndef g(a, b, c): ...\nnums = [1, 2, 3]\ng(*nums)                 # spread list into positionals\ng(**{\"a\": 1, \"b\": 2, \"c\": 3})   # spread dict into keywords\n```\n\nGetting the order wrong is a `SyntaxError`. The `*`\u002F`\u002F` markers partition\nthe signature; remember the sequence \"positional-only → normal → varargs →\nkeyword-only → varkwargs.\"\n",{"id":1611,"difficulty":162,"q":1612,"a":1613},"mutable-default-arg","Why is a mutable default argument dangerous?","The default is evaluated **once at definition time** and **shared across all\ncalls**. A `[]` or `{}` default therefore **persists and accumulates** between\ncalls. Use `None` as the sentinel and create a fresh object inside.\n\n```python\ndef add(item, bucket=[]):        # BUG: one shared list\n    bucket.append(item); return bucket\nadd(1); add(2)                   # [1, 2] !\n\ndef add(item, bucket=None):      # correct\n    if bucket is None: bucket = []\n    bucket.append(item); return bucket\n```\n\nRule of thumb: never use a mutable literal as a default — default to `None`\nand build the object in the body.\n",{"id":1615,"difficulty":162,"q":1616,"a":1617},"unpacking-call","How do * and ** work at the call site?","At a **call**, `*iterable` spreads items into **positional** arguments and\n`**mapping` spreads into **keyword** arguments. It's the inverse of `*args`\u002F\n`**kwargs` in a definition. You can mix them and even use `*` multiple times\n(3.5+).\n\n```python\ndef f(a, b, c): ...\nargs = (1, 2); f(*args, 3)            # f(1, 2, 3)\nkw = {\"b\": 2, \"c\": 3}; f(1, **kw)     # f(1, b=2, c=3)\nf(*[1], *[2], **{\"c\": 3})             # multiple unpacks\n```\n\nRule of thumb: `*`\u002F`**` at the call site unpack collections into arguments;\nin the signature they collect arguments.\n",{"id":580,"difficulty":150,"q":1619,"a":1620},"Does Python pass arguments by value or by reference?","Neither exactly — it's **\"pass by object reference\"** (call by sharing). The\nfunction gets a **reference to the same object**. **Mutating** it (e.g.\n`list.append`) is visible to the caller; **rebinding** the parameter\n(`x = ...`) only changes the local name, not the caller's variable.\n\n```python\ndef f(lst, x):\n    lst.append(1)        # caller sees this (mutation)\n    x = 99               # caller does NOT see this (rebinding)\n\ndata, n = [], 0\nf(data, n)               # data == [1], n == 0\n```\n\nRule of thumb: mutations to the object propagate; reassigning the parameter\nname does not.\n",{"id":1622,"difficulty":162,"q":1623,"a":1624},"kwargs-order","Does **kwargs preserve order?","**Yes** — since Python 3.7, `**kwargs` is an ordinary dict and preserves the\n**order the keyword arguments were passed**. This lets you forward or process\nkwargs predictably (e.g. building HTML attributes in source order).\n\n```python\ndef tag(**attrs):\n    return \" \".join(f'{k}=\"{v}\"' for k, v in attrs.items())\n\ntag(id=\"x\", cls=\"y\")     # 'id=\"x\" cls=\"y\"'  -> order preserved\n```\n\nRule of thumb: you can rely on kwargs insertion order on modern Python.\n",{"id":1626,"difficulty":162,"q":1627,"a":1628},"bare-star","What does a bare `*` in a signature do?","A lone **`*`** marks the start of **keyword-only** parameters — everything\nafter it **must** be passed by name. It's used to force clarity at call sites,\nespecially for boolean flags or options.\n\n```python\ndef connect(host, *, timeout=30, retries=3):\n    ...\nconnect(\"db\", timeout=5)        # OK\nconnect(\"db\", 5)                # TypeError: too many positional args\n```\n\nRule of thumb: put `*` before options you want callers to name explicitly,\navoiding ambiguous positional flags.\n",{"id":1630,"difficulty":150,"q":1631,"a":1632},"slash-positional-only","What does the `\u002F` in a signature mean?","Parameters **before `\u002F`** are **positional-only** — they can't be passed by\nkeyword (3.8+). It mirrors many C built-ins (`len`, `abs`), lets you rename\nparams freely without breaking callers, and avoids name clashes with\n`**kwargs`.\n\n```python\ndef divide(a, b, \u002F):\n    return a \u002F b\ndivide(10, 2)            # OK\ndivide(a=10, b=2)        # TypeError: positional-only\n```\n\nRule of thumb: use `\u002F` for parameters whose names are implementation details\nor that must accept arbitrary keyword keys via `**kwargs`.\n",{"id":1634,"difficulty":253,"q":1635,"a":1636},"arg-vs-param","What is the difference between a parameter and an argument?","A **parameter** is the variable in the **function definition**; an **argument**\nis the actual **value passed** at the call site. Parameters define the\ninterface; arguments fill it in.\n\n```python\ndef greet(name):         # `name` is a parameter\n    ...\ngreet(\"Ada\")             # \"Ada\" is an argument\n```\n\nRule of thumb: parameters live in the `def`, arguments live in the call.\n",{"id":1638,"difficulty":162,"q":1639,"a":1640},"forwarding-args","How do you forward arbitrary arguments to another function?","Accept **`*args, **kwargs`** and pass them straight through with `*args,\n**kwargs`. This is the standard pattern for wrappers, decorators, and\n`super().__init__` chains that shouldn't care about the exact signature.\n\n```python\ndef wrapper(*args, **kwargs):\n    log(\"calling\")\n    return target(*args, **kwargs)     # transparent forwarding\n```\n\nRule of thumb: `*args, **kwargs` in and out is how you write signature-agnostic\nwrappers.\n",{"id":1642,"difficulty":253,"q":1643,"a":1644},"keyword-arg-clarity","When should you call with keyword arguments even if positional is allowed?","Use keywords for **booleans, numbers, and any value whose meaning isn't\nobvious** at the call site. `f(True, False)` is cryptic; `f(verbose=True,\ncache=False)` is self-documenting and resilient to parameter reordering.\n\n```python\nopen(\"f.txt\", \"w\", buffering=1)        # named buffering reads clearly\nsplit(text, maxsplit=1)                # vs split(text, 1)\n```\n\nRule of thumb: pass literals (especially bare `True`\u002F`False`\u002Fnumbers) by\nkeyword for readability.\n",{"id":1646,"difficulty":162,"q":1647,"a":1648},"default-eval-time","When are default argument values evaluated?","**Once, when the `def` executes** (definition time) — not on each call. So a\ndefault referencing a variable captures its **value at definition**, and a\ndefault like `datetime.now()` is **frozen** to one moment. Use `None` + compute\ninside for per-call defaults.\n\n```python\nimport time\ndef stamp(t=time.time()):    # frozen at def time\n    return t\nstamp(); time.sleep(1); stamp()    # same value both times\n\ndef stamp(t=None):                  # fresh each call\n    return time.time() if t is None else t\n```\n\nRule of thumb: if a default must be recomputed per call, default to `None` and\nbuild it in the body.\n",{"description":148},"Python interview questions on args and kwargs, positional vs keyword arguments, defaults, keyword-only and positional-only parameters, unpacking at the call site, and parameter ordering rules.","python\u002Ffunctions\u002Farguments","Function Arguments","JASlkc2YP6c9voJacqZ9yWi8I-cG2pTkF0mxObTjML8",{"id":1655,"title":1656,"body":1657,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":1661,"navigation":153,"order":29,"path":1662,"questions":1663,"questionsCount":217,"related":218,"seo":1723,"seoDescription":1724,"stem":1725,"subtopic":1726,"topic":20,"topicSlug":21,"updated":223,"__hash__":1727},"qa\u002Fpython\u002Ffundamentals\u002Fscope-legb.md","Scope Legb",{"type":145,"value":1658,"toc":1659},[],{"title":148,"searchDepth":29,"depth":29,"links":1660},[],{},"\u002Fpython\u002Ffundamentals\u002Fscope-legb",[1664,1668,1672,1676,1680,1684,1687,1691,1695,1699,1703,1707,1711,1715,1719],{"id":1665,"difficulty":162,"q":1666,"a":1667},"legb-rule","What is the LEGB rule?","**LEGB** describes the order Python searches for a name: **Local** (inside the\ncurrent function), **Enclosing** (any outer functions), **Global** (the module's\ntop level), then **Built-in** (names like `len`, `print`). The first match wins,\nand the search stops there.\n\n```python\nx = \"global\"\ndef outer():\n    x = \"enclosing\"\n    def inner():\n        x = \"local\"\n        print(x)     # \"local\"  — Local found first\n    inner()\nouter()\n```\n\nWhy it matters: nearly every \"why is this variable that value?\" question reduces\nto walking **L -> E -> G -> B** until a name is found.\n",{"id":1669,"difficulty":162,"q":1670,"a":1671},"global-vs-nonlocal","What is the difference between `global` and `nonlocal`?","Both let you **rebind** a name from an outer scope instead of creating a new local.\n`global` targets the **module-level** name; `nonlocal` targets the **nearest\nenclosing function** scope (and that name must already exist there).\n\n```python\ncount = 0\ndef inc():\n    global count\n    count += 1        # rebinds module-level count\n\ndef outer():\n    x = 1\n    def inner():\n        nonlocal x\n        x = 2          # rebinds outer's x, not a new local\n    inner()\n    return x           # 2\n```\n\nRule of thumb: you only need these keywords to **reassign** an outer name — you can\nalways *mutate* an outer mutable object (e.g. `list.append`) without them.\n",{"id":1673,"difficulty":150,"q":1674,"a":1675},"unbound-local-error","Why does assigning to a name make it local and cause UnboundLocalError?","Python decides a name's scope **at compile time** by scanning the whole function\nbody. If a name is **assigned anywhere** in a function, it is treated as **local\nfor the entire function** — even on lines before the assignment. Reading it before\nit's bound raises **UnboundLocalError**.\n\n```python\nx = 10\ndef f():\n    print(x)      # UnboundLocalError: x is local because of the line below\n    x = 20        # this assignment makes x local everywhere in f\n```\n\nThe fix is to declare `global x` (or `nonlocal x`) if you meant the outer name, or\nsimply read a different name. Rule of thumb: **an assignment anywhere makes the\nname local everywhere** in that function.\n",{"id":1677,"difficulty":150,"q":1678,"a":1679},"late-binding-closures","Why do closures in a loop all capture the same value?","Closures capture **variables, not values** — this is **late binding**. The inner\nfunction looks up the loop variable **when it is called**, by which time the loop\nhas finished and the variable holds its final value.\n\n```python\nfuncs = [lambda: i for i in range(3)]\n[f() for f in funcs]      # [2, 2, 2]  — all see the final i\n\n# Fix: bind the current value via a default argument\nfuncs = [lambda i=i: i for i in range(3)]\n[f() for f in funcs]      # [0, 1, 2]\n```\n\nThe default-argument trick captures `i`'s value at definition time. Rule of thumb:\nif loop-created closures behave strangely, you're hitting late binding — bind the\nvalue explicitly.\n",{"id":1681,"difficulty":162,"q":1682,"a":1683},"module-vs-function-shadowing","How does name shadowing work between module and function scope?","A local name **shadows** (hides) an outer name of the same identity for the\nduration of the scope. Assigning to it inside a function creates a separate local\nthat leaves the **module-level** name untouched.\n\n```python\nvalue = \"module\"\ndef f():\n    value = \"function\"   # new local — shadows the global\n    print(value)         # \"function\"\nf()\nprint(value)             # \"module\"  — unchanged\n\nlist = [1, 2]            # shadows the built-in list() in this scope!\n```\n\nWatch out for shadowing **built-ins** (`list`, `id`, `sum`, `type`) — it silently\nbreaks later calls. Rule of thumb: keep names distinct from outer scopes and\nbuilt-ins to avoid surprising lookups.\n",{"id":647,"difficulty":162,"q":1685,"a":1686},"Do comprehensions have their own scope?","Yes. In Python 3 a comprehension runs in its **own implicit function scope**, so\nits loop variable does **not leak** into the surrounding scope. (In Python 2 list\ncomprehensions did leak — a common gotcha when reading old code.)\n\n```python\n[i for i in range(3)]\nprint(i)          # NameError — i never escaped the comprehension\n\nx = 5\n[x for x in range(3)]\nprint(x)          # 5 — outer x untouched\n```\n\nThe comprehension *can* still read enclosing names. Rule of thumb: treat each\ncomprehension like a tiny function — its variables are private to it.\n",{"id":1688,"difficulty":150,"q":1689,"a":1690},"closure-cell-vars","How can you inspect what a closure has captured?","A closure stores captured variables in **cell objects**, exposed via\n`__closure__` (the cells) and `__code__.co_freevars` (their names). This is how you\nprove a function really closed over an outer variable.\n\n```python\ndef make(n):\n    def f():\n        return n\n    return f\n\ng = make(42)\ng.__code__.co_freevars       # ('n',)\ng.__closure__[0].cell_contents  # 42\n```\n\nRule of thumb: if `__closure__` is `None`, the function captured nothing and is\neffectively a plain function.\n",{"id":1692,"difficulty":162,"q":1693,"a":1694},"default-arg-evaluation-scope","In which scope are default argument values evaluated?","Default values are evaluated **once, at function-definition time**, in the\n**enclosing** scope — not each call, and not in the function's local scope. This is\nwhy mutable defaults are shared and why they can't reference other parameters.\n\n```python\ny = 10\ndef f(a, b=y):    # b's default is bound to 10 right now\n    return a, b\ny = 99\nf(1)              # (1, 10) — later change to y is irrelevant\n\n# def g(a, b=a): ...  # NameError — a isn't in scope when default is evaluated\n```\n\nRule of thumb: defaults are snapshots taken at `def` time in the outer scope, so\navoid mutable defaults and don't expect them to see sibling arguments.\n",{"id":1696,"difficulty":162,"q":1697,"a":1698},"globals-locals-functions","What do the `globals()` and `locals()` functions return?","`globals()` returns the **module's namespace dict** (live — editing it changes real\nglobals). `locals()` returns a dict **snapshot** of the current local namespace;\nwriting to it generally does **not** reliably update real locals inside a function.\n\n```python\nx = 1\ndef f():\n    y = 2\n    globals()['x'] = 99    # actually changes module x\n    locals()['y'] = 100    # usually has NO effect on y\n    return y               # still 2\nf()\nprint(x)                   # 99\n```\n\nRule of thumb: `globals()` is a real handle you can mutate; `locals()` inside a\nfunction is read-only-in-practice — don't rely on assigning through it.\n",{"id":1700,"difficulty":162,"q":1701,"a":1702},"nested-function-scope-lookup","Can a nested function read an enclosing variable without `nonlocal`?","Yes — **reading** an enclosing name needs nothing special; LEGB finds it. You only\nneed `nonlocal` to **rebind** it. The keyword's sole purpose is assignment.\n\n```python\ndef outer():\n    msg = \"hi\"\n    def inner():\n        print(msg)      # fine — reads enclosing msg\n    inner()\n\ndef outer2():\n    n = 0\n    def inc():\n        n += 1          # UnboundLocalError without nonlocal\n    inc()\n```\n\nRule of thumb: read freely across scopes; reach for `global`\u002F`nonlocal` only the\nmoment you need to **assign**.\n",{"id":1704,"difficulty":150,"q":1705,"a":1706},"class-body-scope","Why can't methods see class-body variables directly?","The **class body** is its own scope that exists only while the class is being\ndefined; it is **not** an enclosing scope for its methods. So a method can't see a\nclass-level name via plain LEGB — it must qualify it with `self.` or `ClassName.`.\n\n```python\nclass C:\n    factor = 10\n    def scale(self, x):\n        return x * factor          # NameError — factor isn't enclosing\n    def scale_ok(self, x):\n        return x * self.factor      # correct\n```\n\nComprehensions in a class body are also affected — they can't see other class vars.\nRule of thumb: class-body names are attributes, reachable only through `self`\u002Fthe\nclass, never as free variables in methods.\n",{"id":1708,"difficulty":162,"q":1709,"a":1710},"del-and-scope","What does `del` do to a name's scope?","`del name` **unbinds** the name in the current scope — it removes the binding, not\nnecessarily the object. The name still counts as **local** (assignment\u002Fdel makes it\nlocal), so reading it afterward raises `UnboundLocalError`\u002F`NameError`.\n\n```python\ndef f():\n    x = 1\n    del x\n    print(x)     # UnboundLocalError — x is local but now unbound\n\ny = [1, 2, 3]\ndel y[0]         # this deletes an element, not the name\n```\n\nRule of thumb: `del` on a bare name removes the binding (and the name stays local);\n`del` on a subscript\u002Fattribute deletes that item or attribute.\n",{"id":1712,"difficulty":162,"q":1713,"a":1714},"builtins-override","What happens if you assign to a built-in name at module level?","You create a **global** that shadows the built-in for that module. Built-ins are the\nlast place LEGB looks, so a same-named global wins everywhere in the module — often\nbreaking later code that expected the original.\n\n```python\nsum = 0\ntotal = sum([1, 2, 3])    # TypeError: 'int' object is not callable\n\n# recover the built-in if needed:\nimport builtins\nsum = builtins.sum\n```\n\nRule of thumb: never name variables `list`, `dict`, `str`, `sum`, `id`, `type`,\n`input`, etc. — shadowing built-ins causes confusing failures far from the cause.\n",{"id":1716,"difficulty":150,"q":1717,"a":1718},"free-variable-vs-global","What is a \"free variable\" and how does it differ from a global?","A **free variable** is a name used in a function but bound in an **enclosing\nfunction** scope (the E in LEGB) — it lives in a closure cell. A **global** is bound\nat module level (the G). The compiler classifies each name as local, free, or\nglobal at compile time.\n\n```python\ng = 1                     # global\ndef outer():\n    e = 2                 # will be a free var for inner\n    def inner():\n        return g + e      # g is global, e is free\n    return inner\nouter().__code__.co_freevars   # ('e',)  — only e is free\n```\n\nRule of thumb: free = captured from an enclosing function (closure); global =\nmodule-level. `nonlocal` targets free variables, `global` targets globals.\n",{"id":1720,"difficulty":162,"q":1721,"a":1722},"monkeypatch-module-scope","Why does patching a global affect already-defined functions but rebinding a local doesn't?","Functions look up **global** names **at call time**, by name, in the module dict —\nso changing the global before the call changes what the function sees. Locals are\nresolved per call and per scope, so rebinding one elsewhere can't reach in.\n\n```python\nRATE = 0.1\ndef price(x):\n    return x * RATE        # reads RATE at call time\n\nRATE = 0.2                  # patch the global\nprice(100)                  # 20.0 — sees the new value\n```\n\nThis is why monkeypatching module-level config or functions works. Rule of thumb:\nglobals are resolved late (by name, each call); locals are fixed within their scope.\n",{"description":148},"Python interview questions on the LEGB scope rule, global vs nonlocal, UnboundLocalError, late binding in loop closures, and name shadowing.","python\u002Ffundamentals\u002Fscope-legb","Variables, Scope & the LEGB Rule","9a338XX5MBD0P2sq4EuCf8lz964e2Gu0mmf64LTqcUY",{"id":1729,"title":1730,"body":1731,"description":148,"difficulty":253,"extension":151,"framework":10,"frameworkSlug":8,"meta":1735,"navigation":153,"order":29,"path":1736,"questions":1737,"questionsCount":217,"related":218,"seo":1798,"seoDescription":1799,"stem":1800,"subtopic":1801,"topic":135,"topicSlug":137,"updated":223,"__hash__":1802},"qa\u002Fpython\u002Fidioms\u002Fpep8-style.md","Pep8 Style",{"type":145,"value":1732,"toc":1733},[],{"title":148,"searchDepth":29,"depth":29,"links":1734},[],{},"\u002Fpython\u002Fidioms\u002Fpep8-style",[1738,1742,1746,1750,1754,1758,1762,1766,1770,1774,1778,1782,1786,1790,1794],{"id":1739,"difficulty":253,"q":1740,"a":1741},"what-is-pep8","What is PEP 8?","**PEP 8** is the official **style guide for Python code** — a set of conventions for\nformatting and naming that make code **consistent and readable** across the\ncommunity. It's a *recommendation*, not a language rule: code that violates PEP 8\nstill runs fine, but consistency aids collaboration.\n\n```python\n# PEP 8 style\ndef calculate_total(items, tax_rate=0.0):\n    subtotal = sum(items)\n    return subtotal * (1 + tax_rate)\n\n# not PEP 8 — cramped, inconsistent spacing\u002Fnaming\ndef calcTotal(items,taxRate=0.0):\n    subTotal=sum(items);return subTotal*(1+taxRate)\n```\n\nPEP 8 covers indentation (4 spaces), naming, whitespace, imports, and line length.\nIts guiding principle: **readability counts**, since code is read far more often than\nit's written.\n",{"id":1743,"difficulty":253,"q":1744,"a":1745},"naming-conventions","What are PEP 8's naming conventions?","PEP 8 assigns a distinct case to each kind of name so readers can tell them apart at\na glance. **`snake_case`** for functions, variables, and modules;\n**`PascalCase`** (CapWords) for classes; **`UPPER_SNAKE_CASE`** for constants. A\nsingle leading underscore signals \"internal\".\n\n```python\nMAX_RETRIES = 3                  # constant — UPPER_SNAKE_CASE\n\nclass HttpClient:                # class — PascalCase\n    def send_request(self):      # method — snake_case\n        retry_count = 0          # variable — snake_case\n        self._session = None     # _leading underscore = \"internal\"\n```\n\nAvoid single-character names like `l`, `O`, `I` (they look like digits). Method\u002F\nfunction names use the same `snake_case` as variables; only classes and exceptions\nuse `PascalCase`.\n",{"id":1747,"difficulty":253,"q":1748,"a":1749},"imports-line-length","What does PEP 8 say about imports and line length?","**Imports** go at the **top** of the file, **one per line**, grouped in order:\nstandard library, third-party, then local — separated by blank lines. For **line\nlength**, PEP 8 recommends a maximum of **79 characters** (72 for docstrings\u002F\ncomments), though many modern teams relax this to 88 or 100.\n\n```python\n# standard library\nimport os\nimport sys\n\n# third-party\nimport requests\n\n# local\nfrom myapp.utils import helper\n\n# avoid: import os, sys   (multiple on one line)\n```\n\nKeeping imports sorted and grouped makes dependencies obvious. The line-length cap\nkeeps code readable in side-by-side diffs and narrow editors; tools like `isort`\nautomate import ordering.\n",{"id":1751,"difficulty":253,"q":1752,"a":1753},"zen-of-python","What is the Zen of Python?","The **Zen of Python** (PEP 20) is a collection of **19 guiding aphorisms** that\ncapture Python's design philosophy. You can read it any time by running\n**`import this`**. It informs *why* the language and its idioms look the way they do.\n\n```python\nimport this\n# Beautiful is better than ugly.\n# Explicit is better than implicit.\n# Simple is better than complex.\n# Readability counts.\n# There should be one-- and preferably only one --obvious way to do it.\n# ...and 14 more\n```\n\nThe aphorisms favor **clarity, simplicity, and explicitness** over cleverness. They\naren't enforced rules but a cultural compass — when two approaches compete, the Zen\nusually points to the more Pythonic one.\n",{"id":1755,"difficulty":253,"q":1756,"a":1757},"formatters-linters","What are black and ruff, and how do formatters differ from linters?","A **formatter** automatically **rewrites** your code into a consistent layout; a\n**linter** **analyzes** code and **reports** style violations and likely bugs without\n(usually) changing it. **black** is the dominant formatter (opinionated,\nnear-zero-config); **ruff** is an extremely fast linter (and formatter) that\nconsolidates many older tools.\n\n```python\n# before black\nx = {'a':1,'b':2}\n# after black\nx = {\"a\": 1, \"b\": 2}\n\n# command line\n# black .          -> reformats files in place\n# ruff check .     -> reports lint issues\n# ruff check --fix -> auto-fixes what it can\n```\n\nRunning a formatter ends style arguments in code review (the tool decides), while a\nlinter catches unused imports, undefined names, and anti-patterns. Most teams run\nboth, often automatically via pre-commit hooks or CI.\n",{"id":1759,"difficulty":162,"q":1760,"a":1761},"when-break-pep8","When is it acceptable to break PEP 8?","PEP 8 itself says **\"A Foolish Consistency is the Hobgoblin of Little Minds\"** —\nstyle serves readability, so break the rules when following them would make code\n**less readable** or when you must stay consistent with **surrounding code** or an\nexisting API.\n\n```python\n# matching an external library's camelCase API\ndef setUp(self):           # unittest requires this exact name\n    ...\n\n# aligning related assignments can aid readability in some cases\nx      = 1\nlonger = 2\n```\n\nLegitimate reasons: compatibility with code that predates PEP 8, conforming to a\nframework's required names, or when a rule genuinely hurts clarity in context. The\nrule of thumb: **deviate only with a clear readability or compatibility\njustification**, not out of laziness.\n",{"id":1763,"difficulty":253,"q":1764,"a":1765},"whitespace-rules","What are PEP 8's key whitespace rules?","Use a single space **around binary operators and after commas**, but **no** space\ninside brackets, before a call's parenthesis, or around `=` for **keyword\narguments\u002Fdefaults**. Consistent whitespace is a big part of the PEP 8 look.\n\n```python\nx = a + b                  # spaces around operators\nf(a, b, c)                 # space after commas, none before \"(\"\nd = {\"k\": 1}               # no space inside braces\ndef g(x, y=0): ...         # no spaces around = for defaults\nresult = func(value=10)    # no spaces around = for kwargs\n```\n\nRule of thumb: spaces around operators and after commas; no spaces hugging brackets\nor around `=` in argument lists. Let a formatter enforce it.\n",{"id":1767,"difficulty":253,"q":1768,"a":1769},"blank-lines","How many blank lines does PEP 8 recommend between definitions?","**Two** blank lines between **top-level** functions and classes; **one** blank line\nbetween **methods** inside a class. Use blank lines sparingly within functions to\nseparate logical sections.\n\n```python\nimport os\n\n\ndef first():            # 2 blank lines before top-level defs\n    pass\n\n\nclass C:\n    def method_a(self):  # 1 blank line between methods\n        pass\n\n    def method_b(self):\n        pass\n```\n\nRule of thumb: 2 blank lines around top-level defs\u002Fclasses, 1 between methods — it\nvisually chunks the file into units.\n",{"id":1771,"difficulty":162,"q":1772,"a":1773},"docstring-conventions","What does PEP 257 say about docstrings?","**PEP 257** covers docstring conventions: use **triple double-quotes**, write a\none-line summary as an **imperative phrase** ending in a period, and for multi-line\ndocstrings put the closing `\"\"\"` on its own line. They're accessible via `__doc__`.\n\n```python\ndef fetch(url):\n    \"\"\"Return the response body for the given URL.\"\"\"   # one-liner\n\ndef process(data):\n    \"\"\"Transform and validate the input data.\n\n    Longer explanation of behavior, args, and return value.\n    \"\"\"\n    ...\n```\n\nRule of thumb: every public module\u002Fclass\u002Ffunction gets a docstring; first line is a\nconcise imperative summary (\"Return…\", \"Compute…\"), not \"This function…\".\n",{"id":1775,"difficulty":253,"q":1776,"a":1777},"comparison-style","What does PEP 8 recommend for comparisons to None and booleans?","Compare to `None` with **`is`\u002F`is not`**, never `==`. Don't compare booleans with\n`==` — test truthiness directly. And use `if x is not None`, not `if not x is None`,\nfor readability.\n\n```python\nif x is None: ...           # good\nif x == None: ...           # avoid\n\nif flag: ...                # good\nif flag == True: ...        # avoid\n\nif x is not None: ...        # good (not: \"if not x is None\")\n```\n\nRule of thumb: `is`\u002F`is not` for `None`; direct truthiness for booleans — explicit\n`== True`\u002F`== None` is noisy and occasionally wrong.\n",{"id":1779,"difficulty":162,"q":1780,"a":1781},"line-continuation","How should you break long lines in PEP 8 style?","Prefer **implicit continuation inside parentheses\u002Fbrackets\u002Fbraces** over backslashes.\nAlign wrapped elements or use a hanging indent, and put binary operators **before**\nthe operand on the next line (PEP 8's updated guidance).\n\n```python\n# preferred: implicit continuation\ntotal = (first_value\n         + second_value\n         - third_value)\n\nresult = some_function(\n    arg_one,\n    arg_two,\n)\n\n# avoid backslashes:\ntotal = first_value + \\\n        second_value\n```\n\nRule of thumb: wrap inside brackets\u002Fparens (no backslashes); break before operators\nand use a trailing comma so diffs stay clean.\n",{"id":1783,"difficulty":162,"q":1784,"a":1785},"type-hints-style","What are the PEP 8 spacing rules for type annotations?","Put a **space after the colon** (not before) in variable\u002Fparameter annotations, and\nspaces **around `->`** for return types. With an annotation, also put spaces around\n`=` for defaults (unlike unannotated defaults).\n\n```python\ndef f(x: int, y: str = \"a\") -> bool:   # space after :, around ->, around =\n    count: int = 0                      # annotated variable\n    return True\n\ndef g(x, y=\"a\"):                        # no annotation -> no spaces around =\n    ...\n```\n\nRule of thumb: `name: type`, `-> ret`, and `param: type = default` (spaces around `=`\nonly when the parameter is annotated).\n",{"id":1787,"difficulty":162,"q":1788,"a":1789},"dunder-naming","What's the difference between `_name`, `__name`, and `__name__` conventions?","**`_name`** = \"internal, by convention\" (not enforced). **`__name`** (leading only)\ntriggers **name mangling** to avoid subclass clashes. **`__name__`** (dunder) is\nreserved for **Python's own special names** — don't invent your own.\n\n```python\nclass C:\n    def __init__(self):\n        self.public = 1       # public API\n        self._internal = 2    # \"don't touch\" hint\n        self.__mangled = 3    # -> self._C__mangled\n# __dunder__ names like __init__, __repr__ are Python's — never make new ones\n```\n\nRule of thumb: `_x` for internal, `__x` only when you need mangling, and never create\nyour own `__dunder__` names — they're reserved.\n",{"id":1791,"difficulty":253,"q":1792,"a":1793},"trailing-whitespace-eof","What small formatting details does PEP 8 require at line and file level?","No **trailing whitespace**, files should **end with a single newline**, and use\n**spaces, never tabs** for indentation (4 per level). Mixing tabs and spaces is a\n`TabError` in Python 3.\n\n```python\ndef f():\n    return 1        # 4 spaces, no trailing space after \"1\"\n# file ends with exactly one newline here\n```\n\nRule of thumb: 4-space indent, no tabs, no trailing whitespace, newline at EOF — all\nauto-handled by formatters and editor \"trim on save\" settings.\n",{"id":1795,"difficulty":253,"q":1796,"a":1797},"string-quote-consistency","Does PEP 8 mandate single or double quotes?","PEP 8 takes **no position** on single vs double quotes — just be **consistent**.\nPick one for normal strings and use the other to avoid escaping. Tools like **black**\nstandardize on double quotes.\n\n```python\nname = \"Ada\"               # black normalizes to double quotes\nmsg = 'He said \"hi\"'       # use single to avoid escaping the inner \"\nsql = \"SELECT 'x'\"         # use double to avoid escaping the inner '\n```\n\nRule of thumb: consistency over preference — adopt a formatter's choice (usually\ndouble quotes) and switch quote style only to avoid escapes.\n",{"description":148},"Python interview questions on PEP 8, naming conventions, imports and line length, the Zen of Python, formatters and linters like black and ruff, and when it is acceptable to break PEP 8.","python\u002Fidioms\u002Fpep8-style","PEP 8 & Style","AsKJoycOok1CY4S-JXwdWX6wKiWtvoQHwXR95sOkWgE",{"id":1804,"title":1805,"body":1806,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":1810,"navigation":153,"order":29,"path":1811,"questions":1812,"questionsCount":217,"related":218,"seo":1869,"seoDescription":1870,"stem":1871,"subtopic":1872,"topic":99,"topicSlug":101,"updated":223,"__hash__":1873},"qa\u002Fpython\u002Finternals\u002Fidentity-interning.md","Identity Interning",{"type":145,"value":1807,"toc":1808},[],{"title":148,"searchDepth":29,"depth":29,"links":1809},[],{},"\u002Fpython\u002Finternals\u002Fidentity-interning",[1813,1817,1819,1822,1825,1829,1833,1837,1841,1845,1849,1853,1857,1861,1865],{"id":1814,"difficulty":162,"q":1815,"a":1816},"is-vs-equals","What is the difference between is and ==?","**`==`** tests **value equality** — \"do these represent the same data?\" — by\ncalling the object's `__eq__`. **`is`** tests **identity** — \"are these the\n*exact same object* in memory?\" — which is effectively an `id()` comparison. They\nfrequently agree, but conceptually they ask completely different questions.\n\n```python\na = [1, 2, 3]\nb = [1, 2, 3]\na == b      # True  — equal contents\na is b      # False — two distinct list objects\n\nc = a\nc is a      # True  — same object, just another name\n```\n\nUse `==` whenever you care about the **value**, which is almost always. Reserve\n`is` for **identity** checks against singletons. Rule of thumb: if you're\ncomparing data, use `==`; if you're checking \"is this literally that object,\" use\n`is`.\n",{"id":560,"difficulty":162,"q":561,"a":1818},"**`id(obj)`** returns a unique integer **identity** for an object that's constant\nfor the object's **lifetime**. In **CPython** it's the object's **memory\naddress**, though that's an implementation detail. Two names with the same `id`\nrefer to the **same object**, and `is` is essentially an `id` comparison.\n\n```python\na = [1, 2]\nb = a\nid(a) == id(b)   # True  — same object\na is b           # True  — equivalent check\n\nc = [1, 2]\nid(a) == id(c)   # False — equal value, different object\n```\n\n`id` is handy for understanding **aliasing** — why mutating through one name\nshows up through another. Don't rely on the actual numeric value (it can be\nreused after an object is garbage-collected); use it only to reason about\nsameness.\n",{"id":564,"difficulty":162,"q":1820,"a":1821},"What is the small-int cache?","CPython **pre-creates and caches integers from −5 to 256** as singletons at\nstartup. Any time a value in that range is needed, the **same cached object** is\nreused — so equal small ints share identity and `is` returns `True`. Outside that\nwindow, equal integers are typically **distinct** objects.\n\n```python\na = 256\nb = 256\na is b      # True  — both point at the cached 256\n\nc = 257\nd = 257\nc is d      # often False — separate objects\nc == d      # True  — always compare values with ==\n```\n\nThis is purely a **memory\u002Fperformance optimization** and a CPython\nimplementation detail — not a language guarantee. It's the single biggest reason\n`is` \"appears\" to work on numbers. Rule of thumb: never use `is` to compare ints,\nuse `==`.\n",{"id":568,"difficulty":162,"q":1823,"a":1824},"What is string interning and sys.intern?","**Interning** stores a **single shared copy** of a string so identical strings\ncan be the **same object**, saving memory and making equality checks a fast\npointer comparison. CPython **auto-interns** short, identifier-like string\nliterals (and compile-time constants); other strings may not be. You can force it\nwith **`sys.intern`**.\n\n```python\na = \"hello\"\nb = \"hello\"\na is b           # True  — auto-interned literal\n\nc = \"hello world!\"\nd = \"hello world!\"\nc is d           # often False — not auto-interned\n\nimport sys\ne = sys.intern(\"hello world!\")\nf = sys.intern(\"hello world!\")\ne is f           # True — explicitly interned\n```\n\n`sys.intern` is useful when you compare the **same strings repeatedly** (parsing,\ntokenizing, dict keys) and want the speed\u002Fmemory win. Like int caching, which\nstrings auto-intern is an **implementation detail** — never rely on it for\ncorrectness.\n",{"id":1826,"difficulty":150,"q":1827,"a":1828},"is-coincidental","Why does is sometimes \"work\" coincidentally?","`is` sometimes returns `True` for equal values **only because CPython caches or\ninterns those particular objects** — small ints (−5..256) and many short string\nliterals share one object. It's an accident of optimization, not equality, so it\nbreaks the moment you leave the cached range.\n\n```python\nx = 100\nx is 100         # True  — cached small int (deceiving!)\n\ny = 1000\ny is 1000        # often False — outside the cache\ny == 1000        # True  — the correct comparison\n```\n\nThe danger is that code passes during testing with small values and then fails in\nproduction with larger ones. Treat any `is`-on-a-value that works as a **lucky\ncoincidence**. Rule of thumb: if swapping `is` for `==` would change behavior on\n*some* input, you should have used `==`.\n",{"id":1830,"difficulty":253,"q":1831,"a":1832},"correct-use-of-is","What is the correct use of is?","Use `is` to test **identity against singletons** — objects of which there is\nexactly **one** — most commonly **`None`**, but also `True`, `False`, and your own\n**sentinel** objects. For singletons `is` is correct, fast, and can't be fooled by\na custom `__eq__`.\n\n```python\nif x is None:          # idiomatic, recommended by PEP 8\n    ...\n\n_MISSING = object()    # unique sentinel\ndef get(d, key, default=_MISSING):\n    val = d.get(key, _MISSING)\n    if val is _MISSING:    # distinguishes \"absent\" from \"value is None\"\n        return default\n    return val\n```\n\nA sentinel like `object()` is ideal precisely because `is` checks identity — no\nother object can ever match it. Rule of thumb: `is`\u002F`is not` for `None` and\nsentinels; `==`\u002F`!=` for everything else.\n",{"id":1834,"difficulty":150,"q":1835,"a":1836},"hash-and-identity","How do identity and equality relate to hashing in dicts\u002Fsets?","Dicts\u002Fsets find items by **hash first, then `==`** (not `is`). Two objects that are\n`==` and have equal hashes are treated as the **same key**. Identity only matters as\nan optimization: containers may short-circuit `x is key` before calling `__eq__`.\n\n```python\na = (1, 2)\nb = (1, 2)\na is b              # False — distinct objects\nd = {a: \"x\"}\nd[b]                # 'x' — found by hash + ==, identity irrelevant\n\n# nan is the exception: nan != nan, but `in` checks identity first\nn = float(\"nan\")\nn in [n]            # True — same object short-circuits the == check\n```\n\nRule of thumb: dict\u002Fset membership uses hash + `==`, so equal-and-hashable objects\nare interchangeable keys; the `nan`-in-list quirk comes from the identity shortcut.\n",{"id":1838,"difficulty":162,"q":1839,"a":1840},"mutable-identity-stability","Can an object's `id()` change during its lifetime?","No — `id()` is **guaranteed constant for the object's lifetime**. But once an object\nis garbage-collected, CPython may **reuse that id** for a new object, so comparing\nids of objects with non-overlapping lifetimes is meaningless.\n\n```python\nid(object())        # some address\nid(object())        # MAY be the same! first object was already freed\n\nx = object(); old = id(x)\nid(x) == old        # True — stable while x is alive\n```\n\nRule of thumb: `id` is stable per live object but can be recycled after collection —\nnever store an id to identify an object across its lifetime; keep a real reference.\n",{"id":1842,"difficulty":162,"q":1843,"a":1844},"interning-bool-none","Why are `None`, `True`, and `False` always safe to compare with `is`?","They are **true singletons** — the interpreter guarantees exactly one instance of\neach. So `is None`, `is True`, `is False` are always correct and can't be subverted\nby a custom `__eq__`. PEP 8 mandates `is` for these.\n\n```python\nx is None           # the canonical, recommended check\nx is True           # exact identity (rarely needed; usually just `if x:`)\n\nclass Tricky:\n    def __eq__(self, other): return True\nTricky() == None    # True — misleading!\nTricky() is None    # False — identity can't be fooled\n```\n\nRule of thumb: always use `is`\u002F`is not` with `None` (and the bool singletons); `==`\ncan be overridden and lie, `is` cannot.\n",{"id":1846,"difficulty":150,"q":1847,"a":1848},"tuple-interning-quirk","Why might `a is b` be True for some tuple\u002Fstring literals in the same line?","The **compiler** stores immutable literals in `co_consts` and may **share one object**\nfor identical constants within the same code unit (constant folding\u002Fdeduplication).\nThis makes `is` accidentally `True` — a compile-time artifact, not a guarantee.\n\n```python\na = (1, 2, 3)\nb = (1, 2, 3)\na is b              # may be True at module level (shared constant), False elsewhere\n\nx = \"long string\"; y = \"long string\"\nx is y              # may be True if folded into one constant\n```\n\nRule of thumb: literal sharing depends on compilation context — never rely on `is`\nfor tuples\u002Fstrings; compare with `==`.\n",{"id":1850,"difficulty":162,"q":1851,"a":1852},"intern-performance-use","When is `sys.intern` actually worth using?","Use `sys.intern` when you process **many duplicate strings** and compare them\nrepeatedly — parsers, tokenizers, large dict keys, dedup of column values. Interning\nturns equality into a fast pointer check and saves memory by sharing one copy.\n\n```python\nimport sys\n# intern repeated field names while parsing millions of records:\nkey = sys.intern(raw_key)\nrecord[key] = value\n# later comparisons of interned keys are O(1) identity checks\n```\n\nRule of thumb: intern high-duplication, frequently-compared strings for memory\u002Fspeed\nwins; don't bother for unique or short-lived strings.\n",{"id":1854,"difficulty":150,"q":1855,"a":1856},"equality-reflexivity","Why should a custom `__eq__` usually agree with identity for the same object?","Equality should be **reflexive**: `x == x` must be `True` (except deliberate cases\nlike `nan`). Containers rely on this — they often check `x is key` as a fast path\nbefore `==`, so a broken `__eq__` that fails on itself causes lookups to misbehave.\n\n```python\nclass Bad:\n    def __eq__(self, other): return False   # not reflexive!\n\nb = Bad()\nb == b              # False — violates expectations\nb in [b]            # still True — saved only by the `is` short-circuit\n```\n\nRule of thumb: keep `__eq__` reflexive and consistent with `__hash__`; the identity\nshort-circuit in containers assumes an object equals itself.\n",{"id":1858,"difficulty":162,"q":1859,"a":1860},"copy-and-identity","How do `copy`, slicing, and assignment differ in identity?","Assignment (`b = a`) creates an **alias** (`b is a`). A copy (`a[:]`, `list(a)`,\n`copy.copy`) makes a **new object** (`b is not a`) but shares nested references\n(shallow). `copy.deepcopy` makes new objects all the way down.\n\n```python\nimport copy\na = [[1], [2]]\nb = a              # alias\nc = a[:]           # shallow copy\nd = copy.deepcopy(a)\n\nb is a             # True\nc is a             # False — new outer list\nc[0] is a[0]       # True  — shared inner list (shallow)\nd[0] is a[0]       # False — fully independent\n```\n\nRule of thumb: `=` aliases, shallow copy duplicates the outer object but shares\ninnards, `deepcopy` duplicates everything — choose by how deep your independence must be.\n",{"id":1862,"difficulty":162,"q":1863,"a":1864},"frozen-singletons","What other cached singletons exist besides small ints?","Beyond ints −5..256, CPython caches the **empty tuple `()`**, the **empty string\u002F\nbytes**, single-character latin-1 strings, and shares `None`\u002F`True`\u002F`False`\u002F`...`\n(Ellipsis). These are reused, so `is` may return `True` — again an implementation\ndetail.\n\n```python\n() is ()            # True — single empty-tuple singleton\n\"\" is \"\"            # True — empty string cached\nbool(1) is True     # True — bool singletons\n... is Ellipsis     # True\n\n[] is []            # False — empty lists are NOT cached (mutable)\n```\n\nRule of thumb: immutable empties\u002Fsingletons are often shared (so `is` may pass), but\nmutable empties (`[]`, `{}`) are always fresh — still compare values with `==`.\n",{"id":1866,"difficulty":253,"q":1867,"a":1868},"id-debugging-aliasing","How can `id()` help diagnose an aliasing bug?","Print `id()` of suspected variables to confirm whether two names point at the **same\nobject**. If mutating one unexpectedly changes another, matching ids prove they're\naliases (often from a missing copy).\n\n```python\ndef add_item(item, basket=[]):    # mutable default bug\n    basket.append(item)\n    return basket\n\nr1 = add_item(\"a\")\nr2 = add_item(\"b\")\nid(r1) == id(r2)     # True — same list! reveals the shared-default bug\n```\n\nRule of thumb: equal `id()`s on values you expected to be independent flag an\naliasing\u002Fshared-reference bug — fix by copying or using a fresh object.\n",{"description":148},"Python interview questions on object identity: is vs ==, the id() function, the small-int cache, string interning, and the correct use of is for None and sentinels.","python\u002Finternals\u002Fidentity-interning","Identity, is vs ==, & Interning","KODeTmvF19F4rpGP8eN5dGDznfixDoBYr7JkMeIDpAc",{"id":1875,"title":1876,"body":1877,"description":148,"difficulty":253,"extension":151,"framework":10,"frameworkSlug":8,"meta":1881,"navigation":153,"order":29,"path":1882,"questions":1883,"questionsCount":217,"related":218,"seo":1944,"seoDescription":1945,"stem":1946,"subtopic":1947,"topic":37,"topicSlug":38,"updated":223,"__hash__":1948},"qa\u002Fpython\u002Fiteration\u002Fcomprehensions.md","Comprehensions",{"type":145,"value":1878,"toc":1879},[],{"title":148,"searchDepth":29,"depth":29,"links":1880},[],{},"\u002Fpython\u002Fiteration\u002Fcomprehensions",[1884,1888,1892,1896,1900,1904,1908,1912,1916,1920,1924,1928,1932,1936,1940],{"id":1885,"difficulty":253,"q":1886,"a":1887},"list-comp-syntax","What is a list comprehension and why use one?","A **list comprehension** is a concise expression that builds a list in a\nsingle readable line: `[expression for item in iterable]`. It replaces the\ncommon pattern of creating an empty list and `append`-ing in a `for` loop.\n\n```python\n# the verbose way\nsquares = []\nfor n in range(5):\n    squares.append(n * n)\n\n# the comprehension\nsquares = [n * n for n in range(5)]   # [0, 1, 4, 9, 16]\n```\n\nBeyond brevity, comprehensions are usually **faster** than an equivalent\nloop (the iteration runs in optimized C) and they signal intent: \"I'm\ntransforming a sequence into a new list.\" Reach for one whenever you're\nbuilding a list by mapping or filtering another iterable.\n",{"id":1889,"difficulty":253,"q":1890,"a":1891},"comp-filtering","How do you filter and conditionally transform inside a comprehension?","A trailing **`if` clause filters** items — only elements for which it is\ntruthy are kept. A **conditional expression** (`x if cond else y`) goes at\nthe **front**, before the `for`, because it's part of the output expression,\nnot a filter.\n\n```python\nnums = range(6)\n\nevens = [n for n in nums if n % 2 == 0]          # filter: [0, 2, 4]\nlabels = [\"even\" if n % 2 == 0 else \"odd\"        # transform every item\n          for n in nums]                         # ['even','odd',...]\nboth = [n * 2 for n in nums if n > 2]            # filter THEN transform\n```\n\nRemember the position rule: a **filter `if`** comes after the `for`; a\n**`if\u002Felse` expression** comes before it. Mixing them up is a common\nbeginner error.\n",{"id":1893,"difficulty":162,"q":1894,"a":1895},"nested-comp","How do nested comprehensions work?","You can chain multiple `for` clauses to flatten or iterate over nested\ndata. The clauses read **left to right in the same order** as nested\nloops. You can also nest a comprehension *inside* the output expression to\nbuild a list of lists.\n\n```python\nmatrix = [[1, 2], [3, 4]]\n\nflat = [x for row in matrix for x in row]   # [1, 2, 3, 4]\n# equivalent to:\n#   for row in matrix:\n#       for x in row:\n\ngrid = [[r * c for c in range(3)] for r in range(3)]  # list of rows\n```\n\nThe trap is reading the order backwards — the **outer** loop is written\nfirst. Keep nesting shallow; two levels is usually the readability limit\nbefore a plain loop is clearer.\n",{"id":1897,"difficulty":253,"q":1898,"a":1899},"dict-set-comp","What are dict and set comprehensions?","The same syntax works for dicts and sets. A **dict comprehension** uses\n`{key: value for ...}` and a **set comprehension** uses `{expr for ...}`\n(no colon). Sets automatically deduplicate results.\n\n```python\nnames = [\"ada\", \"grace\", \"ada\"]\n\nlengths = {n: len(n) for n in names}     # {'ada': 3, 'grace': 5}\nunique  = {n for n in names}             # {'ada', 'grace'}  (deduped)\nswapped = {v: k for k, v in lengths.items()}  # invert a dict\n```\n\nNote `{}` alone is an empty **dict**, not a set — use `set()` for an empty\nset. Dict and set comprehensions accept the same `if` filters and nested\n`for` clauses as list comprehensions.\n",{"id":1901,"difficulty":162,"q":1902,"a":1903},"comp-vs-map-filter","When should you NOT use a comprehension?","Comprehensions are for **building a collection**. Avoid them when you only\nwant **side effects** (printing, writing to a DB) — that abuse hides intent\nand builds a throwaway list. Use a plain `for` loop instead. Also skip them\nwhen the logic is so complex that the line becomes unreadable.\n\n```python\n# bad: comprehension purely for side effects\n[print(x) for x in items]      # builds a useless list of Nones\n\n# good: a loop says \"do this for each\"\nfor x in items:\n    print(x)\n```\n\nComprehensions overlap with `map`\u002F`filter`, but are usually more readable\nand avoid `lambda`. Prefer a generator expression `(...)` over a list comp\nwhen you only iterate once and don't need to materialize the whole result.\n",{"id":1905,"difficulty":162,"q":1906,"a":1907},"generator-expression","What is a generator expression and how does it differ from a list comprehension?","A **generator expression** uses parentheses `(...)` and is **lazy** — it yields items\none at a time instead of building the whole list in memory. Ideal for large or\ninfinite data and for feeding aggregate functions.\n\n```python\nsquares_list = [n * n for n in range(1000000)]   # builds a big list\nsquares_gen  = (n * n for n in range(1000000))   # lazy, tiny memory\n\ntotal = sum(n * n for n in range(1000000))       # no intermediate list\nnext(squares_gen)                                 # 0 — pull one at a time\n```\n\nRule of thumb: use a genexp when you only iterate once or pass it to `sum`\u002F`max`\u002F\n`any`; use a list comp when you need indexing, length, or to reuse the result.\n",{"id":1909,"difficulty":162,"q":1910,"a":1911},"comp-scope-leak","Does the loop variable in a comprehension leak into the enclosing scope?","No — in Python 3 a comprehension has its **own scope**, so its loop variable doesn't\nexist afterward and won't clobber an outer variable of the same name. (This was *not*\ntrue for list comps in Python 2.)\n\n```python\nx = \"important\"\nresult = [x for x in range(3)]\nprint(x)        # 'important' — outer x untouched\n\n[i for i in range(3)]\nprint(i)        # NameError — i never leaked\n```\n\nRule of thumb: comprehension variables are private to the comprehension; you can\nsafely reuse names without side effects.\n",{"id":1913,"difficulty":150,"q":1914,"a":1915},"walrus-in-comp","How is the walrus operator useful inside a comprehension?","The **walrus** `:=` lets you compute a value **once**, bind it, and reuse it in both\nthe filter and the output — avoiding a double computation of an expensive call.\n\n```python\ndata = [\" 12 \", \"x\", \" 7 \"]\n\n# without walrus: parse twice or use a helper\nresult = [v for s in data if (v := s.strip()).isdigit()]\n# 'v' is reused: filtered on isdigit AND used as the output\nresult        # ['12', '7']\n```\n\nRule of thumb: use `:=` in a comprehension when you'd otherwise call the same\nexpensive function in both the `if` and the output expression.\n",{"id":1917,"difficulty":162,"q":1918,"a":1919},"conditional-dict-comp","How do you filter or transform values in a dict comprehension?","Dict comprehensions take the same `if` filter (after the `for`) and conditional\nexpressions (in the key or value). This makes inverting, filtering, or remapping a\ndict a one-liner.\n\n```python\nprices = {\"a\": 10, \"b\": 0, \"c\": 5}\n\nin_stock = {k: v for k, v in prices.items() if v > 0}   # drop zero\ncapped   = {k: min(v, 8) for k, v in prices.items()}    # transform values\nflagged  = {k: (\"free\" if v == 0 else v) for k, v in prices.items()}\n```\n\nRule of thumb: filter with a trailing `if`, transform with expressions in the key\u002F\nvalue slots — same rules as list comprehensions.\n",{"id":1921,"difficulty":162,"q":1922,"a":1923},"comp-performance","Why is a comprehension often faster than an equivalent for-loop?","A comprehension runs its iteration and `append` logic in **optimized C** with a\nspecialized bytecode, avoiding the repeated `list.append` **attribute lookup and\nmethod call** a manual loop performs each iteration.\n\n```python\n# slower: attribute lookup + bound-method call every iteration\nout = []\nappend = out.append          # caching append helps, but still Python-level\nfor n in range(1000):\n    append(n * n)\n\n# faster: dedicated LIST_APPEND bytecode, no per-item method lookup\nout = [n * n for n in range(1000)]\n```\n\nRule of thumb: comprehensions win on speed and clarity for building collections; for\nside effects or complex bodies, a plain loop is the right call.\n",{"id":1925,"difficulty":162,"q":1926,"a":1927},"flatten-vs-nested-output","What is the difference between `[x for row in m for x in row]` and `[[...] for ...]`?","Multiple `for` clauses **flatten** (one combined output list); a nested comprehension\nin the **output position** produces a **list of lists**. The placement of brackets\ndecides the shape.\n\n```python\nm = [[1, 2], [3, 4]]\n\nflat   = [x for row in m for x in row]        # [1, 2, 3, 4]\nnested = [[x * 10 for x in row] for row in m]  # [[10, 20], [30, 40]]\n```\n\nRule of thumb: chained `for`s flatten; an inner `[...]` in the expression slot\npreserves structure. Read chained `for`s top-down like nested loops.\n",{"id":1929,"difficulty":162,"q":1930,"a":1931},"dependent-nested-loops","Can a later `for` clause depend on an earlier one in a comprehension?","Yes — each `for` clause can reference variables bound by the clauses **to its left**,\nexactly like nested loops. This lets you generate dependent combinations.\n\n```python\n# upper triangle of pairs — j depends on i\npairs = [(i, j) for i in range(3) for j in range(i + 1, 3)]\n# [(0, 1), (0, 2), (1, 2)]\n\n# filter can also use earlier variables\n[(i, j) for i in range(3) for j in range(3) if i != j]\n```\n\nRule of thumb: inner clauses see outer ones (left-to-right), so you can build\ntriangular ranges, dependent filters, and Cartesian subsets in one expression.\n",{"id":1933,"difficulty":253,"q":1934,"a":1935},"comp-with-function-call","How do you avoid calling an expensive function twice in a filtered comprehension?","Without the walrus operator, factor the work into a **generator step** or helper so\nthe expensive call runs once per item, then filter\u002Ftransform its result. This keeps\nboth correctness and readability.\n\n```python\n# naive: process(x) called twice per item\nresult = [process(x) for x in data if process(x) is not None]\n\n# better: compute once via an inner generator\nprocessed = (process(x) for x in data)\nresult = [p for p in processed if p is not None]\n```\n\nRule of thumb: never repeat an expensive call in both the filter and output — use an\nintermediate generator (or `:=`) to compute it a single time.\n",{"id":1937,"difficulty":253,"q":1938,"a":1939},"empty-and-edge-comp","What does a comprehension return when the source is empty?","It returns an **empty collection of the matching type** — never an error. An empty\niterable simply yields no items, so you get `[]`, `{}`, or `set()`.\n\n```python\n[x * 2 for x in []]            # []\n{k: v for k, v in []}         # {}\n{x for x in ()}               # set()\n[x for x in range(10) if x > 100]   # [] — filter removes everything\n```\n\nRule of thumb: comprehensions degrade gracefully to empty results on empty\u002Ffiltered\ninput — no need to guard against empty sources.\n",{"id":1941,"difficulty":253,"q":1942,"a":1943},"comp-readability-limit","When does a comprehension become too complex and need refactoring?","When it has **multiple `for` clauses plus filters plus a conditional expression** all\non one line, readability collapses. Break it into a loop or a helper function — the\ngoal is clarity, not minimal line count.\n\n```python\n# too dense:\nr = [f(x) if g(x) else h(x) for sub in data for x in sub if x and p(x)]\n\n# clearer:\nr = []\nfor sub in data:\n    for x in sub:\n        if x and p(x):\n            r.append(f(x) if g(x) else h(x))\n```\n\nRule of thumb: if you can't read the comprehension aloud in one breath, expand it to\na loop — comprehensions should simplify, not obfuscate.\n",{"description":148},"Python interview questions on list, dict, and set comprehensions, filtering with if, conditional expressions, nested comprehensions, and when not to use them.","python\u002Fiteration\u002Fcomprehensions","List, Dict & Set Comprehensions","ktksL0ND441tcm_IMYevKA7uevH_P7bOupcYYSv-WHs",{"id":1950,"title":1951,"body":1952,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":1956,"navigation":153,"order":29,"path":1957,"questions":1958,"questionsCount":217,"related":218,"seo":2018,"seoDescription":2019,"stem":2020,"subtopic":2021,"topic":81,"topicSlug":83,"updated":223,"__hash__":2022},"qa\u002Fpython\u002Fmodules\u002Fpackages.md","Packages",{"type":145,"value":1953,"toc":1954},[],{"title":148,"searchDepth":29,"depth":29,"links":1955},[],{},"\u002Fpython\u002Fmodules\u002Fpackages",[1959,1963,1967,1971,1974,1978,1982,1986,1990,1994,1998,2002,2006,2010,2014],{"id":1960,"difficulty":253,"q":1961,"a":1962},"package-vs-module","What is the difference between a module and a package, and what does __init__.py do?","A **module** is a single `.py` file. A **package** is a **directory** of modules\nthat you import as a unit, traditionally marked by an **`__init__.py`** file. That\nfile runs when the package is first imported, so it's where you can set the\npackage's public API or do setup; it's often empty, which is fine.\n\n```python\n# myapp\u002F                 \u003C- package\n#   __init__.py          \u003C- marks it a package, runs on import\n#   db.py                \u003C- module\n#   utils\u002F               \u003C- subpackage\n#       __init__.py\n#       text.py\n\nimport myapp.db                 # import a module from the package\nfrom myapp.utils.text import slug\n```\n\nA common use of `__init__.py` is to **re-export** so callers can write\n`from myapp import db` cleanly. Modules organize code into files; packages organize\nmodules into a namespace tree.\n",{"id":1964,"difficulty":162,"q":1965,"a":1966},"python-m-main","What do `python -m` and __main__.py do?","**`python -m pkg.module`** runs a module **as a script** while still locating it via\nthe import system (so relative imports and the package context work). When you point\n`-m` at a **package**, Python runs that package's **`__main__.py`** — that's how a\npackage becomes executable, like `python -m http.server`.\n\n```bash\npython -m http.server 8000     # runs http.server's __main__.py\npython -m myapp                # runs myapp\u002F__main__.py\npython -m pytest               # run an installed tool as a module\n```\n\n```python\n# myapp\u002F__main__.py\nfrom myapp.cli import main\nmain()\n```\n\nUse `-m` to run installed\u002Fpackaged code by its import name rather than a file path,\nwhich avoids the `sys.path` surprises you get from running a file directly.\n",{"id":1968,"difficulty":162,"q":1969,"a":1970},"dunder-all","What does __all__ control?","`__all__` is a list of names that defines a module's (or package's) **public API for\nwildcard imports** — `from module import *` imports exactly those names. Without it,\n`import *` grabs every name not starting with an underscore.\n\n```python\n# mymath.py\n__all__ = [\"add\", \"PI\"]      # only these are exported by *\n\ndef add(a, b): return a + b\ndef _helper(): ...           # private anyway\nPI = 3.14159\n\n# elsewhere\nfrom mymath import *         # gets add and PI only\n```\n\nIt does **not** prevent explicit imports (`from mymath import _helper` still works)\n— it only curates `*` and documents intent. Define `__all__` to keep `import *`\nclean and to signal what's officially public.\n",{"id":956,"difficulty":150,"q":1972,"a":1973},"What is a namespace package?","A **namespace package** (PEP 420) is a package with **no `__init__.py`** whose\ncontents can be **split across multiple directories** on `sys.path`. Python merges\nthose directories into one logical package — useful for plugins where different\ndistributions contribute to a shared top-level namespace.\n\n```python\n# path1\u002Facme\u002Ffoo.py\n# path2\u002Facme\u002Fbar.py     (no __init__.py in either acme\u002F)\n# with both paths on sys.path:\nimport acme.foo\nimport acme.bar          # both resolve under the merged 'acme' namespace\n```\n\nSince Python 3.3, a directory **without** `__init__.py` can still be importable as a\nnamespace package. For an ordinary single-location package you usually still want a\nregular package (with `__init__.py`); reserve namespace packages for splitting a\nnamespace across separately-installed parts.\n",{"id":1975,"difficulty":162,"q":1976,"a":1977},"script-vs-import","What is the difference between running a file and importing it?","When you **run** a file (`python foo.py`), Python sets its `__name__` to\n**`\"__main__\"`**. When you **import** it, `__name__` is the **module's name**. The\n`if __name__ == \"__main__\":` guard uses this so a file can act as both a runnable\nscript and an importable library — the guarded code runs only on direct execution.\n\n```python\n# greet.py\ndef hello(name): return f\"Hi {name}\"\n\nif __name__ == \"__main__\":   # runs only via `python greet.py`\n    print(hello(\"world\"))    # NOT run when `import greet`\n```\n\nWithout the guard, your script's top-level side effects (running, printing, parsing\nargs) would fire **every time the module is imported**. Always put script entry\npoints behind the `__main__` guard.\n",{"id":1979,"difficulty":162,"q":1980,"a":1981},"import-package-vs-distribution","What's the difference between an import package and a distribution package?","An **import package** is what you `import` in code (`import requests`). A\n**distribution package** is what you `pip install` (the project on PyPI). Their names\nusually match but **don't have to** — one distribution can ship several import\npackages, and the names can differ entirely.\n\n```bash\npip install beautifulsoup4     # distribution name\n```\n```python\nimport bs4                      # import name — different!\npip install scikit-learn       # -> import sklearn\n```\n\nRule of thumb: the name you `pip install` and the name you `import` are separate\nidentifiers; check a project's docs when they don't match.\n",{"id":1983,"difficulty":162,"q":1984,"a":1985},"pyproject-toml","What is `pyproject.toml` and why did it replace `setup.py`?","`pyproject.toml` is the **standardized, declarative** project config (PEP 517\u002F518):\nit declares the build backend, dependencies, and metadata in one file. It replaced\nexecutable `setup.py` scripts, making builds reproducible and tool-agnostic.\n\n```toml\n[build-system]\nrequires = [\"hatchling\"]\nbuild-backend = \"hatchling.build\"\n\n[project]\nname = \"mypkg\"\nversion = \"1.0.0\"\ndependencies = [\"requests>=2.0\"]\n```\n\nRule of thumb: new projects use `pyproject.toml` with a build backend (hatchling,\nsetuptools, flit); `setup.py` is legacy and runs arbitrary code at build time.\n",{"id":1987,"difficulty":162,"q":1988,"a":1989},"editable-install","What does `pip install -e .` (editable install) do?","An **editable install** links your project into the environment **in place** instead\nof copying it, so code edits take effect **without reinstalling**. It's the standard\nway to develop a package locally while importing it like an installed one.\n\n```bash\npip install -e .          # install current project in editable mode\n# edit source files -> changes are picked up immediately on next import\n```\n\nRule of thumb: use `pip install -e .` during development so your working tree is the\ninstalled package; use a regular install for deployment.\n",{"id":1991,"difficulty":150,"q":1992,"a":1993},"entry-points-console-scripts","How do you expose a command-line tool from a package?","Declare a **console script entry point** in `pyproject.toml`. On install, pip\ngenerates an executable that calls your function — turning a package into a CLI\ncommand available on `PATH`.\n\n```toml\n[project.scripts]\nmytool = \"mypkg.cli:main\"     # creates a `mytool` command -> calls main()\n```\n```python\n# mypkg\u002Fcli.py\ndef main():\n    print(\"hello from mytool\")\n```\n\nRule of thumb: use `[project.scripts]` entry points (not shebang hacks) to ship CLI\ncommands; pip creates the wrapper executable for the user's platform.\n",{"id":1995,"difficulty":162,"q":1996,"a":1997},"package-data","How do you access non-code files bundled in a package?","Use **`importlib.resources`** rather than building paths from `__file__` — it works\neven when the package is zipped or installed oddly. Declare the data files in your\nbuild config so they're included in the distribution.\n\n```python\nfrom importlib.resources import files\n\n# read mypkg\u002Fdata\u002Fconfig.json shipped inside the package:\ntext = files(\"mypkg.data\").joinpath(\"config.json\").read_text()\n```\n\nRule of thumb: load bundled resources with `importlib.resources`, not\n`open(os.path.join(os.path.dirname(__file__), ...))`, which breaks for zipped\u002Fegg\ninstalls.\n",{"id":1999,"difficulty":162,"q":2000,"a":2001},"relative-import-in-package","When do relative imports work and when do they fail?","Relative imports (`from . import x`, `from ..pkg import y`) only work when the module\nis part of a **package with a known parent** — i.e. imported or run via `python -m`.\nRunning the file directly (`python sub\u002Fmod.py`) makes it `__main__` with no package,\nso they raise `ImportError`.\n\n```python\n# mypkg\u002Fsub\u002Fmod.py\nfrom ..utils import helper      # works via `python -m mypkg.sub.mod`\n                                 # fails via `python mypkg\u002Fsub\u002Fmod.py`\n```\n\nRule of thumb: relative imports need package context — run package code with\n`python -m pkg.module`, not by file path, or use absolute imports.\n",{"id":2003,"difficulty":253,"q":2004,"a":2005},"subpackage-structure","How do nested subpackages and their imports work?","A **subpackage** is a package inside a package — each level is a directory (with\n`__init__.py` for regular packages). You import down the tree with dotted paths, and\neach `__init__.py` runs as its level is first imported.\n\n```text\nmyapp\u002F\n  __init__.py\n  api\u002F\n    __init__.py\n    routes.py\n```\n```python\nfrom myapp.api.routes import handler\nimport myapp.api.routes          # imports myapp, then myapp.api, then routes\n```\n\nRule of thumb: importing `a.b.c` initializes every package along the path (`a`, then\n`a.b`, then `a.b.c`) in order, top down.\n",{"id":2007,"difficulty":150,"q":2008,"a":2009},"module-level-getattr","What does a module-level `__getattr__` enable?","Defining **`__getattr__(name)`** at module level (PEP 562) intercepts access to\n**missing** module attributes — used for **lazy imports**, deprecation warnings, or\ncomputed attributes, without loading everything at import time.\n\n```python\n# mypkg\u002F__init__.py\ndef __getattr__(name):\n    if name == \"heavy\":\n        import mypkg._heavy as h    # load only when accessed\n        return h\n    raise AttributeError(name)\n\n# mypkg.heavy triggers the lazy load on first access\n```\n\nRule of thumb: module `__getattr__` powers lazy submodule loading and deprecation\nshims — it runs only for names not already defined in the module.\n",{"id":2011,"difficulty":162,"q":2012,"a":2013},"init-reexport-api","How do you use `__init__.py` to flatten a package's public API?","Re-export key names in `__init__.py` so users import from the **package root**\ninstead of deep module paths. Pair with `__all__` to define the official surface.\n\n```python\n# mypkg\u002F__init__.py\nfrom .client import Client\nfrom .errors import ApiError\n__all__ = [\"Client\", \"ApiError\"]\n\n# users write:\nfrom mypkg import Client        # not mypkg.client.Client\n```\n\nRule of thumb: curate the top-level API via `__init__.py` re-exports so internal\nmodule layout can change without breaking users' imports.\n",{"id":2015,"difficulty":150,"q":2016,"a":2017},"circular-import-package","How does package structure contribute to circular imports?","Heavy `__init__.py` re-exports can create cycles: importing the package triggers\n`__init__.py`, which imports submodules that import the package back. The fix is to\ndefer or restructure imports, or keep `__init__.py` light.\n\n```python\n# mypkg\u002F__init__.py\nfrom .a import A      # imports a.py\n# mypkg\u002Fa.py\nfrom mypkg import B   # cycle! mypkg's __init__ isn't finished yet\n```\n\nRule of thumb: avoid importing the package from its own submodules; import siblings\ndirectly (`from mypkg.b import B`) or defer the import inside a function.\n",{"description":148},"Python interview questions on packages vs modules, the role of __init__.py, python -m and __main__.py, __all__, namespace packages, and running a script versus importing it.","python\u002Fmodules\u002Fpackages","Packages & __main__","WQWCC4eBzMeHrKFYmka-gM8YF5LKIacI-GKkPnKiHTk",{"id":2024,"title":2025,"body":2026,"description":148,"difficulty":253,"extension":151,"framework":10,"frameworkSlug":8,"meta":2030,"navigation":153,"order":29,"path":2031,"questions":2032,"questionsCount":217,"related":218,"seo":2093,"seoDescription":2094,"stem":2095,"subtopic":2096,"topic":54,"topicSlug":56,"updated":223,"__hash__":2097},"qa\u002Fpython\u002Foop\u002Fclasses.md","Classes",{"type":145,"value":2027,"toc":2028},[],{"title":148,"searchDepth":29,"depth":29,"links":2029},[],{},"\u002Fpython\u002Foop\u002Fclasses",[2033,2037,2041,2045,2049,2053,2057,2061,2065,2069,2073,2077,2081,2085,2089],{"id":2034,"difficulty":253,"q":2035,"a":2036},"class-vs-instance","What is the difference between a class and an instance?","A **class** is a **blueprint** — it defines the attributes and methods that\nobjects of that type will have. An **instance** is a concrete **object built\nfrom that blueprint**, with its own state. You write the class once and create\nmany instances from it.\n\n```python\nclass Dog:                 # the blueprint\n    def __init__(self, name):\n        self.name = name   # per-instance state\n\nrex = Dog(\"Rex\")           # an instance\nfido = Dog(\"Fido\")         # a separate instance\nrex.name                   # 'Rex'\nfido.name                  # 'Fido' — independent state\ntype(rex)                  # \u003Cclass '__main__.Dog'>\n```\n\nThe class itself is also an object (of type `type`). Each instance carries its\nown data but shares the class's methods. Think of the class as the cookie\ncutter and the instances as the cookies.\n",{"id":2038,"difficulty":162,"q":2039,"a":2040},"init-vs-new","What is the difference between __init__ and __new__?","`__new__` **creates and returns the new object**; `__init__` **initializes**\nthat already-created object. `__new__` runs first and is a static method that\nreceives the **class**; `__init__` runs second and receives the **instance**\n(`self`) it should configure. `__init__` must return `None`.\n\n```python\nclass Widget:\n    def __new__(cls, *args):\n        print(\"__new__ — allocating\")\n        return super().__new__(cls)   # returns the instance\n    def __init__(self, size):\n        print(\"__init__ — configuring\")\n        self.size = size              # sets state on self\n\nw = Widget(10)   # prints __new__ then __init__\n```\n\nYou rarely override `__new__` — it's mainly for **immutable types** (subclassing\n`int`\u002F`str`\u002F`tuple`), singletons, or metaclass tricks. For everyday classes,\njust use `__init__`.\n",{"id":2042,"difficulty":253,"q":2043,"a":2044},"what-is-self","What is `self` in Python?","`self` is the **instance the method was called on** — it's how a method accesses\nthat object's attributes and other methods. It isn't a keyword; it's just the\nconventional **name of the first parameter**. Python passes the instance\nautomatically when you call `obj.method()`.\n\n```python\nclass Counter:\n    def __init__(self):\n        self.count = 0\n    def increment(self):\n        self.count += 1        # self refers to this instance\n\nc = Counter()\nc.increment()                  # Python passes c as self\nCounter.increment(c)           # exactly equivalent — self is explicit here\n```\n\nSo `c.increment()` is sugar for `Counter.increment(c)`. The explicitness is\ndeliberate — Python makes the instance visible rather than hiding it like\n`this` in other languages.\n",{"id":2046,"difficulty":162,"q":2047,"a":2048},"instance-vs-class-attributes","What is the difference between instance and class attributes?","A **class attribute** is defined in the class body and **shared by every\ninstance**; an **instance attribute** is set on `self` (usually in `__init__`)\nand is **unique per object**. Attribute lookup checks the instance first, then\nfalls back to the class.\n\n```python\nclass Dog:\n    species = \"Canis familiaris\"   # class attribute — shared\n    def __init__(self, name):\n        self.name = name           # instance attribute — per-object\n\na, b = Dog(\"Rex\"), Dog(\"Fido\")\na.species                # 'Canis familiaris' (from the class)\na.name, b.name           # 'Rex', 'Fido' (independent)\na.species = \"wolf\"       # creates an instance attr that SHADOWS the class one\nb.species                # still 'Canis familiaris'\n```\n\nWatch the classic trap: a **mutable** class attribute (like `[]`) is shared and\nwill leak state between instances — initialize mutable state in `__init__`.\n",{"id":2050,"difficulty":162,"q":2051,"a":2052},"repr-vs-str","What is the difference between __repr__ and __str__?","`__repr__` is the **unambiguous, developer-facing** representation — ideally\nsomething that could recreate the object — and is what you see in the REPL and\nin containers. `__str__` is the **readable, user-facing** string used by\n`print()` and `str()`. If `__str__` is missing, Python **falls back to\n`__repr__`**.\n\n```python\nclass Point:\n    def __init__(self, x, y):\n        self.x, self.y = x, y\n    def __repr__(self):\n        return f\"Point(x={self.x}, y={self.y})\"   # for developers\n    def __str__(self):\n        return f\"({self.x}, {self.y})\"            # for users\n\np = Point(1, 2)\nprint(p)     # (1, 2)        — __str__\nrepr(p)      # 'Point(x=1, y=2)'  — __repr__\n[p]          # [Point(x=1, y=2)]  — containers use __repr__\n```\n\nRule of thumb: always define `__repr__`; add `__str__` only when you need a\ndistinct friendly form.\n",{"id":2054,"difficulty":162,"q":2055,"a":2056},"object-creation-flow","What happens, step by step, when you call ClassName()?","Calling `ClassName(args)` invokes the class's metaclass `__call__`, which\norchestrates two steps: it calls **`__new__(cls, args)`** to allocate the\nobject, then — if `__new__` returned an instance of `cls` — calls\n**`__init__(instance, args)`** to initialize it, and finally returns the\ninstance.\n\n```python\nclass Demo:\n    def __new__(cls, *a):\n        print(\"1. __new__\")\n        return super().__new__(cls)\n    def __init__(self, *a):\n        print(\"2. __init__\")\n\nd = Demo()       # prints: 1. __new__  then  2. __init__\n# 3. d is now bound to the fully initialized instance\n```\n\nKey subtlety: if `__new__` returns an object that is **not** an instance of the\nclass, `__init__` is **skipped entirely**. For normal classes you never see\nthis machinery — you just call the class and get back a ready object.\n",{"id":2058,"difficulty":162,"q":2059,"a":2060},"instance-dict","Where does an instance store its attributes?","By default each instance keeps its attributes in a per-object dictionary,\n**`__dict__`**. Setting `self.x = 1` writes into that dict; this is why you can add\nattributes dynamically at runtime.\n\n```python\nclass P:\n    def __init__(self):\n        self.x = 1\np = P()\np.__dict__            # {'x': 1}\np.y = 2               # dynamically add an attribute\np.__dict__            # {'x': 1, 'y': 2}\nvars(p)               # same as p.__dict__\n```\n\nRule of thumb: instance attributes live in `__dict__` — flexible, but it costs\nmemory; use `__slots__` to remove it when you have many small objects.\n",{"id":2062,"difficulty":162,"q":2063,"a":2064},"class-method-vs-static-method","What is the difference between `@classmethod` and `@staticmethod`?","A **`@classmethod`** receives the **class** as its first argument (`cls`) — ideal for\nalternative constructors. A **`@staticmethod`** receives **nothing automatic** — it's\na plain function grouped under the class for namespacing.\n\n```python\nclass Date:\n    def __init__(self, y, m, d):\n        self.y, self.m, self.d = y, m, d\n    @classmethod\n    def from_string(cls, s):           # alt constructor\n        return cls(*map(int, s.split(\"-\")))\n    @staticmethod\n    def is_leap(year):                  # utility, no self\u002Fcls\n        return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)\n\nDate.from_string(\"2026-06-19\")\nDate.is_leap(2024)                      # True\n```\n\nRule of thumb: use `classmethod` when you need `cls` (factories, subclass-aware);\n`staticmethod` for related helpers that touch neither instance nor class.\n",{"id":2066,"difficulty":162,"q":2067,"a":2068},"dynamic-attributes","How do `getattr`, `setattr`, and `hasattr` work?","These built-ins access attributes **by name string** at runtime: `getattr(obj,\n\"x\")` reads (with an optional default), `setattr(obj, \"x\", v)` writes, `hasattr`\ntests existence. Useful for dynamic\u002Fconfig-driven code.\n\n```python\nclass C: pass\nc = C()\nsetattr(c, \"speed\", 5)        # c.speed = 5\ngetattr(c, \"speed\")           # 5\ngetattr(c, \"missing\", 0)      # 0 — default avoids AttributeError\nhasattr(c, \"speed\")           # True\ndelattr(c, \"speed\")           # remove it\n```\n\nRule of thumb: reach for `getattr`\u002F`setattr` when the attribute name is computed or\ndata-driven; otherwise use plain dot access.\n",{"id":2070,"difficulty":253,"q":2071,"a":2072},"init-no-return","Can `__init__` return a value?","No — `__init__` must return **`None`**. Returning anything else raises `TypeError`.\nIts job is to mutate `self` in place, not to produce the object (that's `__new__`'s\nrole).\n\n```python\nclass Bad:\n    def __init__(self):\n        return 42        # TypeError: __init__ should return None\n\nclass Good:\n    def __init__(self, x):\n        self.x = x       # configure self, return nothing\n```\n\nRule of thumb: `__init__` sets up state on `self` and implicitly returns `None`; if\nyou need to control what object comes back, override `__new__`.\n",{"id":2074,"difficulty":150,"q":2075,"a":2076},"equality-and-hash","What happens to hashing when you define `__eq__`?","Defining **`__eq__`** sets `__hash__` to `None`, making instances **unhashable**\n(can't go in sets\u002Fdict keys) unless you also define `__hash__`. Python does this\nbecause equal objects must have equal hashes.\n\n```python\nclass Point:\n    def __init__(self, x, y):\n        self.x, self.y = x, y\n    def __eq__(self, other):\n        return (self.x, self.y) == (other.x, other.y)\n    def __hash__(self):\n        return hash((self.x, self.y))   # restore hashability, consistent with __eq__\n\n{Point(1, 2)}        # works only because __hash__ is defined\n```\n\nRule of thumb: if you implement `__eq__` and need the object hashable, implement a\nconsistent `__hash__` over the same fields — or use `@dataclass(frozen=True)`.\n",{"id":2078,"difficulty":162,"q":2079,"a":2080},"name-mangling","What does a double-underscore prefix like `__name` do?","A leading **double underscore** (no trailing) triggers **name mangling**: inside\nclass `C`, `self.__x` becomes `self._C__x`. It's not true privacy but avoids\naccidental clashes in subclasses.\n\n```python\nclass Base:\n    def __init__(self):\n        self.__secret = 1        # stored as _Base__secret\nb = Base()\nb.__secret                       # AttributeError\nb._Base__secret                  # 1 — accessible if you know the mangled name\n```\n\nRule of thumb: use single `_name` for \"internal, please don't touch\"; reserve `__name`\nmangling for attributes you must protect from subclass name collisions.\n",{"id":2082,"difficulty":162,"q":2083,"a":2084},"bound-vs-unbound-methods","What is a bound method versus accessing a function on the class?","Accessing a method **through an instance** gives a **bound method** — the instance is\npre-bound as `self`. Accessing it **through the class** gives the plain function, so\nyou must pass the instance explicitly.\n\n```python\nclass C:\n    def greet(self): return \"hi\"\n\nc = C()\nc.greet            # \u003Cbound method C.greet of \u003CC object>>\nc.greet()          # \"hi\" — self is c automatically\nC.greet            # \u003Cfunction C.greet> — plain function\nC.greet(c)         # \"hi\" — pass self manually\nm = c.greet; m()   # \"hi\" — bound method remembers c\n```\n\nRule of thumb: `instance.method` captures the instance (a bound method you can store\nand call later); `Class.method` is just the function.\n",{"id":2086,"difficulty":150,"q":2087,"a":2088},"class-as-object","In what sense is a class itself an object?","A class is an **instance of its metaclass** (normally `type`). So classes are\nfirst-class objects: you can assign them to variables, pass them to functions, store\nthem in lists, and even create them at runtime with `type(name, bases, dict)`.\n\n```python\nclass A: pass\ntype(A)            # \u003Cclass 'type'> — A is an instance of type\nisinstance(A, object)   # True\n\nregistry = {\"a\": A}      # store classes like any value\nDynamic = type(\"Dynamic\", (), {\"x\": 1})   # build a class at runtime\nDynamic().x              # 1\n```\n\nRule of thumb: classes are objects produced by `type`; treating them as values\nenables factories, registries, and metaclass-based frameworks.\n",{"id":2090,"difficulty":150,"q":2091,"a":2092},"del-and-finalizer","What does `__del__` do and why is it unreliable?","`__del__` is a **finalizer** called when an object is about to be garbage-collected —\nnot a deterministic destructor. Its timing is unpredictable (especially with\nreference cycles), exceptions in it are ignored, and it may not run at all at\ninterpreter exit.\n\n```python\nclass Resource:\n    def __del__(self):\n        print(\"cleanup\")     # runs whenever GC decides — maybe never\n\nr = Resource(); del r        # may or may not print immediately\n```\n\nRule of thumb: don't rely on `__del__` for cleanup — use context managers\n(`__enter__`\u002F`__exit__`) or explicit `close()`; reach for `__del__` only as a\nlast-resort safety net.\n",{"description":148},"Python interview questions on classes vs instances, __init__ vs __new__, the self parameter, instance vs class attributes, __repr__ vs __str__, and the object creation flow.","python\u002Foop\u002Fclasses","Classes, Instances & __init__","L59T-GGMw4jqtwQInSzW3-O6ZaIu1hELBPX3xp_APF4",{"id":2099,"title":2100,"body":2101,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":2105,"navigation":153,"order":29,"path":2106,"questions":2107,"questionsCount":217,"related":218,"seo":2168,"seoDescription":2169,"stem":2170,"subtopic":2171,"topic":117,"topicSlug":119,"updated":223,"__hash__":2172},"qa\u002Fpython\u002Fstdlib\u002Ffiles-pathlib.md","Files Pathlib",{"type":145,"value":2102,"toc":2103},[],{"title":148,"searchDepth":29,"depth":29,"links":2104},[],{},"\u002Fpython\u002Fstdlib\u002Ffiles-pathlib",[2108,2112,2116,2120,2124,2128,2132,2136,2140,2144,2148,2152,2156,2160,2164],{"id":2109,"difficulty":253,"q":2110,"a":2111},"with-open","Why open files with the `with` statement?","`with open(...)` uses the file as a **context manager**, which **guarantees the file\nis closed** when the block exits — even if an exception is raised. Without it you\nmust remember to call `.close()` manually, and a crash mid-block leaks the handle.\n\n```python\nwith open(\"data.txt\") as f:      # f.close() is automatic\n    contents = f.read()\n# file is closed here, even on error\n\nf = open(\"data.txt\")             # manual style — fragile\ntry:\n    contents = f.read()\nfinally:\n    f.close()\n```\n\nLeaked handles can exhaust OS file descriptors and leave buffered writes unflushed.\nAlways prefer `with` for any resource that needs cleanup (files, locks, sockets).\n",{"id":2113,"difficulty":162,"q":2114,"a":2115},"lazy-iteration","What is the difference between iterating a file and read()\u002Freadlines()?","A file object is its own **iterator**, yielding **one line at a time** and holding\nonly that line in memory. `read()` loads the **entire file** into a single string,\nand `readlines()` loads **all lines into a list** — both can blow up memory on large\nfiles.\n\n```python\nwith open(\"huge.log\") as f:\n    for line in f:               # lazy — one line in memory at a time\n        process(line)\n\nwith open(\"huge.log\") as f:\n    data = f.read()              # whole file as one string\n    lines = f.readlines()        # whole file as a list of strings\n```\n\nIterating is the idiomatic, memory-safe way to process big files line by line.\nReserve `read()`\u002F`readlines()` for small files where you genuinely need the whole\ncontent at once.\n",{"id":2117,"difficulty":162,"q":2118,"a":2119},"text-binary-mode","What is the difference between text and binary mode, and why specify encoding?","**Text mode** (`\"r\"`, the default) decodes bytes into `str` using an **encoding** and\nnormalizes newlines. **Binary mode** (`\"rb\"`\u002F`\"wb\"`) reads and writes raw `bytes`\nwith no decoding — required for images, archives, or any non-text data. In text\nmode you should pass **`encoding=`** explicitly, because the default is\nplatform-dependent.\n\n```python\nwith open(\"notes.txt\", \"r\", encoding=\"utf-8\") as f:\n    text: str = f.read()         # decoded to str\n\nwith open(\"photo.jpg\", \"rb\") as f:\n    raw: bytes = f.read()        # raw bytes, no decoding\n```\n\nRelying on the default encoding is a classic cross-platform bug (UTF-8 on Linux\u002FMac,\noften a legacy codepage on Windows). Always specify `encoding=\"utf-8\"` for text, and\nuse binary mode for everything that isn't text.\n",{"id":2121,"difficulty":162,"q":2122,"a":2123},"pathlib-vs-os-path","What is pathlib.Path and how does it compare to os.path?","**`pathlib.Path`** is the modern, object-oriented way to handle filesystem paths. A\n`Path` is an object with **methods and operators**, whereas the older **`os.path`**\nmodule is a collection of **string-based functions**. Path's `\u002F` operator joins\nsegments cleanly and works across operating systems.\n\n```python\nfrom pathlib import Path\n\np = Path(\"data\") \u002F \"logs\" \u002F \"app.log\"   # join with \u002F\np.exists()\np.suffix                                 # \".log\"\np.stem                                   # \"app\"\np.read_text(encoding=\"utf-8\")            # one-liner read\n\nimport os.path\nold = os.path.join(\"data\", \"logs\", \"app.log\")\nos.path.exists(old)\n```\n\n`pathlib` is generally preferred for new code: it's more readable and bundles\ncommon operations (`read_text`, `mkdir`, `glob`) as methods. Use `os.path` mainly\nwhen working with existing string-based APIs.\n",{"id":2125,"difficulty":162,"q":2126,"a":2127},"globbing-path-methods","How do you find files with globbing using pathlib?","Use **`Path.glob(pattern)`** for matches in one directory and **`Path.rglob(pattern)`**\n(or `glob(\"**\u002F...\")`) to recurse into subdirectories. Both return a **lazy generator**\nof `Path` objects, where `*` matches any characters and `**` matches directories\nrecursively.\n\n```python\nfrom pathlib import Path\n\nroot = Path(\"project\")\nfor py in root.glob(\"*.py\"):         # top level only\n    print(py.name)\n\nfor py in root.rglob(\"*.py\"):        # all subdirectories too\n    print(py)\n\nroot.mkdir(parents=True, exist_ok=True)  # create dirs safely\n[p.name for p in root.iterdir()]         # list directory contents\n```\n\nOther handy `Path` methods: `iterdir()` (list a directory), `is_file()`\u002F`is_dir()`,\n`mkdir()`, `unlink()` (delete), and `with_suffix()`. Globbing returns generators, so\nwrap in `list(...)` if you need a concrete collection.\n",{"id":2129,"difficulty":253,"q":2130,"a":2131},"file-modes","What do the file modes `r`, `w`, `a`, `x`, and `+` mean?","**`r`** read (default, must exist), **`w`** write (truncates\u002Fcreates), **`a`** append\n(writes at end), **`x`** exclusive create (fails if exists), and **`+`** adds the\nother capability (read+write). Add `b` for binary.\n\n```python\nopen(\"f\", \"r\")     # read existing\nopen(\"f\", \"w\")     # overwrite\u002Fcreate — WIPES existing content!\nopen(\"f\", \"a\")     # append to end, create if missing\nopen(\"f\", \"x\")     # create new, FileExistsError if it exists\nopen(\"f\", \"r+\")    # read and write, must exist\n```\n\nRule of thumb: `w` destroys existing content — use `a` to append or `x` to avoid\nclobbering; combine with `b` (`\"rb\"`, `\"wb\"`) for binary data.\n",{"id":2133,"difficulty":253,"q":2134,"a":2135},"path-properties","What are the key parts of a `Path` (name, stem, suffix, parent)?","A `Path` exposes its components as properties: **`.name`** (file + ext), **`.stem`**\n(name without ext), **`.suffix`** (extension), **`.parent`** (containing dir), and\n**`.parts`** (tuple of all segments).\n\n```python\nfrom pathlib import Path\np = Path(\"\u002Fhome\u002Fuser\u002Freport.tar.gz\")\np.name        # 'report.tar.gz'\np.stem        # 'report.tar'\np.suffix      # '.gz'\np.suffixes    # ['.tar', '.gz']\np.parent      # Path('\u002Fhome\u002Fuser')\np.parts       # ('\u002F', 'home', 'user', 'report.tar.gz')\n```\n\nRule of thumb: use these properties instead of string splitting — `.with_suffix()`\u002F\n`.with_name()` build modified paths safely across platforms.\n",{"id":2137,"difficulty":162,"q":2138,"a":2139},"relative-absolute-paths","How do you resolve, and work with relative vs absolute paths?","`Path.resolve()` returns an **absolute, symlink-resolved** path; `.absolute()` makes\nit absolute without resolving symlinks; `.relative_to(base)` computes a relative\npath. `Path.cwd()` and `Path.home()` give common roots.\n\n```python\nfrom pathlib import Path\np = Path(\"data\u002Ffile.txt\")\np.resolve()                       # \u002Ffull\u002Fabs\u002Fpath\u002Fdata\u002Ffile.txt\nPath.cwd()                        # current working directory\nPath.home()                       # user home\n\nabs_p = Path(\"\u002Fa\u002Fb\u002Fc.txt\")\nabs_p.relative_to(\"\u002Fa\")           # Path('b\u002Fc.txt')\n```\n\nRule of thumb: `resolve()` to normalize to a canonical absolute path; `relative_to`\nto express one path relative to a known base (raises if not a subpath).\n",{"id":2141,"difficulty":253,"q":2142,"a":2143},"reading-writing-helpers","What are `Path.read_text`, `write_text`, and their bytes variants?","`Path.read_text()`\u002F`write_text()` and `read_bytes()`\u002F`write_bytes()` are **one-line**\nopen-read\u002Fwrite-close helpers — no `with` block needed for simple whole-file I\u002FO.\nPass `encoding=` for text.\n\n```python\nfrom pathlib import Path\np = Path(\"notes.txt\")\np.write_text(\"hello\", encoding=\"utf-8\")   # opens, writes, closes\ncontent = p.read_text(encoding=\"utf-8\")\n\nPath(\"img.bin\").write_bytes(b\"\\x00\\x01\")\ndata = Path(\"img.bin\").read_bytes()\n```\n\nRule of thumb: use `read_text`\u002F`write_text` for quick whole-file access; switch to\n`with open(...)` when you need streaming, appending, or line-by-line processing.\n",{"id":2145,"difficulty":162,"q":2146,"a":2147},"os-vs-pathlib-operations","How do common `os`\u002F`shutil` operations map to `pathlib`?","Many `os` functions have `Path` method equivalents: `os.mkdir`→`p.mkdir()`,\n`os.remove`→`p.unlink()`, `os.rename`→`p.rename()`, `os.listdir`→`p.iterdir()`,\n`os.path.exists`→`p.exists()`. Directory trees still use **`shutil`**.\n\n```python\nfrom pathlib import Path\nimport shutil\n\np = Path(\"out\")\np.mkdir(parents=True, exist_ok=True)\n(p \u002F \"a.txt\").touch()             # create empty file\n(p \u002F \"a.txt\").unlink()            # delete a file\nshutil.rmtree(p)                  # remove a whole directory tree\nshutil.copy(\"src.txt\", \"dst.txt\") # copy a file\n```\n\nRule of thumb: prefer `Path` methods for single files\u002Fdirs; use `shutil` for\nrecursive copy\u002Fmove\u002Fdelete of trees (pathlib has no rmtree).\n",{"id":2149,"difficulty":162,"q":2150,"a":2151},"tempfile-usage","How do you safely create temporary files and directories?","Use the **`tempfile`** module: `TemporaryDirectory` and `NamedTemporaryFile` are\ncontext managers that **auto-clean up** on exit, and they create files securely\n(avoiding race conditions of hand-rolled temp names).\n\n```python\nimport tempfile\nfrom pathlib import Path\n\nwith tempfile.TemporaryDirectory() as tmp:\n    path = Path(tmp) \u002F \"work.txt\"\n    path.write_text(\"scratch\")\n# directory and contents removed automatically here\n\nwith tempfile.NamedTemporaryFile(mode=\"w\", suffix=\".csv\", delete=True) as f:\n    f.write(\"a,b\\n\")\n```\n\nRule of thumb: never build temp paths by hand — `tempfile` gives secure, auto-cleaned\ntemporary files\u002Fdirs via context managers.\n",{"id":2153,"difficulty":162,"q":2154,"a":2155},"file-seek-tell","What do `seek()` and `tell()` do on a file object?","`tell()` returns the current **byte position**; `seek(offset, whence)` moves it.\n`whence` is 0=start (default), 1=current, 2=end. Useful for re-reading, skipping\nheaders, or random access in binary files.\n\n```python\nwith open(\"data.bin\", \"rb\") as f:\n    f.read(4)          # read header\n    pos = f.tell()     # current position (4)\n    f.seek(0)          # back to start\n    f.seek(-10, 2)     # 10 bytes before end\n```\n\nRule of thumb: use `seek`\u002F`tell` for random access (mostly binary mode); in text mode\nonly seek to positions returned by `tell()` (byte offsets aren't char counts).\n",{"id":2157,"difficulty":162,"q":2158,"a":2159},"glob-vs-pattern-syntax","What's the difference between glob wildcards `*`, `**`, `?`, and `[...]`?","`*` matches any run of characters within one path segment; `**` matches across\ndirectories (recursive); `?` matches a single character; `[...]` matches a character\nset. `**` needs `rglob` or `glob(\"**\u002F...\")`.\n\n```python\nfrom pathlib import Path\nroot = Path(\"src\")\nlist(root.glob(\"*.py\"))        # .py in src only\nlist(root.glob(\"**\u002F*.py\"))     # .py at any depth\nlist(root.glob(\"test_?.py\"))   # test_1.py, test_a.py\nlist(root.glob(\"[abc]*.txt\"))  # starts with a, b, or c\n```\n\nRule of thumb: `*` within a directory, `**` for recursion, `?`\u002F`[...]` for\nsingle-char and set matches — same patterns the shell uses.\n",{"id":2161,"difficulty":150,"q":2162,"a":2163},"file-buffering-flush","Why might written data not appear in a file immediately?","File writes are **buffered** — data sits in memory until the buffer fills, you call\n**`flush()`**, or the file is **closed**. A crash before flush\u002Fclose loses buffered\ndata. `with` guarantees a close (and flush).\n\n```python\nf = open(\"log.txt\", \"w\")\nf.write(\"important\")     # may still be in the buffer, not on disk\nf.flush()                # force it to the OS now\nimport os\nos.fsync(f.fileno())     # force OS to write to physical disk\nf.close()                # flushes and closes\n```\n\nRule of thumb: rely on `with` (or `close()`) to flush; call `flush()`\u002F`os.fsync()`\nexplicitly only when you need durability before the block ends.\n",{"id":2165,"difficulty":162,"q":2166,"a":2167},"encoding-errors-handling","How do you handle decoding errors when reading text files?","Pass an **`errors=`** strategy to `open`\u002F`read_text`: `\"strict\"` (default, raises),\n`\"ignore\"` (drop bad bytes), `\"replace\"` (insert �), or `\"backslashreplace\"`. Useful\nfor messy real-world data with unknown encodings.\n\n```python\nopen(\"messy.txt\", encoding=\"utf-8\", errors=\"replace\").read()   # bad bytes -> \nopen(\"messy.txt\", encoding=\"utf-8\", errors=\"ignore\").read()    # drop bad bytes\n\nfrom pathlib import Path\nPath(\"messy.txt\").read_text(encoding=\"utf-8\", errors=\"replace\")\n```\n\nRule of thumb: keep `errors=\"strict\"` to catch encoding problems; use `replace`\u002F\n`ignore` only when you must tolerate malformed input and can accept data loss.\n",{"description":148},"Python interview questions on open() and the with statement, lazy file iteration, text vs binary mode and encoding, pathlib.Path vs os.path, and globbing.","python\u002Fstdlib\u002Ffiles-pathlib","Files, pathlib & os","RO-DmYGSm-vEq4UzqgIUx1p-9US94YC1MGQeW0owXUg",{"id":2174,"title":2175,"body":2176,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":2180,"navigation":153,"order":29,"path":2181,"questions":2182,"questionsCount":217,"related":218,"seo":2243,"seoDescription":2244,"stem":2245,"subtopic":2246,"topic":126,"topicSlug":128,"updated":223,"__hash__":2247},"qa\u002Fpython\u002Ftesting\u002Fmocking.md","Mocking",{"type":145,"value":2177,"toc":2178},[],{"title":148,"searchDepth":29,"depth":29,"links":2179},[],{},"\u002Fpython\u002Ftesting\u002Fmocking",[2183,2187,2191,2195,2199,2203,2207,2211,2215,2219,2223,2227,2231,2235,2239],{"id":2184,"difficulty":253,"q":2185,"a":2186},"what-is-mocking","What is mocking and why do you use it?","**Mocking** replaces a real dependency with a **fake stand-in object** that records\nhow it was called and returns whatever you tell it to. You use it to **isolate the\ncode under test** from slow, unreliable, or side-effecting collaborators — network\ncalls, databases, the clock, third-party APIs.\n\n```python\nfrom unittest.mock import Mock\n\nservice = Mock()\nservice.fetch.return_value = {\"id\": 1}    # canned response\n\nresult = service.fetch(\"\u002Fusers\u002F1\")        # no real network call\nresult                                    # {\"id\": 1}\nservice.fetch.assert_called_once()        # verify it happened\n```\n\nMocking makes tests **fast, deterministic, and focused** on your logic rather than\nthe dependency's. Rule of thumb: mock at the **boundaries** of your system (I\u002FO,\nexternal services), not your own pure functions.\n",{"id":2188,"difficulty":162,"q":2189,"a":2190},"mock-vs-magicmock","What is the difference between Mock and MagicMock?","Both auto-create attributes and methods on access. The difference: **`MagicMock`**\nadditionally supports **magic (dunder) methods** — `__len__`, `__iter__`,\n`__getitem__`, `__enter__`\u002F`__exit__`, etc. — so it can stand in for objects used\nwith `len()`, iteration, indexing, or `with`. A plain **`Mock`** raises on dunder\naccess.\n\n```python\nfrom unittest.mock import Mock, MagicMock\n\nm = Mock()\nlen(m)                  # TypeError — Mock has no __len__\n\nmm = MagicMock()\nmm.__len__.return_value = 3\nlen(mm)                 # 3 — magic methods supported\nlist(mm)                # works — __iter__ is mocked too\n```\n\n`patch()` uses `MagicMock` by default, which is why patched objects \"just work\" in\nmost cases. Use `MagicMock` when the dependency relies on protocols\u002Fdunders; `Mock`\nis fine for plain method calls.\n",{"id":2192,"difficulty":150,"q":2193,"a":2194},"patch-where-used","How does unittest.mock.patch work, and what does \"patch where it's used\" mean?","**`patch`** temporarily replaces an object with a mock for the duration of a test,\nas a **decorator** or a **context manager**, restoring the original afterward. The\ncritical rule is **\"patch where it's looked up, not where it's defined\"** — you patch\nthe name in the **module that imports and uses it**.\n\n```python\n# app.py\nfrom time import time\ndef stamp(): return time()\n\n# test.py — patch the reference INSIDE app, not 'time.time'\nfrom unittest.mock import patch\n\n@patch(\"app.time\")                 # where it's USED\ndef test_stamp(mock_time):\n    mock_time.return_value = 123\n    assert stamp() == 123\n\nwith patch(\"app.time\") as mock_time:   # context-manager form\n    mock_time.return_value = 123\n```\n\nPatching `\"time.time\"` here would fail, because `app` already bound its own `time`\nname at import. Always target the **importing module's namespace** — this is the\nsingle most common mocking mistake.\n",{"id":2196,"difficulty":162,"q":2197,"a":2198},"return-value-side-effect","What is the difference between return_value and side_effect?","**`return_value`** sets a **single fixed value** the mock returns on every call.\n**`side_effect`** is more powerful: assign a **function** (called with the same args),\nan **exception** (which gets raised), or an **iterable** (returning a different value\nper successive call).\n\n```python\nfrom unittest.mock import Mock\n\nm = Mock(return_value=42)\nm(); m()                       # 42, 42 — always the same\n\nm.side_effect = [1, 2, 3]      # one per call\nm(); m()                       # 1, then 2\n\nm.side_effect = ValueError(\"boom\")\nm()                            # raises ValueError\n\nm.side_effect = lambda x: x * 2\nm(10)                          # 20 — computed from the arg\n```\n\nUse `return_value` for a constant stub, and `side_effect` to **raise errors**,\n**vary results across calls**, or **compute** based on arguments. If both are set,\n`side_effect` wins (unless it returns the sentinel `DEFAULT`).\n",{"id":2200,"difficulty":162,"q":2201,"a":2202},"call-assertions","How do you assert a mock was called correctly?","Mocks **record every call**, so you verify interactions with the `assert_called*`\nfamily. Check **whether\u002Fhow many times** it was called and **with what arguments**,\nand inspect history via `call_args` \u002F `call_args_list`.\n\n```python\nfrom unittest.mock import Mock, call\n\nm = Mock()\nm(1, 2)\nm(3, key=\"v\")\n\nm.assert_called()                       # at least once\nm.assert_called_once()                  # exactly once -> would FAIL here\nm.assert_called_with(3, key=\"v\")        # the MOST RECENT call\nm.assert_any_call(1, 2)                 # any call matched\nm.assert_has_calls([call(1, 2), call(3, key=\"v\")])\nm.call_count                            # 2\n```\n\nNote `assert_called_with` checks only the **last** call — use `assert_any_call` or\n`assert_has_calls` for earlier ones. Beware typos: a misspelled assertion (e.g.\n`assert_called_once_with` -> `assert_called_onced_with`) silently passes, so spell\nthese carefully.\n",{"id":2204,"difficulty":162,"q":2205,"a":2206},"autospec","What is autospec and why is it useful?","A normal mock accepts **any** attribute access or call signature, so it can hide bugs\n— a test passes even if you call a method that doesn't exist or with wrong arguments.\n**Autospec** (`autospec=True`, or `create_autospec`) builds the mock to **match the\nreal object's API**, so it rejects nonexistent attributes and mismatched signatures.\n\n```python\nfrom unittest.mock import patch, create_autospec\n\nclass Api:\n    def fetch(self, url): ...\n\n@patch(\"app.Api\", autospec=True)\ndef test_it(MockApi):\n    api = MockApi()\n    api.fetch(\"\u002Fx\")          # OK — matches real signature\n    api.fetch()              # TypeError — missing 'url'!\n    api.delete()             # AttributeError — no such method\n```\n\nAutospec makes mocks **stay in sync with the real interface**, catching drift when\nthe real API changes. The tradeoff is a small overhead, but it's strongly\nrecommended for non-trivial dependencies.\n",{"id":2208,"difficulty":162,"q":2209,"a":2210},"patch-object","When do you use `patch.object` instead of `patch`?","**`patch.object(target, \"attr\")`** patches an **attribute on an object\u002Fclass you\nalready have a reference to** — clearer and refactor-safe (no string path to a name).\n`patch(\"module.attr\")` uses a string path; `patch.object` uses the actual object.\n\n```python\nfrom unittest.mock import patch\nfrom app import Service\n\nwith patch.object(Service, \"fetch\", return_value={\"id\": 1}) as m:\n    Service().fetch(\"\u002Fx\")        # patched method\n    m.assert_called_once()\n\n# patch an instance attribute too:\nsvc = Service()\nwith patch.object(svc, \"timeout\", 5):\n    ...\n```\n\nRule of thumb: use `patch.object` when you hold the object\u002Fclass directly (safer under\nrenames); use string-path `patch` when targeting a name in another module's namespace.\n",{"id":2212,"difficulty":162,"q":2213,"a":2214},"patch-dict","How do you temporarily patch a dictionary or environment variables?","**`patch.dict`** temporarily modifies a dict (like `os.environ` or a config) for the\ntest and **restores** it afterward. Pass `clear=True` to start empty.\n\n```python\nimport os\nfrom unittest.mock import patch\n\nwith patch.dict(os.environ, {\"API_KEY\": \"test123\"}):\n    assert os.environ[\"API_KEY\"] == \"test123\"\n# original environ restored here\n\nwith patch.dict(\"app.config\", {\"debug\": True}, clear=True):\n    ...\n```\n\nRule of thumb: use `patch.dict` for env vars and config dicts — it guarantees cleanup\nso tests don't leak state into each other.\n",{"id":2216,"difficulty":162,"q":2217,"a":2218},"mock-spec-attribute","What's the difference between `spec`, `spec_set`, and `autospec`?","**`spec=Cls`** restricts which **attributes** exist (but not call signatures).\n**`spec_set`** additionally forbids **setting** new attributes. **`autospec`** goes\nfurthest — it also enforces **method call signatures** recursively.\n\n```python\nfrom unittest.mock import Mock\n\nm = Mock(spec=[\"fetch\"])     # only .fetch allowed\nm.fetch()                    # ok\nm.missing                    # AttributeError\n\nm2 = Mock(spec_set=SomeClass)\nm2.new_attr = 1              # AttributeError — can't add attributes\n```\n\nRule of thumb: `spec` to catch typo'd attributes, `spec_set` to also block stray\nassignments, `autospec` for full signature checking — increasing strictness.\n",{"id":2220,"difficulty":150,"q":2221,"a":2222},"mock-property","How do you mock a property or attribute (not a method)?","Properties need **`PropertyMock`** (assigned on the **type**, not the instance), since\naccessing a property runs code. Plain attributes can be set directly on the mock.\n\n```python\nfrom unittest.mock import patch, PropertyMock\n\nclass Account:\n    @property\n    def balance(self): ...\n\nwith patch.object(Account, \"balance\", new_callable=PropertyMock) as m:\n    m.return_value = 100\n    assert Account().balance == 100    # property access returns 100\n```\n\nRule of thumb: use `PropertyMock` via `new_callable` to fake a property; for ordinary\nattributes, just assign `mock.attr = value`.\n",{"id":2224,"difficulty":150,"q":2225,"a":2226},"mocking-context-manager","How do you mock an object used as a context manager?","`with` calls **`__enter__`**, whose return value is bound by `as`. With `MagicMock`,\nset **`mock.__enter__.return_value`** (or `mock.return_value.__enter__.return_value`\nwhen patching the class) to the object the block should receive.\n\n```python\nfrom unittest.mock import patch, MagicMock\n\nwith patch(\"app.open\") as mock_open:\n    handle = mock_open.return_value.__enter__.return_value\n    handle.read.return_value = \"data\"\n    # code doing `with open(...) as f: f.read()` now gets \"data\"\n```\n\nRule of thumb: for `with` targets, configure `__enter__.return_value`; or use the\nbuilt-in helper `unittest.mock.mock_open` for file-open patterns.\n",{"id":2228,"difficulty":162,"q":2229,"a":2230},"when-not-to-mock","What are the downsides of over-mocking?","Excessive mocking couples tests to **implementation details** (which methods are\ncalled, in what order), making them brittle and giving false confidence — they can\npass while the real integration is broken. Mocks can also drift from the real API.\n\n```python\n# over-mocked: tests internal call sequence, not behavior — brittle\nmock_db.query.assert_called_with(\"SELECT ...\")\n\n# better: test observable behavior against a real\u002Ffake in-memory dependency\nresult = service.get_user(1)\nassert result.name == \"Ada\"\n```\n\nRule of thumb: mock only true external boundaries (network, time, filesystem); for\ninternal collaborators prefer real objects or fakes, and assert on **outcomes** not\ncall mechanics.\n",{"id":2232,"difficulty":162,"q":2233,"a":2234},"mock-open-helper","How does `mock_open` simplify testing file I\u002FO?","**`unittest.mock.mock_open`** is a prebuilt helper that mocks `open()` with working\n`read`, `readline`, iteration, and context-manager support — so you can test\nfile-reading code without touching the disk.\n\n```python\nfrom unittest.mock import patch, mock_open\n\nm = mock_open(read_data=\"line1\\nline2\")\nwith patch(\"app.open\", m):\n    # code doing `with open(path) as f: f.read()` sees \"line1\\nline2\"\n    ...\nm.assert_called_once_with(\"path\u002Fto\u002Ffile\")\n```\n\nRule of thumb: use `mock_open(read_data=...)` for file-read tests instead of\nhand-building `__enter__`\u002F`read` mocks; patch `open` in the module that uses it.\n",{"id":2236,"difficulty":253,"q":2237,"a":2238},"mock-reset","How do you reset a mock's recorded calls between assertions?","**`mock.reset_mock()`** clears recorded calls (`call_count`, `call_args_list`) without\nremoving configured `return_value`\u002F`side_effect`. Useful to reuse one mock across\nphases of a test. Pass `return_value=True`\u002F`side_effect=True` to also clear those.\n\n```python\nfrom unittest.mock import Mock\nm = Mock(return_value=1)\nm(); m()\nm.call_count            # 2\nm.reset_mock()\nm.call_count            # 0\nm.return_value          # still 1 — config preserved\n```\n\nRule of thumb: `reset_mock()` to clear call history mid-test while keeping stubs;\ngenerally prefer a fresh mock per test for clarity.\n",{"id":2240,"difficulty":150,"q":2241,"a":2242},"async-mock","How do you mock an async function or coroutine?","Use **`AsyncMock`** (Python 3.8+), whose calls return awaitables. `patch` auto-uses it\nwhen the target is an `async def`. Configure `return_value`\u002F`side_effect` as usual;\nthe result is awaited.\n\n```python\nfrom unittest.mock import AsyncMock, patch\n\nasync def test_it():\n    m = AsyncMock(return_value={\"id\": 1})\n    assert await m(\"\u002Fx\") == {\"id\": 1}\n    m.assert_awaited_once_with(\"\u002Fx\")\n\n@patch(\"app.fetch\", new_callable=AsyncMock)   # explicit if needed\nasync def test_fetch(mock_fetch): ...\n```\n\nRule of thumb: mock coroutines with `AsyncMock` and assert via `assert_awaited*`;\n`patch` detects async targets automatically in modern Python.\n",{"description":148},"Python interview questions on mocking and why it matters, Mock vs MagicMock, unittest.mock.patch and patching where it's used, return_value vs side_effect, call assertions, and autospec.","python\u002Ftesting\u002Fmocking","Mocking & Patching","2GD2AUvZD9K-WGxUsp3_mmPV0T03Mc7MzirtGgV3_AE",{"id":2249,"title":2250,"body":2251,"description":148,"difficulty":150,"extension":151,"framework":10,"frameworkSlug":8,"meta":2255,"navigation":153,"order":29,"path":2256,"questions":2257,"questionsCount":217,"related":218,"seo":2318,"seoDescription":2319,"stem":2320,"subtopic":2321,"topic":108,"topicSlug":110,"updated":223,"__hash__":2322},"qa\u002Fpython\u002Ftyping\u002Fgenerics-protocols.md","Generics Protocols",{"type":145,"value":2252,"toc":2253},[],{"title":148,"searchDepth":29,"depth":29,"links":2254},[],{},"\u002Fpython\u002Ftyping\u002Fgenerics-protocols",[2258,2262,2266,2270,2274,2278,2282,2286,2290,2294,2298,2302,2306,2310,2314],{"id":2259,"difficulty":150,"q":2260,"a":2261},"typevar-generic","What are TypeVar and Generic, and how do you write a generic class?","A **`TypeVar`** is a type variable — a placeholder that lets a function or class\nwork with **any type while preserving it** across inputs and outputs. Subclassing\n**`Generic[T]`** turns a class into a generic container parameterized by that\nvariable, so a `Stack[int]` is known to hold and return `int`s.\n\n```python\nfrom typing import TypeVar, Generic\n\nT = TypeVar(\"T\")               # one placeholder type\n\ndef first(items: list[T]) -> T:  # in and out share T\n    return items[0]\n\nclass Stack(Generic[T]):       # generic class\n    def __init__(self) -> None:\n        self._items: list[T] = []\n    def push(self, x: T) -> None:\n        self._items.append(x)\n    def pop(self) -> T:\n        return self._items.pop()\n\ns: Stack[int] = Stack()\ns.push(1)\nn = s.pop()                    # type checker knows n is int\n```\n\nUse a `TypeVar` whenever a relationship between argument and return types must be\ncaptured — `def first(items: list) -> object` loses that, but `list[T] -> T`\nkeeps it. In Python 3.12+ you can write `def first[T](items: list[T]) -> T`\nwithout the explicit `TypeVar`.\n",{"id":2263,"difficulty":150,"q":2264,"a":2265},"bounded-typevar","What is a bounded or constrained TypeVar?","A plain `TypeVar` accepts **any** type. A **bound** (`bound=...`) restricts it to a\ntype **and its subclasses**, while **constraints** (`TypeVar(\"T\", int, str)`)\nrestrict it to a fixed set of specific types. Both let the body safely use the\ncapabilities implied by the bound.\n\n```python\nfrom typing import TypeVar\n\nclass Animal:\n    def speak(self) -> str: ...\n\nA = TypeVar(\"A\", bound=Animal)     # A must be Animal or a subclass\n\ndef loudest(animals: list[A]) -> A:\n    for a in animals:\n        a.speak()                  # OK — bound guarantees this method\n    return animals[0]\n\nNum = TypeVar(\"Num\", int, float)   # constrained: ONLY int or float\ndef double(x: Num) -> Num:\n    return x * 2\n```\n\nReach for a **bound** when \"any subtype of X\" is acceptable and the body needs X's\ninterface; use **constraints** when only a handful of unrelated concrete types\nshould be allowed.\n",{"id":2267,"difficulty":150,"q":2268,"a":2269},"protocol-structural","What is typing.Protocol and how does it enable structural typing?","A **`Protocol`** defines an interface by **shape** rather than inheritance: any\nobject that has the right methods\u002Fattributes is accepted, even if it never\nexplicitly subclasses the protocol. This is **structural typing** (\"duck typing\")\nchecked statically — \"if it walks like a duck.\"\n\n```python\nfrom typing import Protocol\n\nclass SupportsClose(Protocol):\n    def close(self) -> None: ...\n\ndef shutdown(resource: SupportsClose) -> None:\n    resource.close()\n\nclass File:                # never inherits SupportsClose...\n    def close(self) -> None: ...\n\nshutdown(File())           # ...but accepted: it has close()\n```\n\nContrast with **nominal typing** (the usual `class B(A)`), where you must declare the\nrelationship. Protocols decouple the consumer from concrete classes — great for\ntyping third-party objects you can't modify.\n",{"id":2271,"difficulty":162,"q":2272,"a":2273},"runtime-checkable","What does @runtime_checkable do for a Protocol?","By default a `Protocol` exists only for **static** checkers — `isinstance()` against\nit raises `TypeError`. Decorating it with **`@runtime_checkable`** allows\n`isinstance()` \u002F `issubclass()` checks at runtime, but only for the **presence of\nthe named methods**, not their signatures or return types.\n\n```python\nfrom typing import Protocol, runtime_checkable\n\n@runtime_checkable\nclass Sized(Protocol):\n    def __len__(self) -> int: ...\n\nisinstance([1, 2, 3], Sized)   # True  — list has __len__\nisinstance(42, Sized)          # False — int has no __len__\n```\n\nIt's a convenience, not a guarantee: the check confirms a method *exists*, not that\nit takes the right arguments. Prefer static checking; use `@runtime_checkable` only\nwhen you genuinely need a runtime branch.\n",{"id":2275,"difficulty":162,"q":2276,"a":2277},"callable-types","How do you type a function passed as an argument?","Use **`Callable[[ArgTypes], ReturnType]`** from `typing` (or the built-in\n`collections.abc.Callable`). The first element is the **list of parameter types**,\nthe second is the **return type**. Use `...` for the parameters when you want to\naccept any signature.\n\n```python\nfrom collections.abc import Callable\n\ndef apply(fn: Callable[[int, int], int], a: int, b: int) -> int:\n    return fn(a, b)\n\napply(lambda x, y: x + y, 2, 3)        # 5\n\nhandler: Callable[..., None]           # any args, returns None\nno_args: Callable[[], str]             # takes nothing, returns str\n```\n\nFor more precise signatures (preserving exact parameters of a wrapped function),\n`ParamSpec` exists, but `Callable[[...], R]` covers the common cases. Type your\ncallbacks so the checker catches mismatched handlers.\n",{"id":2279,"difficulty":150,"q":2280,"a":2281},"covariance-invariance","What is the difference between covariance and invariance in generics?","Variance describes whether `Container[Subtype]` is usable where `Container[Supertype]`\nis expected. **Invariant** (the default, e.g. `list[T]`): `list[int]` is **not** a\n`list[str]` *or* a `list[object]`. **Covariant**: `Tuple[int]` is acceptable as\n`Tuple[object]`. The intuition: **mutable** containers must be invariant for safety;\n**read-only** ones can be covariant.\n\n```python\ndef total(nums: list[float]) -> float: ...\nints: list[int] = [1, 2]\ntotal(ints)        # type ERROR — list is invariant\n\nfrom collections.abc import Sequence\ndef total2(nums: Sequence[float]) -> float: ...\ntotal2(ints)       # OK — Sequence is covariant (read-only)\n```\n\nWhy mutables are invariant: if `list[int]` were a `list[object]`, a function could\nappend a `str` to it, corrupting the original `list[int]`. Rule of thumb: accept\n**`Sequence`\u002F`Iterable`** (covariant, read-only) in parameters to be flexible; reserve\n`list`\u002F`dict` for when you truly need to mutate.\n",{"id":2283,"difficulty":162,"q":2284,"a":2285},"multiple-typevars","How do you use multiple type variables in one function or class?","Declare **several `TypeVar`s** and use them independently to capture **distinct but\nrelated** types — for example a key type and a value type, or an input and an output.\nEach variable is resolved separately by the type checker.\n\n```python\nfrom typing import TypeVar\nfrom collections.abc import Callable\n\nK = TypeVar(\"K\")\nV = TypeVar(\"V\")\nR = TypeVar(\"R\")\n\ndef get_or(d: dict[K, V], key: K, default: V) -> V:   # K and V independent\n    return d.get(key, default)\n\ndef transform(items: list[K], fn: Callable[[K], R]) -> list[R]:\n    return [fn(x) for x in items]                     # K in, R out\n```\n\nRule of thumb: use one `TypeVar` per **independent** type relationship — pair them\n(like `K`\u002F`V`) when a function genuinely ties two types together.\n",{"id":2287,"difficulty":162,"q":2288,"a":2289},"self-type","What is typing.Self and when is it useful?","**`Self`** (Python 3.11+) is a type that means \"**the current class**\", so methods that\nreturn the instance — fluent builders, `__enter__`, alternative constructors — type\ncorrectly **even in subclasses**. It replaces the old bound-`TypeVar` trick.\n\n```python\nfrom typing import Self\n\nclass Query:\n    def where(self, cond: str) -> Self:   # returns same type as the caller\n        ...\n        return self\n\nclass AdminQuery(Query):\n    pass\n\nq = AdminQuery().where(\"active\")   # inferred as AdminQuery, not Query\n```\n\nRule of thumb: annotate \"returns itself\" methods with `Self` so chaining and factory\nmethods keep the **subclass** type instead of collapsing to the base class.\n",{"id":2291,"difficulty":150,"q":2292,"a":2293},"overload","What does @typing.overload do?","**`@overload`** lets you declare **multiple type signatures** for one function whose\nreturn type depends on its argument types. You write several `@overload` stubs (bodies\nare `...`) followed by a **single real implementation** — the checker matches callers\nagainst the stubs; the stubs vanish at runtime.\n\n```python\nfrom typing import overload\n\n@overload\ndef parse(x: int) -> int: ...\n@overload\ndef parse(x: str) -> list[str]: ...\n\ndef parse(x):                       # the only real implementation\n    return x if isinstance(x, int) else x.split()\n\nn = parse(10)        # checker knows: int\nparts = parse(\"a b\") # checker knows: list[str]\n```\n\nRule of thumb: use `@overload` when one function's **return type varies by input\ntype**; keep exactly one implementation below the stubs.\n",{"id":2295,"difficulty":150,"q":2296,"a":2297},"paramspec","What problem does ParamSpec solve?","**`ParamSpec`** (3.10+) captures an **entire parameter list** so a decorator can wrap a\nfunction **without losing its signature**. A plain `Callable[..., R]` forgets the\narguments; `ParamSpec` forwards them, keeping arg-level type checking on the wrapper.\n\n```python\nfrom typing import ParamSpec, TypeVar\nfrom collections.abc import Callable\nimport functools\n\nP = ParamSpec(\"P\")\nR = TypeVar(\"R\")\n\ndef logged(fn: Callable[P, R]) -> Callable[P, R]:\n    @functools.wraps(fn)\n    def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:   # same params as fn\n        print(\"calling\", fn.__name__)\n        return fn(*args, **kwargs)\n    return wrapper\n\n@logged\ndef add(a: int, b: int) -> int: ...\nadd(1, 2)        # still type-checked; add(\"x\") flagged\n```\n\nRule of thumb: type signature-preserving decorators with `Callable[P, R]` +\n`*args: P.args, **kwargs: P.kwargs` so wrapped calls keep full type safety.\n",{"id":2299,"difficulty":150,"q":2300,"a":2301},"generic-protocol","How do you write a generic Protocol?","A `Protocol` can itself be **generic** by also subclassing `Generic[T]` (or using the\n3.12 `class P[T](Protocol)` form). This describes a **shape parameterized by a type** —\ne.g. \"anything that produces `T` values\" — combining structural typing with generics.\n\n```python\nfrom typing import Protocol, TypeVar\n\nT = TypeVar(\"T\")\n\nclass Producer(Protocol[T]):\n    def produce(self) -> T: ...\n\ndef collect(p: Producer[int]) -> list[int]:\n    return [p.produce() for _ in range(3)]\n\nclass IntGen:                 # no inheritance needed\n    def produce(self) -> int: return 7\n\ncollect(IntGen())             # accepted: matches Producer[int] structurally\n```\n\nRule of thumb: parameterize a `Protocol` when the **shape carries a type** (a container,\nfactory, or callback interface) you want checked structurally.\n",{"id":2303,"difficulty":162,"q":2304,"a":2305},"protocol-attributes","Can a Protocol declare attributes, not just methods?","Yes — a `Protocol` can require **attributes\u002Fproperties** by declaring **annotated class\nvariables**. Any object with those attributes (however they're implemented — plain\nfield, property, slot) satisfies the protocol.\n\n```python\nfrom typing import Protocol\n\nclass Named(Protocol):\n    name: str               # required attribute, not a method\n\ndef greet(obj: Named) -> str:\n    return f\"Hi {obj.name}\"\n\nclass User:                 # has a 'name' attribute -> matches\n    def __init__(self, name: str) -> None:\n        self.name = name\n\ngreet(User(\"Ada\"))          # OK\n```\n\nRule of thumb: list required data as annotated attributes in the `Protocol`; the\nchecker accepts any object exposing them, regardless of how they're stored.\n",{"id":2307,"difficulty":150,"q":2308,"a":2309},"declaring-variance","How do you declare a covariant or contravariant TypeVar?","Pass **`covariant=True`** or **`contravariant=True`** when creating the `TypeVar`\n(conventionally named with `_co`\u002F`_contra`). **Covariant** suits **producer\u002Fread-only**\ngenerics (outputs); **contravariant** suits **consumer** generics (inputs). The default\nis invariant.\n\n```python\nfrom typing import TypeVar, Generic\n\nT_co = TypeVar(\"T_co\", covariant=True)\n\nclass Box(Generic[T_co]):        # read-only producer\n    def __init__(self, value: T_co) -> None:\n        self._value = value\n    def get(self) -> T_co:\n        return self._value\n\nb: Box[object] = Box[int](1)     # OK — Box[int] usable as Box[object]\n```\n\nA covariant variable must only appear in **output** positions; using it as a method\n**argument** is a type error. Rule of thumb: mark `covariant` for read-only producers,\n`contravariant` for write-only consumers, and leave mutable containers invariant.\n",{"id":2311,"difficulty":162,"q":2312,"a":2313},"pep695-syntax","What is the PEP 695 type-parameter syntax in Python 3.12?","Python 3.12 added **inline type parameters**: write `def fn[T](...)` and\n`class C[T]:` **without** declaring a separate `TypeVar`, plus a **`type`** statement\nfor aliases. It's more concise and scopes the variable to the function\u002Fclass.\n\n```python\n# 3.12+ — no `T = TypeVar(\"T\")` needed\ndef first[T](items: list[T]) -> T:\n    return items[0]\n\nclass Stack[T]:\n    def push(self, x: T) -> None: ...\n\ntype Vector = list[float]          # generic-capable alias\ntype Pair[T] = tuple[T, T]\n```\n\nIt coexists with the classic `TypeVar` style (still required pre-3.12). Rule of thumb:\nprefer the inline `[T]` syntax and `type` aliases on 3.12+; fall back to explicit\n`TypeVar`\u002F`TypeAlias` for older runtimes.\n",{"id":2315,"difficulty":162,"q":2316,"a":2317},"abc-vs-protocol","When should you use a Protocol versus an abstract base class?","Both define an interface, but differ in **how conformance is established**. An **ABC**\n(`abc.ABC`) is **nominal** — a class must **explicitly subclass** it and implement its\nabstract methods. A **`Protocol`** is **structural** — any class with the right shape\nmatches, **no inheritance required**.\n\n```python\nfrom abc import ABC, abstractmethod\nfrom typing import Protocol\n\nclass WriterABC(ABC):              # nominal: must subclass\n    @abstractmethod\n    def write(self, data: str) -> None: ...\n\nclass WriterProto(Protocol):       # structural: just needs write()\n    def write(self, data: str) -> None: ...\n\nclass Console:                     # matches WriterProto, NOT WriterABC\n    def write(self, data: str) -> None: print(data)\n```\n\nUse an **ABC** when you own the hierarchy and want to share implementation or force\nregistration; use a **Protocol** to type objects you **don't control** (third-party or\nduck-typed). Rule of thumb: Protocol for \"any object shaped like this\", ABC for \"a\nmember of this explicit family\".\n",{"description":148},"Python interview questions on TypeVar and Generic classes, bounded type variables, typing.Protocol structural typing, Callable types, and covariance vs invariance.","python\u002Ftyping\u002Fgenerics-protocols","Generics & Protocols","l1CqURptVQYiDQ_M6oy8yYT5IqmBSLbmyXk_vkV_QAo",{"id":2324,"title":2325,"body":2326,"description":148,"difficulty":150,"extension":151,"framework":10,"frameworkSlug":8,"meta":2330,"navigation":153,"order":11,"path":2331,"questions":2332,"questionsCount":1346,"related":218,"seo":2397,"seoDescription":2398,"stem":2399,"subtopic":2400,"topic":90,"topicSlug":92,"updated":223,"__hash__":2401},"qa\u002Fpython\u002Fconcurrency\u002Fasyncio.md","Asyncio",{"type":145,"value":2327,"toc":2328},[],{"title":148,"searchDepth":29,"depth":29,"links":2329},[],{},"\u002Fpython\u002Fconcurrency\u002Fasyncio",[2333,2337,2341,2345,2349,2353,2357,2361,2365,2369,2373,2377,2381,2385,2389,2393],{"id":2334,"difficulty":150,"q":2335,"a":2336},"what-is-asyncio","What is asyncio and the event loop?","**asyncio** is Python's framework for **single-threaded concurrency** using an\n**event loop**. The **event loop** is a scheduler that runs many **coroutines**\ncooperatively: when one coroutine **awaits** something (typically I\u002FO), it\n**yields control** back to the loop, which runs another ready coroutine while the\nfirst waits. No thread is blocked sitting idle.\n\n```python\nimport asyncio\n\nasync def main():\n    print(\"hello\")\n    await asyncio.sleep(1)    # yields to the loop instead of blocking\n    print(\"world\")\n\nasyncio.run(main())           # creates the loop, runs main, then closes it\n```\n\nCrucially this is **concurrency, not parallelism** — one thread, one core,\ninterleaving tasks at `await` points. Rule of thumb: asyncio shines when you\nhave **many tasks that spend most of their time waiting** on I\u002FO.\n",{"id":2338,"difficulty":162,"q":2339,"a":2340},"async-def-await","What do async def and await do?","**`async def`** defines a **coroutine function** — calling it doesn't run the\nbody, it returns a **coroutine object** that must be awaited or scheduled.\n**`await`** **suspends** the current coroutine until the awaited **awaitable**\n(another coroutine, a Task, or a Future) completes, handing control back to the\nevent loop in the meantime.\n\n```python\nimport asyncio\n\nasync def fetch():\n    await asyncio.sleep(1)    # suspension point — loop runs others here\n    return \"data\"\n\nasync def main():\n    coro = fetch()            # nothing has run yet\n    result = await coro       # now it runs; main suspends until it finishes\n    print(result)\n\nasyncio.run(main())\n```\n\nYou can only `await` **inside** an `async def`. Forgetting to await a coroutine\nis a common bug — it never runs and you get a \"coroutine was never awaited\"\nwarning. Think of `await` as \"pause me here and let others run until this is\nready.\"\n",{"id":2342,"difficulty":150,"q":2343,"a":2344},"coroutines-vs-threads","How do coroutines differ from threads?","**Threads** use **preemptive** multitasking — the OS can switch threads at\n**any** point, so shared state needs locks and context switches are relatively\nexpensive. **Coroutines** use **cooperative** multitasking on **one thread** —\nswitches happen **only at explicit `await` points**, so the code between awaits\nis effectively atomic and switching is cheap.\n\n```python\nimport asyncio\n\nasync def task(name):\n    print(f\"{name} start\")\n    await asyncio.sleep(1)        # the ONLY place this can yield\n    print(f\"{name} done\")\n\nasync def main():\n    await asyncio.gather(task(\"a\"), task(\"b\"))   # thousands are feasible\n\nasyncio.run(main())\n```\n\nBecause there's no OS thread per task, you can run **tens of thousands** of\ncoroutines cheaply, and most data races disappear. The catch: a coroutine that\nnever awaits **monopolizes** the loop. Threads tolerate blocking code;\ncoroutines do not.\n",{"id":2346,"difficulty":162,"q":2347,"a":2348},"asyncio-gather","How do you run coroutines concurrently with asyncio.gather?","Awaiting coroutines one by one runs them **sequentially**. To run them\n**concurrently**, schedule them together with **`asyncio.gather`** (or wrap each\nin a **Task**), which lets the loop interleave their `await` points.\n\n```python\nimport asyncio\n\nasync def fetch(n):\n    await asyncio.sleep(1)\n    return n * 2\n\nasync def main():\n    # all three overlap -> ~1 second total, not 3\n    results = await asyncio.gather(fetch(1), fetch(2), fetch(3))\n    print(results)        # [2, 4, 6] — order matches the arguments\n\nasyncio.run(main())\n```\n\n`gather` returns results in argument order and, by default, propagates the first\nexception. `asyncio.create_task(coro)` schedules a coroutine to start running\nimmediately so it overlaps with later code. The key idea: concurrency comes from\n**scheduling tasks together**, not from awaiting them in turn.\n",{"id":2350,"difficulty":150,"q":2351,"a":2352},"dont-block-the-loop","What is the \"don't block the event loop\" rule?","Because asyncio runs on **one thread**, any code that **doesn't await** —\nheavy CPU work or **blocking synchronous I\u002FO** like `time.sleep`,\n`requests.get`, or blocking DB drivers — **freezes the entire loop**. Every other\ncoroutine stalls until that call returns.\n\n```python\nimport asyncio, time\n\nasync def bad():\n    time.sleep(5)              # BLOCKS the whole loop for 5s\n\nasync def good():\n    await asyncio.sleep(5)     # yields; other tasks keep running\n\n# offload unavoidable blocking\u002FCPU work to a thread or process pool:\nasync def offloaded():\n    loop = asyncio.get_running_loop()\n    await loop.run_in_executor(None, time.sleep, 5)   # runs in a thread\n```\n\nFixes: use **async-native libraries** (`aiohttp`, `asyncpg`), and push\nCPU-bound or unavoidably-blocking calls into `run_in_executor` (a thread pool, or\na process pool for CPU work). Rule of thumb: inside `async` code, never call\nsomething that blocks without awaiting it.\n",{"id":2354,"difficulty":162,"q":2355,"a":2356},"when-async-helps","When does asyncio actually help?","asyncio wins for **high-concurrency I\u002FO-bound** workloads — thousands of\nnetwork calls, web requests, websocket connections, or database queries that\nspend their time **waiting**. While one request waits, the loop services others,\nso a single thread handles huge concurrency cheaply.\n\n```python\nimport asyncio\n\nasync def call_api(i):\n    await asyncio.sleep(0.5)        # stand-in for a network round trip\n    return i\n\nasync def main():\n    # 1000 \"requests\" overlap on one thread in ~0.5s of wall time\n    results = await asyncio.gather(*(call_api(i) for i in range(1000)))\n    print(len(results))             # 1000\n\nasyncio.run(main())\n```\n\nIt does **not** help **CPU-bound** work — that needs multiprocessing for real\nparallelism. And for a handful of blocking calls, plain threads are often\nsimpler. Reach for asyncio when concurrency is high and the bottleneck is\nwaiting on I\u002FO.\n",{"id":2358,"difficulty":162,"q":2359,"a":2360},"create-task","What is the difference between awaiting a coroutine and asyncio.create_task?","**Awaiting a coroutine directly** runs it to completion **before** the next\nline — that is sequential. **`asyncio.create_task(coro)`** schedules the\ncoroutine on the loop **immediately** and returns a **Task** you can await\nlater, so work overlaps.\n\n```python\nimport asyncio\n\nasync def work(n):\n    await asyncio.sleep(1)\n    return n\n\nasync def main():\n    await work(1); await work(2)          # sequential -> ~2s\n\n    t1 = asyncio.create_task(work(1))      # both start now\n    t2 = asyncio.create_task(work(2))\n    await t1; await t2                     # concurrent -> ~1s\n\nasyncio.run(main())\n```\n\nRule of thumb: a bare `await` is \"do this **now and wait**\"; `create_task`\nis \"**start this in the background** and collect it later\".\n",{"id":2362,"difficulty":162,"q":2363,"a":2364},"gather-vs-wait","What is the difference between asyncio.gather and asyncio.wait?","**`gather`** runs awaitables concurrently and returns their **results in\norder** as a list; if one raises, `gather` propagates that exception (unless\nyou pass `return_exceptions=True`). **`wait`** returns two **sets** —\n`(done, pending)` of Tasks — and never raises for you; you inspect each\nTask's result yourself.\n\n```python\nresults = await asyncio.gather(a(), b(), c())     # [ra, rb, rc], ordered\n\ndone, pending = await asyncio.wait(\n    tasks, return_when=asyncio.FIRST_COMPLETED)   # set-based, manual\n```\n\nRule of thumb: reach for **`gather`** when you want all results back in\norder; use **`wait`** only when you need fine control like \"return when the\nfirst finishes\".\n",{"id":2366,"difficulty":162,"q":2367,"a":2368},"return-exceptions","How does gather behave when a task raises, and what does return_exceptions do?","By default the **first exception** propagates out of `gather` as soon as it\noccurs; the other tasks keep running but their results (and errors) are\n**lost\u002Fignored**. With **`return_exceptions=True`**, exceptions are returned\n**in the results list** alongside normal values instead of being raised.\n\n```python\nasync def ok():  return 1\nasync def bad(): raise ValueError(\"boom\")\n\nres = await asyncio.gather(ok(), bad(), return_exceptions=True)\n# res == [1, ValueError('boom')]  -> inspect each item\n\nfor r in res:\n    if isinstance(r, Exception):\n        print(\"failed:\", r)\n```\n\nRule of thumb: use `return_exceptions=True` when you want **every** task to\nfinish and to handle failures individually rather than aborting on the first.\n",{"id":2370,"difficulty":162,"q":2371,"a":2372},"to-thread","How do you call blocking code from async without freezing the loop?","Run it in a **thread** so the event loop stays free. **`asyncio.to_thread(fn,\n*args)`** (3.9+) is the simple way; under the hood it uses\n`loop.run_in_executor`. The blocking call happens on a worker thread and the\ncoroutine simply awaits the result.\n\n```python\nimport asyncio, time\n\ndef blocking_io():\n    time.sleep(2)          # a real blocking call (DB driver, requests, ...)\n    return \"done\"\n\nasync def main():\n    result = await asyncio.to_thread(blocking_io)   # loop stays responsive\n    print(result)\n\nasyncio.run(main())\n```\n\nRule of thumb: never call `time.sleep`, `requests.get`, or other blocking\nfunctions directly in a coroutine — wrap them in `asyncio.to_thread` (I\u002FO)\nor use multiprocessing (CPU).\n",{"id":2374,"difficulty":162,"q":2375,"a":2376},"timeout","How do you put a timeout on an awaitable?","Wrap it with **`asyncio.wait_for(aw, timeout)`**, which cancels the awaitable\nand raises **`asyncio.TimeoutError`** if it runs too long. Python 3.11 added\nthe **`asyncio.timeout()`** context manager for guarding a whole block.\n\n```python\ntry:\n    data = await asyncio.wait_for(fetch(), timeout=5)\nexcept asyncio.TimeoutError:\n    data = None                      # fell back after 5s\n\n# 3.11+ context-manager form:\nasync with asyncio.timeout(5):\n    data = await fetch()\n```\n\nRule of thumb: any external call deserves a timeout — without one a single\nhung request can stall a coroutine forever.\n",{"id":2378,"difficulty":150,"q":2379,"a":2380},"cancellation","How does task cancellation work in asyncio?","Calling **`task.cancel()`** requests cancellation: at the task's next\n`await`, asyncio raises **`asyncio.CancelledError`** inside it. You can clean\nup in a `try\u002Ffinally` or `except CancelledError`, but you should **re-raise**\nit — swallowing it breaks cancellation semantics.\n\n```python\nasync def worker():\n    try:\n        await asyncio.sleep(10)\n    except asyncio.CancelledError:\n        print(\"cleaning up\")\n        raise                       # re-raise so cancellation completes\n\nasync def main():\n    t = asyncio.create_task(worker())\n    await asyncio.sleep(0.1)\n    t.cancel()\n    await asyncio.gather(t, return_exceptions=True)\n```\n\nRule of thumb: cancellation is **cooperative** — it only takes effect at an\n`await` point, and `CancelledError` should be re-raised after cleanup.\n",{"id":2382,"difficulty":162,"q":2383,"a":2384},"async-with-for","What are async with and async for used for?","They are the async versions of `with` and `for`. **`async with`** drives an\n**async context manager** (`__aenter__`\u002F`__aexit__`), e.g. acquiring a\nconnection that itself awaits. **`async for`** iterates an **async generator\n\u002F async iterator** (`__anext__`), awaiting between items — ideal for\nstreaming I\u002FO.\n\n```python\nasync def stream(url):\n    async with session.get(url) as resp:     # awaits the connection\n        async for line in resp.content:       # awaits each chunk\n            process(line)\n```\n\nRule of thumb: use them whenever the setup\u002Fteardown or the per-item step\ninvolves I\u002FO that must be awaited.\n",{"id":2386,"difficulty":162,"q":2387,"a":2388},"semaphore","How do you limit how many coroutines run at once?","Use an **`asyncio.Semaphore(n)`** to cap concurrency. Each coroutine acquires\nit before the protected work and releases on exit; only `n` can hold it at\nonce, so the rest wait. This prevents hammering an API or exhausting\nconnections when you fan out thousands of tasks.\n\n```python\nsem = asyncio.Semaphore(10)          # at most 10 in flight\n\nasync def fetch(url):\n    async with sem:                  # blocks if 10 already running\n        return await get(url)\n\nresults = await asyncio.gather(*(fetch(u) for u in urls))\n```\n\nRule of thumb: when you `gather` a huge number of tasks, gate them with a\nSemaphore so you control the real level of parallelism.\n",{"id":2390,"difficulty":162,"q":2391,"a":2392},"asyncio-queue","What is asyncio.Queue used for?","It is an **async-aware queue** for the **producer\u002Fconsumer** pattern within\none loop. `await queue.put(item)` and `await queue.get()` suspend instead of\nblocking the thread, letting producers and consumers run as concurrent tasks.\n`task_done()`\u002F`join()` let you wait until all items are processed.\n\n```python\nq = asyncio.Queue(maxsize=100)\n\nasync def producer():\n    for i in range(10):\n        await q.put(i)\n\nasync def consumer():\n    while True:\n        item = await q.get()\n        ...                          # handle item\n        q.task_done()\n```\n\nRule of thumb: use `asyncio.Queue` (not `queue.Queue`) to hand work between\ncoroutines — it cooperates with the event loop instead of blocking it.\n",{"id":2394,"difficulty":150,"q":2395,"a":2396},"loop-already-running","Why does \"asyncio.run() cannot be called from a running event loop\" happen?","**`asyncio.run()`** creates a **new** event loop and is meant to be the\nsingle top-level entry point. Calling it again while a loop is already\nrunning — common inside **Jupyter notebooks** or another async framework —\nraises `RuntimeError`. From inside a coroutine you should simply **await**,\nnot call `asyncio.run` again.\n\n```python\nasync def main(): ...\n\nasyncio.run(main())          # correct: top level, once\n\nasync def outer():\n    asyncio.run(main())      # WRONG: loop already running\n    await main()             # correct: just await\n```\n\nRule of thumb: call `asyncio.run` **once** at the program's entry point;\neverywhere else inside async code, `await` the coroutine.\n",{"description":148},"Python interview questions on asyncio and async\u002Fawait: the event loop, coroutines vs threads, asyncio.gather, the don't-block-the-loop rule, and when async helps.","python\u002Fconcurrency\u002Fasyncio","asyncio & async\u002Fawait","8MzOsmKaza08YWClEoPXEPBCSSIxPyI-XACdFC_4O20",{"id":2403,"title":2404,"body":2405,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":2409,"navigation":153,"order":11,"path":2410,"questions":2411,"questionsCount":1346,"related":218,"seo":2476,"seoDescription":2477,"stem":2478,"subtopic":2404,"topic":28,"topicSlug":30,"updated":223,"__hash__":2479},"qa\u002Fpython\u002Fdata-structures\u002Fdictionaries.md","Dictionaries",{"type":145,"value":2406,"toc":2407},[],{"title":148,"searchDepth":29,"depth":29,"links":2408},[],{},"\u002Fpython\u002Fdata-structures\u002Fdictionaries",[2412,2416,2420,2424,2428,2432,2436,2440,2444,2448,2452,2456,2460,2464,2468,2472],{"id":2413,"difficulty":253,"q":2414,"a":2415},"insertion-ordering","Are Python dictionaries ordered?","Yes. Since **Python 3.7**, dictionaries **preserve insertion order** as a\n**language guarantee** — iterating a dict yields keys in the order they were\nfirst added. (This was an implementation detail in CPython 3.6, then made\nofficial in 3.7.)\n\n```python\nd = {}\nd[\"b\"] = 1\nd[\"a\"] = 2\nd[\"c\"] = 3\nlist(d)            # ['b', 'a', 'c'] — insertion order, not sorted\n\nd[\"b\"] = 9         # updating a value does NOT change order\nlist(d)            # ['b', 'a', 'c']\n```\n\nNote that **updating** an existing key keeps its original position; only the\nfirst insertion fixes the order. Reassigning doesn't move it. Because ordering\nis guaranteed, plain `dict` now covers most cases that once needed\n`OrderedDict`.\n",{"id":2417,"difficulty":253,"q":2418,"a":2419},"get-vs-bracket-setdefault","What is the difference between d[key], d.get(key), and d.setdefault()?","**`d[key]`** raises `KeyError` if the key is missing. **`d.get(key, default)`**\nreturns a default (or `None`) instead of raising — a safe read.\n**`d.setdefault(key, default)`** returns the existing value if present, but if\nmissing it **inserts** the default and returns it.\n\n```python\nd = {\"a\": 1}\nd[\"b\"]               # KeyError\nd.get(\"b\")           # None — no error\nd.get(\"b\", 0)        # 0   — supplied default\n\nd.setdefault(\"a\", 99)  # 1  — already present, unchanged\nd.setdefault(\"c\", []).append(5)  # inserts c=[], then appends -> {'c': [5]}\n```\n\nUse `get` for a safe lookup that **doesn't mutate**, and `setdefault` to\n**read-or-initialize** in one step (handy for grouping). For heavy grouping work,\n`collections.defaultdict` is usually cleaner.\n",{"id":2421,"difficulty":162,"q":2422,"a":2423},"merging-dicts","What are the ways to merge two dictionaries?","The modern way is the **`|` merge operator** (Python 3.9+), which returns a new\ndict; **`|=`** merges in place. Before 3.9, the idiom was **`{**a, **b}`\nunpacking**, and `dict.update()` merges in place.\n\n```python\na = {\"x\": 1, \"y\": 2}\nb = {\"y\": 9, \"z\": 3}\n\na | b           # {'x': 1, 'y': 9, 'z': 3}  — new dict (3.9+)\n{**a, **b}      # {'x': 1, 'y': 9, 'z': 3}  — same, works pre-3.9\n\na.update(b)     # mutates a in place -> {'x': 1, 'y': 9, 'z': 3}\n```\n\nIn every approach the **right-hand dict wins** on key collisions (`y` becomes\n`9`). Use `|` for a clean new dict on modern Python, `{**a, **b}` for\ncompatibility, and `update()`\u002F`|=` when you want to mutate in place.\n",{"id":2425,"difficulty":162,"q":2426,"a":2427},"keys-values-items-views","What do keys(), values(), and items() return, and what is a view object?","They return **view objects** — dynamic, read-only windows onto the dict that\n**reflect changes live** rather than copying the data. They're iterable and\nsupport set-like operations, but they're not lists.\n\n```python\nd = {\"a\": 1, \"b\": 2}\nkeys = d.keys()\nd[\"c\"] = 3\nlist(keys)          # ['a', 'b', 'c'] — view updated automatically!\n\nkeys[0]             # TypeError — a view isn't indexable\nlist(d.keys())      # ['a', 'b', 'c'] — materialize when you need a list\n\nd.keys() & {\"a\"}    # {'a'} — keys views support set operations\n```\n\nBecause a view is **live**, it's memory-cheap but you must `list(...)` it to\nindex or snapshot it. Also avoid mutating the dict's size while iterating a view\n— that raises `RuntimeError`. Use views to iterate efficiently; copy to a list\nwhen you need a stable, indexable sequence.\n",{"id":2429,"difficulty":162,"q":2430,"a":2431},"keys-hashable-lookup","Why must dictionary keys be hashable, and why are lookups O(1)?","A dict is a **hash table**: it computes `hash(key)` to decide which bucket the\nentry lives in, giving **average O(1)** insertion and lookup regardless of size.\nFor this to work, a key's hash must **never change**, so keys must be\n**hashable** — effectively **immutable**.\n\n```python\nd = {}\nd[(1, 2)] = \"ok\"     # tuple is immutable -> hashable\nd[[1, 2]] = \"no\"     # TypeError: unhashable type: 'list'\n\n# O(1): lookup time doesn't grow with the dict's size\n\"x\" in d             # hashes \"x\", checks one bucket — not a full scan\n```\n\nIf a mutable key could change after insertion, its hash would change and the\nentry would land in the wrong bucket — you'd never find it again. That's why\nlists, dicts, and sets can't be keys, but tuples and frozensets can. The\nhash-table design is exactly what makes membership tests near-instant.\n",{"id":2433,"difficulty":253,"q":2434,"a":2435},"dict-comprehension","What is a dict comprehension?","A **dict comprehension** builds a dictionary in one expression using\n`{key: value for item in iterable}`, optionally with a filtering `if`. It's the\nconcise, readable alternative to a `for` loop that calls `d[k] = v`.\n\n```python\nsquares = {n: n * n for n in range(5)}\n# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}\n\nprices = {\"apple\": 3, \"pear\": 0, \"fig\": 7}\nin_stock = {k: v for k, v in prices.items() if v > 0}\n# {'apple': 3, 'fig': 7}\n\ninverted = {v: k for k, v in prices.items()}   # swap keys\u002Fvalues\n```\n\nLike other comprehensions it has its **own scope** (no leaked loop variable) and\nis generally faster than the equivalent loop. Use it to transform, filter, or\ninvert mappings clearly — but keep it readable rather than cramming in logic.\n",{"id":2437,"difficulty":253,"q":2438,"a":2439},"dict-pop-del","What are the ways to remove a key from a dict?","**`del d[k]`** removes the key but raises `KeyError` if absent. **`d.pop(k)`**\nremoves and **returns** the value, with an optional default to avoid the\nerror. **`d.popitem()`** removes and returns the **last** inserted pair (LIFO\nsince 3.7). **`d.clear()`** empties it.\n\n```python\nd = {\"a\": 1, \"b\": 2}\nd.pop(\"a\")            # 1\nd.pop(\"x\", None)      # None, no error\nd.popitem()           # ('b', 2)\ndel d[\"missing\"]      # KeyError\n```\n\nRule of thumb: prefer `pop(k, default)` when the key might be absent; use\n`del` only when you know it exists.\n",{"id":2441,"difficulty":162,"q":2442,"a":2443},"dict-iteration-order","Can you modify a dict while iterating over it?","**No** — adding or removing keys during iteration raises\n`RuntimeError: dictionary changed size during iteration`. Iterate over a\n**snapshot** (`list(d)` or `list(d.items())`) if you must mutate, or build a\nnew dict via comprehension.\n\n```python\nd = {\"a\": 1, \"b\": 0, \"c\": 2}\nfor k in list(d):              # snapshot of keys\n    if d[k] == 0:\n        del d[k]\n# or: d = {k: v for k, v in d.items() if v != 0}\n```\n\nRule of thumb: never resize a dict mid-iteration — iterate a copy of the keys\nor rebuild with a comprehension.\n",{"id":2445,"difficulty":162,"q":2446,"a":2447},"dict-get-vs-setdefault","When does setdefault shine over get, and what is its catch?","**`get(k, default)`** only reads; **`setdefault(k, default)`** reads **and\ninserts** the default if missing, returning the stored value — handy for\naccumulating into lists. The catch: the **default is always evaluated**, even\nwhen the key exists, so an expensive default is wasteful.\n\n```python\ngroups = {}\nfor name, dept in people:\n    groups.setdefault(dept, []).append(name)   # one-liner grouping\n\nd.setdefault(k, expensive())   # expensive() runs even if k present!\n```\n\nRule of thumb: `setdefault` for accumulate-into patterns; for heavy defaults\nprefer `defaultdict` or an explicit `if k not in d` check.\n",{"id":2449,"difficulty":162,"q":2450,"a":2451},"hashable-keys-mutable","Why can't you use a list as a dict key but can use a tuple?","Keys must be **hashable**, meaning a stable `__hash__` for their lifetime.\nLists are **mutable**, so their contents (and any hash) could change,\nbreaking lookups — Python makes them unhashable. Tuples are immutable and\nhashable **only if all their elements are**.\n\n```python\n{(\"x\", 1): \"ok\"}          # fine, tuple of immutables\n{[1, 2]: \"v\"}             # TypeError: unhashable type: 'list'\n{(1, [2]): \"v\"}           # TypeError: tuple contains a list\n```\n\nRule of thumb: use immutable values (tuples, frozensets, strings, numbers) as\nkeys; convert mutable containers to tuples\u002Ffrozensets first.\n",{"id":2453,"difficulty":253,"q":2454,"a":2455},"dict-comprehension-from-keys","What does dict.fromkeys do, and what is its shared-default trap?","**`dict.fromkeys(keys, value)`** builds a dict mapping each key to the **same**\nvalue. The trap: if that value is **mutable** (a list), **all keys share one\nobject** — mutating via one key affects all. Use a comprehension for\nper-key fresh values.\n\n```python\ndict.fromkeys(\"abc\", 0)          # {'a': 0, 'b': 0, 'c': 0}  fine (immutable)\n\nd = dict.fromkeys(\"ab\", [])      # both share ONE list\nd[\"a\"].append(1)                 # {'a': [1], 'b': [1]} !\nd = {k: [] for k in \"ab\"}        # correct: independent lists\n```\n\nRule of thumb: `fromkeys` is safe for immutable defaults; use a dict\ncomprehension whenever the default is mutable.\n",{"id":2457,"difficulty":150,"q":2458,"a":2459},"dict-hashmap-collisions","How does a Python dict handle hash collisions internally?","A dict is an **open-addressing hash table**. The key's hash picks a slot; on a\n**collision** it **probes** other slots (a perturbation sequence) until it\nfinds the key or an empty slot. It stores the full hash to compare quickly and\n**resizes\u002Frehashes** when it gets ~2\u002F3 full to keep operations near O(1).\n\n```text\nhash(key) -> slot index -> collision? -> probe next slots -> place\u002Ffind\n```\n\nRule of thumb: average lookup\u002Finsert is O(1); a well-behaved `__hash__` that\nspreads values keeps it that way (bad hashes degrade toward O(n)).\n",{"id":2461,"difficulty":162,"q":2462,"a":2463},"dict-memory-views-live","Are dict view objects live?","**Yes** — `keys()`, `values()`, and `items()` return **dynamic views** that\nreflect later changes to the dict, rather than static snapshots. They're also\n**set-like** for keys\u002Fitems, supporting `&`, `|`, `-` for comparing dicts.\n\n```python\nd = {\"a\": 1}\nks = d.keys()\nd[\"b\"] = 2\nlist(ks)                 # ['a', 'b']  -> view updated\n\n{\"a\", \"x\"} & d.keys()    # {'a'}  -> set operations on views\n```\n\nRule of thumb: views stay in sync with the dict; materialize with `list()`\nif you need a frozen snapshot.\n",{"id":2465,"difficulty":253,"q":2466,"a":2467},"merge-operator","What do the | and |= dict operators do?","Since **Python 3.9**, **`d1 | d2`** returns a **new merged dict** (right side\nwins on key conflicts), and **`d1 |= d2`** updates `d1` in place (like\n`update`). They're the modern, readable alternative to `{**d1, **d2}`.\n\n```python\na, b = {\"x\": 1, \"y\": 2}, {\"y\": 9, \"z\": 3}\na | b              # {'x': 1, 'y': 9, 'z': 3}  new dict\na |= b             # a becomes {'x': 1, 'y': 9, 'z': 3}\n```\n\nRule of thumb: on 3.9+ use `|`\u002F`|=` for clarity; `{**a, **b}` still works and\nis needed on older versions.\n",{"id":2469,"difficulty":162,"q":2470,"a":2471},"dict-default-vs-counter","For counting, when do you choose Counter vs defaultdict(int) vs get?","All three count, with rising convenience. **`d.get(k, 0) + 1`** works on a\nplain dict. **`defaultdict(int)`** drops the `get` boilerplate. **`Counter`**\nadds counting-specific tools (`most_common`, arithmetic, `+`\u002F`-`) and can\ncount an iterable in one call.\n\n```python\nfrom collections import Counter, defaultdict\nCounter(text)                         # one-shot, richest API\nd = defaultdict(int)\nfor c in text: d[c] += 1              # simple, no extra import beyond collections\n```\n\nRule of thumb: use `Counter` when you want ranking\u002Farithmetic, `defaultdict(int)`\nfor plain accumulation, and `get` only for tiny ad-hoc counts.\n",{"id":2473,"difficulty":150,"q":2474,"a":2475},"dict-key-collision-equality","How does a dict decide two keys are the same?","It uses **both `__hash__` and `__eq__`**: keys land in the same bucket if\ntheir hashes match, then are confirmed equal with `==`. So **equal objects\nmust have equal hashes** (the hash\u002Feq contract). Break it and lookups fail —\n`1`, `1.0`, and `True` collide intentionally because they're equal and\nhash-equal.\n\n```python\nd = {1: \"int\", True: \"bool\"}     # True == 1 and hash(True)==hash(1)\nd                                # {1: 'bool'}  -> same key, overwritten\n```\n\nRule of thumb: any custom key class must implement `__hash__` and `__eq__`\nconsistently, or it will misbehave as a dict key.\n",{"description":148},"Python interview questions on dict insertion ordering, get vs bracket access, setdefault, merging dicts, view objects, why keys must be hashable, and dict comprehensions.","python\u002Fdata-structures\u002Fdictionaries","FdPUFj0eIp9aECP3DB9-9s2GIZiWtJc-9pVNnKko17g",{"id":2481,"title":2482,"body":2483,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":2487,"navigation":153,"order":11,"path":2488,"questions":2489,"questionsCount":1346,"related":218,"seo":2554,"seoDescription":2555,"stem":2556,"subtopic":2557,"topic":63,"topicSlug":65,"updated":223,"__hash__":2558},"qa\u002Fpython\u002Fexceptions\u002Fcustom-exceptions.md","Custom Exceptions",{"type":145,"value":2484,"toc":2485},[],{"title":148,"searchDepth":29,"depth":29,"links":2486},[],{},"\u002Fpython\u002Fexceptions\u002Fcustom-exceptions",[2490,2494,2498,2502,2506,2510,2514,2518,2522,2526,2530,2534,2538,2542,2546,2550],{"id":2491,"difficulty":162,"q":2492,"a":2493},"hierarchy","What is the Python exception hierarchy and why shouldn't you catch BaseException?","All exceptions inherit from **`BaseException`** at the root. Directly under it sit a\nfew special ones — **`SystemExit`**, **`KeyboardInterrupt`**, and\n**`GeneratorExit`** — that are **not** meant to be caught by normal error handling.\nEverything you'd normally catch (and every built-in error like `ValueError`)\ndescends from **`Exception`**, which is itself a child of `BaseException`.\n\n```python\n# BaseException\n#  ├── SystemExit\n#  ├── KeyboardInterrupt\n#  └── Exception          \u003C- catch THIS, not BaseException\n#       ├── ValueError\n#       ├── KeyError\n#       └── ...\n```\n\nCatching `BaseException` (or a bare `except:`) traps `KeyboardInterrupt` and\n`SystemExit`, so Ctrl-C and clean shutdowns stop working. **Catch `Exception`**\n(or narrower) and let the system-level ones propagate.\n",{"id":2495,"difficulty":253,"q":2496,"a":2497},"defining-custom","How do you define a custom exception class?","Subclass **`Exception`** (almost never `BaseException`). The simplest custom\nexception needs no body at all — `pass` is enough, since it inherits message\nhandling from `Exception`.\n\n```python\nclass ConfigError(Exception):\n    \"\"\"Raised when configuration is invalid.\"\"\"\n\nraise ConfigError(\"missing 'port' key\")\n\ntry:\n    ...\nexcept ConfigError as e:\n    print(e)            # missing 'port' key\n```\n\nGive the class a clear, specific name ending in `Error`, and a docstring describing\nwhen it's raised. Even an empty subclass is valuable because it lets callers catch\n*your* error specifically instead of a generic `Exception`.\n",{"id":2499,"difficulty":162,"q":2500,"a":2501},"when-to-subclass","When and why should you create custom exceptions?","Create a custom exception when callers need to **distinguish your error** from\nothers and handle it differently. A common pattern is a single **base exception for\nyour library\u002Fapp**, with specific subclasses beneath it — so users can catch the\nbase to handle \"anything from this library\" or a subclass for fine-grained control.\n\n```python\nclass PaymentError(Exception):\n    \"\"\"Base for all payment problems.\"\"\"\n\nclass CardDeclined(PaymentError): pass\nclass InsufficientFunds(PaymentError): pass\n\ntry:\n    charge(card)\nexcept InsufficientFunds:\n    retry_later()\nexcept PaymentError:          # catches any other payment error\n    alert_support()\n```\n\nDon't invent a custom exception when a built-in already fits — bad input is a\n`ValueError`, a missing key is a `KeyError`. Add your own only when it carries\nmeaning the built-ins can't.\n",{"id":2503,"difficulty":162,"q":2504,"a":2505},"passing-args","How do you pass arguments and messages to an exception?","Arguments passed to an exception are stored in its **`.args`** tuple, and the first\none becomes the string returned by `str(exception)`. To attach **structured data**,\nadd attributes in a custom `__init__` (and call `super().__init__()` so the message\nstill works).\n\n```python\nraise ValueError(\"bad value\", 42)\n# e.args == (\"bad value\", 42)\n\nclass ApiError(Exception):\n    def __init__(self, message, status_code):\n        super().__init__(message)   # sets the message \u002F .args\n        self.status_code = status_code\n\ntry:\n    raise ApiError(\"not found\", 404)\nexcept ApiError as e:\n    print(e, e.status_code)         # not found 404\n```\n\nUse a plain message for simple cases; add attributes when handlers need to inspect\ndetails (like an HTTP status or the offending value) rather than parse the text.\n",{"id":2507,"difficulty":162,"q":2508,"a":2509},"base-catches-subclasses","Why does catching a base exception class also catch its subclasses?","`except` matches via **`isinstance`**, so a handler for a base class catches the\nbase **and every subclass**. That's why `except Exception` catches almost\neverything, and why ordering matters: put **specific subclasses before** their base,\nor the base will intercept them first.\n\n```python\nclass AppError(Exception): pass\nclass NotFound(AppError): pass\n\ntry:\n    raise NotFound(\"user\")\nexcept AppError:            # matches — NotFound IS-A AppError\n    print(\"caught by base\")\n\ntry:\n    raise NotFound(\"user\")\nexcept AppError:            # this runs first...\n    print(\"base\")\nexcept NotFound:            # ...so this is unreachable!\n    print(\"specific\")\n```\n\nOrder `except` clauses **most-specific first**. The same rule is why\n`except Exception` should come last among your handlers.\n",{"id":2511,"difficulty":253,"q":2512,"a":2513},"common-builtins","What are some common built-in exceptions and when are they raised?","Knowing the standard ones lets you catch precisely and raise the *right* error.\n**`ValueError`** — right type, wrong value (`int(\"abc\")`). **`TypeError`** — wrong\ntype entirely (`\"x\" + 1`). **`KeyError`** — missing dict key. **`IndexError`** —\nlist index out of range. **`AttributeError`** — missing attribute. **`KeyError`**\nand **`IndexError`** both subclass **`LookupError`**.\n\n```python\nint(\"abc\")          # ValueError\n\"x\" + 1             # TypeError\n{\"a\": 1}[\"b\"]       # KeyError\n[1, 2][5]           # IndexError\nNone.foo            # AttributeError\n```\n\nRaise the built-in that best describes the problem instead of a generic\n`Exception` — `ValueError` for bad arguments, `TypeError` for wrong types — so\ncallers can handle them idiomatically.\n",{"id":2515,"difficulty":162,"q":2516,"a":2517},"exception-vs-baseexception","Why inherit from Exception and not BaseException?","**`BaseException`** is the root and also the parent of control-flow\nexceptions you should **not** normally catch: `SystemExit`,\n`KeyboardInterrupt`, `GeneratorExit`. Inheriting from **`Exception`** (the base\nfor \"ordinary\" errors) ensures a broad `except Exception` catches your error\nwithout you accidentally interfering with shutdown or Ctrl-C.\n\n```python\nclass MyError(Exception):     # correct base\n    pass\n\ntry:\n    risky()\nexcept Exception:             # catches MyError, not KeyboardInterrupt\n    handle()\n```\n\nRule of thumb: always derive custom exceptions from `Exception` (or a more\nspecific subclass), never from `BaseException`.\n",{"id":2519,"difficulty":162,"q":2520,"a":2521},"exception-args","How does the args attribute and str() of an exception work?","`Exception.__init__` stores its positional arguments in **`.args`**, and\n`str(exc)` renders them (a single arg shows as-is). Overriding `__str__` lets\nyou control the message; calling `super().__init__(msg)` keeps `.args`\npopulated for tooling.\n\n```python\ne = ValueError(\"bad\", 42)\ne.args            # ('bad', 42)\nstr(e)            # \"('bad', 42)\"\n\nclass HTTPError(Exception):\n    def __init__(self, status):\n        super().__init__(f\"HTTP {status}\")\n        self.status = status\n```\n\nRule of thumb: pass the message to `super().__init__` and add custom\nattributes for structured data callers can inspect.\n",{"id":2523,"difficulty":150,"q":2524,"a":2525},"exception-group","What are ExceptionGroup and except* (3.11+)?","**`ExceptionGroup`** bundles **multiple exceptions** raised together (e.g.\nfrom concurrent tasks), and **`except*`** matches and handles **subsets** of\nthem by type, leaving the rest to propagate. It solves \"many things failed at\nonce\" that a single `except` can't express.\n\n```python\ntry:\n    raise ExceptionGroup(\"fails\", [ValueError(\"a\"), TypeError(\"b\")])\nexcept* ValueError as eg:\n    print(\"values:\", eg.exceptions)\nexcept* TypeError as eg:\n    print(\"types:\", eg.exceptions)\n```\n\nRule of thumb: use ExceptionGroup\u002F`except*` for concurrent or batched\noperations where several independent errors can surface simultaneously.\n",{"id":2527,"difficulty":162,"q":2528,"a":2529},"catch-specific","Why catch specific exceptions rather than broad ones?","A narrow `except` only handles errors you actually anticipate, letting\nunexpected ones surface instead of being silently mishandled. Catching\n`Exception` (or worse, bare `except`) can **mask bugs**, typos\n(`NameError`), and `KeyboardInterrupt`, making failures hard to diagnose.\n\n```python\ntry:\n    value = data[key]\nexcept KeyError:                 # only the expected case\n    value = default\n# not: except Exception -> would also hide a real bug above\n```\n\nRule of thumb: catch the **most specific** exception that you can genuinely\nrecover from; let everything else propagate.\n",{"id":2531,"difficulty":162,"q":2532,"a":2533},"reraise-preserve-traceback","How do you re-raise an exception without losing the traceback?","Use a **bare `raise`** inside the `except` block — it re-raises the **current**\nexception with its original traceback. Writing `raise e` works but can reset\ncontext; `raise ... from e` deliberately chains a **new** exception to the\ncause.\n\n```python\ntry:\n    work()\nexcept IOError:\n    log.error(\"io failed\")\n    raise                       # re-raises with original traceback\n\nexcept ValueError as e:\n    raise ConfigError(\"bad config\") from e   # chained cause\n```\n\nRule of thumb: bare `raise` to propagate as-is; `raise New from e` when you\nwrap it in a more meaningful exception.\n",{"id":2535,"difficulty":162,"q":2536,"a":2537},"exception-with-cleanup-pattern","What is the EAFP pattern with exceptions?","**EAFP** — \"Easier to Ask Forgiveness than Permission\" — means **try the\noperation and catch the failure** rather than pre-checking. It's idiomatic\nPython, avoids race conditions (the state can change between check and use),\nand is often faster on the success path.\n\n```python\n# EAFP (preferred)\ntry:\n    return cache[key]\nexcept KeyError:\n    return compute(key)\n\n# LBYL (race-prone, more code)\nif key in cache: return cache[key]\n```\n\nRule of thumb: prefer try\u002Fexcept (EAFP) over look-before-you-leap checks for\noperations that usually succeed.\n",{"id":2539,"difficulty":162,"q":2540,"a":2541},"custom-exception-attributes","How do you attach structured data to a custom exception?","Store extra fields on the instance in `__init__`, then handlers can read them\nto react programmatically (retry, format a response, log details) instead of\nparsing a message string.\n\n```python\nclass APIError(Exception):\n    def __init__(self, message, status, payload=None):\n        super().__init__(message)\n        self.status = status\n        self.payload = payload\n\ntry:\n    call()\nexcept APIError as e:\n    if e.status == 429:\n        retry_later(e.payload)\n```\n\nRule of thumb: put machine-readable data (codes, IDs) on attributes; keep the\nmessage human-readable.\n",{"id":2543,"difficulty":162,"q":2544,"a":2545},"exception-hierarchy-design","How do you design an exception hierarchy for a library?","Define one **base exception** for your package and derive specific errors from\nit. Callers can then catch the **base** to handle \"anything from this library\"\nor a **specific subclass** for fine-grained handling — without depending on\nmessage text.\n\n```python\nclass LibError(Exception): ...\nclass NotFound(LibError): ...\nclass RateLimited(LibError): ...\n\ntry:\n    client.get(...)\nexcept RateLimited:\n    backoff()\nexcept LibError:               # catch-all for the library\n    report()\n```\n\nRule of thumb: give every library a common base exception so users can catch\nbroadly or narrowly as they choose.\n",{"id":2547,"difficulty":162,"q":2548,"a":2549},"finally-cleanup-guarantee","When does finally NOT run?","`finally` runs on normal exit, exceptions, `return`, `break`, and `continue`.\nIt is skipped only in rare hard-stop cases: the process is **killed**\n(`os._exit`, `SIGKILL`), the interpreter **crashes**, or an **infinite loop \u002F\ndeadlock** never reaches it. `sys.exit()` (which raises `SystemExit`) still\nruns `finally`.\n\n```python\ntry:\n    sys.exit(1)\nfinally:\n    print(\"still runs\")        # SystemExit is a normal exception here\n```\n\nRule of thumb: treat `finally` as guaranteed for cleanup except under\n`os._exit`\u002FSIGKILL or a true crash.\n",{"id":2551,"difficulty":162,"q":2552,"a":2553},"warnings-vs-exceptions","When do you use warnings instead of exceptions?","Raise an **exception** when execution **cannot sensibly continue**. Emit a\n**`warnings.warn(...)`** for non-fatal advisories — deprecations, suspicious\nbut recoverable conditions — that the program can keep running past. Warnings\nare filterable and can be escalated to errors in tests.\n\n```python\nimport warnings\ndef old_api():\n    warnings.warn(\"use new_api()\", DeprecationWarning, stacklevel=2)\n    ...\n```\n\nRule of thumb: exception = \"stop, this failed\"; warning = \"heads up, but\ncarrying on\".\n",{"description":148},"Python interview questions on the exception hierarchy, BaseException vs Exception, defining custom exception classes, passing messages, and how catching a base class catches its subclasses.","python\u002Fexceptions\u002Fcustom-exceptions","Custom Exceptions & the Hierarchy","Z6dI4gk9r3T7-b-09ixyrdl2sPsFt5dM77h5MQVE5Ac",{"id":2560,"title":2561,"body":2562,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":2566,"navigation":153,"order":11,"path":2567,"questions":2568,"questionsCount":2637,"related":218,"seo":2638,"seoDescription":2639,"stem":2640,"subtopic":2641,"topic":72,"topicSlug":74,"updated":223,"__hash__":2642},"qa\u002Fpython\u002Ffunctional\u002Fitertools.md","Itertools",{"type":145,"value":2563,"toc":2564},[],{"title":148,"searchDepth":29,"depth":29,"links":2565},[],{},"\u002Fpython\u002Ffunctional\u002Fitertools",[2569,2573,2577,2581,2585,2589,2593,2597,2601,2605,2609,2613,2617,2621,2625,2629,2633],{"id":2570,"difficulty":162,"q":2571,"a":2572},"infinite-iterators","What do count, cycle, and repeat do?","These are the three **infinite iterators**. **`count(start, step)`** yields an\nendless arithmetic sequence. **`cycle(iterable)`** repeats an iterable's items\nforever. **`repeat(value, times)`** yields the same value endlessly, or `times`\ntimes if given.\n\n```python\nfrom itertools import count, cycle, repeat, islice\n\nlist(islice(count(10, 2), 3))      # [10, 12, 14]\nlist(islice(cycle(\"AB\"), 5))       # ['A', 'B', 'A', 'B', 'A']\nlist(repeat(7, 3))                 # [7, 7, 7]\n```\n\nBecause `count` and `cycle` never stop, you must bound them — with `islice`, `zip`,\nor a `break` — or your loop runs forever. They're ideal for generating ids,\nround-robin assignment, or padding.\n",{"id":2574,"difficulty":253,"q":2575,"a":2576},"chain","What does itertools.chain do?","`chain(*iterables)` lazily **concatenates** multiple iterables into one stream,\nwithout building an intermediate combined list. `chain.from_iterable(iter_of_iters)`\ndoes the same when the iterables come from a single iterable (e.g. a list of lists).\n\n```python\nfrom itertools import chain\n\nlist(chain([1, 2], [3, 4], [5]))        # [1, 2, 3, 4, 5]\n\nrows = [[1, 2], [3, 4], [5, 6]]\nlist(chain.from_iterable(rows))         # [1, 2, 3, 4, 5, 6] — flatten one level\n```\n\nIt's the memory-friendly way to iterate over several sequences as if they were one,\nand the idiomatic one-level flatten.\n",{"id":2578,"difficulty":162,"q":2579,"a":2580},"islice","What is islice and how is it different from regular slicing?","`islice(iterable, stop)` or `islice(iterable, start, stop, step)` slices any\n**iterator lazily** — including infinite ones and generators that don't support\n`[ ]` indexing. Unlike list slicing, it **can't use negative indices** (it can't\nlook backward in a stream) and it **consumes** the underlying iterator.\n\n```python\nfrom itertools import islice, count\n\nlist(islice(count(), 2, 7))      # [2, 3, 4, 5, 6] — works on an infinite source\ngen = (x * x for x in range(10))\nlist(islice(gen, 3))             # [0, 1, 4] — slice a generator\n```\n\nUse `islice` to take a window from a stream without materializing it. For a concrete\nlist where you want negative indices, ordinary `seq[a:b]` is fine.\n",{"id":2582,"difficulty":162,"q":2583,"a":2584},"combinatorics","What do combinations, permutations, and product produce?","These generate **combinatorial** results lazily. **`permutations(it, r)`** —\nordered arrangements (order matters). **`combinations(it, r)`** — unordered\nselections (order doesn't, no repeats). **`product(*its)`** — the Cartesian product\n(nested loops), with `repeat=n` for self-products.\n\n```python\nfrom itertools import permutations, combinations, product\n\nlist(permutations([1, 2, 3], 2))   # (1,2)(1,3)(2,1)(2,3)(3,1)(3,2)\nlist(combinations([1, 2, 3], 2))   # (1,2)(1,3)(2,3)\nlist(product([0, 1], repeat=2))    # (0,0)(0,1)(1,0)(1,1)\n```\n\nCounts grow fast (factorial \u002F exponential), so keep `r` and inputs small or consume\nlazily. `product(a, b)` replaces a nested `for` over two sequences.\n",{"id":2586,"difficulty":150,"q":2587,"a":2588},"groupby","Why does itertools.groupby require sorted input?","`groupby` groups **only consecutive** items that share a key — it does **not** sort\nfirst. So the same key appearing in non-adjacent positions creates **multiple\ngroups**. To get one group per key, **sort by the same key function first**.\n\n```python\nfrom itertools import groupby\n\ndata = [\"apple\", \"avocado\", \"banana\", \"apricot\"]\n# WRONG — not sorted: 'a' group splits because 'banana' is between\nfor k, g in groupby(data, key=lambda s: s[0]):\n    print(k, list(g))      # a [...] , b [...] , a [apricot]\n\ndata.sort(key=lambda s: s[0])      # sort by the SAME key\nfor k, g in groupby(data, key=lambda s: s[0]):\n    print(k, list(g))      # a [...], b [...]  — correct\n```\n\nAlso note each group is a **lazy sub-iterator** that's invalidated when you advance\nto the next group — materialize it with `list()` if you need it later. Always sort\nby the grouping key before `groupby`.\n",{"id":2590,"difficulty":162,"q":2591,"a":2592},"accumulate","What does itertools.accumulate do?","`accumulate(iterable, func=operator.add)` yields **running totals** — each output is\nthe function applied cumulatively, so by default you get a running sum. Pass a\ndifferent binary `func` for running max, product, etc.\n\n```python\nfrom itertools import accumulate\nimport operator\n\nlist(accumulate([1, 2, 3, 4]))                 # [1, 3, 6, 10] — running sum\nlist(accumulate([1, 2, 3, 4], operator.mul))   # [1, 2, 6, 24] — running product\nlist(accumulate([3, 1, 4, 1, 5], max))         # [3, 3, 4, 4, 5] — running max\n```\n\nUnlike `functools.reduce`, which returns only the **final** value, `accumulate`\nyields **every intermediate** result lazily. Use it for prefix sums and similar\nscans.\n",{"id":2594,"difficulty":162,"q":2595,"a":2596},"laziness-memory","What is the main benefit of itertools being lazy?","Every `itertools` function returns a **lazy iterator** that computes items **on\ndemand**, so it never holds the whole sequence in memory. This lets you process\n**huge or infinite** streams in **constant memory**, and chain operations into a\npipeline that only does the work actually consumed.\n\n```python\nfrom itertools import count, islice\n\n# find the first 5 squares over 1000 — from an infinite source\nsquares = (n * n for n in count(1))\nbig = (s for s in squares if s > 1000)\nprint(list(islice(big, 5)))    # computed lazily, nothing materialized\n\nsum(islice(count(1), 1_000_000))   # no million-element list built\n```\n\nThe trade-off is that iterators are **single-pass** and not indexable. Reach for\n`itertools` when streaming or composing transformations over large data;\nmaterialize to a list only when you need random access or multiple passes.\n",{"id":2598,"difficulty":162,"q":2599,"a":2600},"chain-from-iterable","What is itertools.chain.from_iterable for?","It flattens **one level** of a nested iterable lazily — like `chain(*lists)`\nbut without unpacking everything up front, so it works on an **infinite or\nhuge** sequence of iterables. Preferred when the outer iterable is itself\nlazy.\n\n```python\nfrom itertools import chain\nrows = [[1, 2], [3], [4, 5]]\nlist(chain.from_iterable(rows))      # [1, 2, 3, 4, 5]\n# streams without building the arg list that chain(*rows) needs\n```\n\nRule of thumb: use `chain.from_iterable` to flatten a stream of iterables;\n`chain(a, b, c)` when you have a few named ones.\n",{"id":2602,"difficulty":150,"q":2603,"a":2604},"tee","What does itertools.tee do and what's its catch?","**`tee(it, n)`** splits one iterator into `n` independent iterators. The\ncatch: it **buffers** items consumed by one branch until the others catch up,\nso if branches advance at very different rates it can use **lots of memory**.\nAlso, **don't use the original iterator** after teeing it.\n\n```python\nfrom itertools import tee\na, b = tee(source)\nnext(a)                  # b still starts from the beginning (buffered)\n```\n\nRule of thumb: `tee` is great for a couple of roughly-in-step passes; if one\nbranch lags far behind, just materialize to a list.\n",{"id":2606,"difficulty":162,"q":2607,"a":2608},"zip-longest","How does itertools.zip_longest differ from zip?","`zip` stops at the **shortest** iterable; **`zip_longest`** continues to the\n**longest**, filling missing values with **`fillvalue`** (default `None`).\nUse it when you must process every element of unequal-length iterables.\n\n```python\nfrom itertools import zip_longest\nlist(zip(\"abc\", [1, 2]))                       # [('a',1),('b',2)]\nlist(zip_longest(\"abc\", [1, 2], fillvalue=0))  # [('a',1),('b',2),('c',0)]\n```\n\nRule of thumb: `zip` to align equal-length data (or deliberately truncate);\n`zip_longest` when you can't afford to drop the tail.\n",{"id":2610,"difficulty":162,"q":2611,"a":2612},"takewhile-dropwhile","What do takewhile and dropwhile do?","**`takewhile(pred, it)`** yields items **until** the predicate first fails,\nthen stops. **`dropwhile(pred, it)`** **skips** leading items while the\npredicate holds, then yields **everything after**. They split a stream at the\nfirst boundary — unlike `filter`, which tests every element.\n\n```python\nfrom itertools import takewhile, dropwhile\ndata = [1, 2, 3, 1, 0]\nlist(takewhile(lambda x: x \u003C 3, data))   # [1, 2]\nlist(dropwhile(lambda x: x \u003C 3, data))   # [3, 1, 0]\n```\n\nRule of thumb: use these for \"stop\u002Fskip at the first transition\" on sorted or\nprefixed data; use `filter` to test each element independently.\n",{"id":2614,"difficulty":162,"q":2615,"a":2616},"starmap","When do you use itertools.starmap instead of map?","Use **`starmap`** when your iterable holds **pre-grouped argument tuples** —\nit calls `f(*args)` for each. `map` passes each item as a single argument, so\nit can't unpack tuples into multiple parameters.\n\n```python\nfrom itertools import starmap\npairs = [(2, 3), (4, 5)]\nlist(starmap(pow, pairs))        # [8, 1024]  -> pow(2,3), pow(4,5)\n# map(pow, pairs) would fail: pow gets one tuple arg\n```\n\nRule of thumb: `map(f, items)` for one-arg calls; `starmap(f, tuples)` when\neach element is already an argument tuple.\n",{"id":2618,"difficulty":253,"q":2619,"a":2620},"filterfalse","What is itertools.filterfalse?","The **complement of `filter`**: it keeps items for which the predicate is\n**false**. It saves writing `filter(lambda x: not pred(x), it)` and reads more\nclearly for \"everything that doesn't match\".\n\n```python\nfrom itertools import filterfalse\nnums = range(6)\nlist(filterfalse(lambda x: x % 2, nums))   # [0, 2, 4]  -> the evens\n```\n\nRule of thumb: use `filterfalse` for the \"reject matching\" case instead of\nnegating a predicate inside `filter`.\n",{"id":2622,"difficulty":162,"q":2623,"a":2624},"pairwise","What does itertools.pairwise do?","**`pairwise(it)`** (3.10+) yields **consecutive overlapping pairs**:\n`(s0,s1), (s1,s2), ...`. It's the clean way to compare each element with its\nneighbor — for diffs, deltas, or detecting transitions — without manual\nindexing.\n\n```python\nfrom itertools import pairwise\nlist(pairwise([1, 4, 9]))            # [(1, 4), (4, 9)]\ndeltas = [b - a for a, b in pairwise(readings)]\n```\n\nRule of thumb: use `pairwise` for neighbor comparisons instead of\n`zip(seq, seq[1:])` or index math.\n",{"id":2626,"difficulty":253,"q":2627,"a":2628},"count-step","How is itertools.count different from range?","**`count(start, step)`** is an **infinite** counter (and supports float\nsteps), while `range` is **finite** and integer-only. `count` is handy as an\nendless id generator or paired with `zip`\u002F`islice` to number a stream.\n\n```python\nfrom itertools import count, islice\nlist(islice(count(10, 5), 3))        # [10, 15, 20]\nfor i, item in zip(count(1), stream): ...   # 1-based numbering\n```\n\nRule of thumb: use `count` for unbounded or float sequences (bounded with\n`islice`); `range` for ordinary finite integer loops.\n",{"id":2630,"difficulty":162,"q":2631,"a":2632},"groupby-pattern","How do you correctly consume itertools.groupby output?","`groupby` yields `(key, group)` where **`group` is a lazy sub-iterator** tied\nto the underlying stream — advancing to the next group **invalidates** the\nprevious one. Materialize each group (e.g. `list(group)`) before moving on,\nand remember to **sort by the same key first**.\n\n```python\nfrom itertools import groupby\ndata = sorted(items, key=keyfn)\nfor key, group in groupby(data, key=keyfn):\n    members = list(group)            # consume before next iteration\n```\n\nRule of thumb: sort by the grouping key first, and `list()` each group inside\nthe loop before advancing.\n",{"id":2634,"difficulty":162,"q":2635,"a":2636},"islice-step","Can islice do negative indices or steps?","**No** — `islice(it, start, stop, step)` accepts only **non-negative**\nvalues and **can't** use negative indices\u002Fsteps (it can't look backward in a\none-pass iterator). It consumes and discards skipped items. For negative\nindexing you must materialize to a list and slice normally.\n\n```python\nfrom itertools import islice\nlist(islice(range(10), 2, 8, 2))     # [2, 4, 6]\nislice(range(10), -1)                # ValueError\n```\n\nRule of thumb: `islice` for forward, non-negative lazy slicing; convert to a\nlist when you need negative indices or steps.\n",17,{"description":148},"Python interview questions on itertools — count\u002Fcycle\u002Frepeat, chain, islice, combinations\u002Fpermutations\u002Fproduct, groupby's sorted-input rule, accumulate, and the memory benefit of lazy iterators.","python\u002Ffunctional\u002Fitertools","itertools","q6wyK-Coe-c3XdeKub6O4MoScgoMn-cjeQc_nDrd-QQ",{"id":2644,"title":2645,"body":2646,"description":148,"difficulty":150,"extension":151,"framework":10,"frameworkSlug":8,"meta":2650,"navigation":153,"order":11,"path":2651,"questions":2652,"questionsCount":217,"related":218,"seo":2711,"seoDescription":2712,"stem":2713,"subtopic":2714,"topic":45,"topicSlug":47,"updated":223,"__hash__":2715},"qa\u002Fpython\u002Ffunctions\u002Fclosures.md","Closures",{"type":145,"value":2647,"toc":2648},[],{"title":148,"searchDepth":29,"depth":29,"links":2649},[],{},"\u002Fpython\u002Ffunctions\u002Fclosures",[2653,2657,2661,2664,2668,2672,2676,2680,2683,2687,2691,2695,2699,2703,2707],{"id":2654,"difficulty":150,"q":2655,"a":2656},"what-is-a-closure","What is a closure and what are free variables?","A **closure** is a nested function that **remembers variables from its\nenclosing scope** even after that outer function has returned. The\nremembered names are called **free variables** — they're neither local\nparameters nor globals. Python stores them on the function's `__closure__`\nattribute.\n\n```python\ndef multiplier(factor):\n    def multiply(n):\n        return n * factor      # 'factor' is a free variable\n    return multiply\n\ndouble = multiplier(2)\ndouble(5)                      # 10\ndouble.__closure__[0].cell_contents   # 2 — captured value\n```\n\nThe inner function keeps the binding alive via a **cell object**, which is\nwhy `multiplier` can return and `double` still works. Closures are how\nPython functions carry private state without a class.\n",{"id":2658,"difficulty":162,"q":2659,"a":2660},"nonlocal","What does the nonlocal keyword do?","By default, assigning to a name inside a function creates a **new local**.\n**`nonlocal`** tells Python that an assignment should instead **rebind a\nvariable in the nearest enclosing function scope** — letting a closure\nmutate, not just read, the captured variable.\n\n```python\ndef counter():\n    count = 0\n    def increment():\n        nonlocal count        # rebind outer 'count'\n        count += 1\n        return count\n    return increment\n\nc = counter()\nc(); c()                       # 1, then 2\n```\n\nWithout `nonlocal`, `count += 1` would raise `UnboundLocalError` (it reads\nthen assigns a local). Use `nonlocal` for enclosing-function scope and\n`global` for module scope.\n",{"id":2662,"difficulty":150,"q":1678,"a":2663},"late-binding","Closures capture **variables, not values** — this is **late binding**. A\nfunction created in a loop looks up the loop variable when it's *called*, not\nwhen it's defined, so every closure sees the variable's **final** value.\n\n```python\nfuncs = [lambda: i for i in range(3)]\n[f() for f in funcs]           # [2, 2, 2]  — all see final i\n\n# fix: bind the current value via a default argument\nfuncs = [lambda i=i: i for i in range(3)]\n[f() for f in funcs]           # [0, 1, 2]\n```\n\nThe default-argument trick works because **defaults are evaluated at\ndefinition time**, snapshotting `i` per iteration. A factory function that\ntakes `i` as a parameter achieves the same. This is a favorite interview\ngotcha.\n",{"id":2665,"difficulty":150,"q":2666,"a":2667},"closure-cell","How can you inspect a closure's captured variables?","A function with free variables has a non-`None` **`__closure__`** — a tuple\nof **cell** objects, each holding one captured binding accessible via\n`cell_contents`. The matching names are listed in\n`__code__.co_freevars`. Functions with no closure have `__closure__ is\nNone`.\n\n```python\ndef make(x, y):\n    def inner():\n        return x + y\n    return inner\n\nf = make(3, 4)\nf.__code__.co_freevars               # ('x', 'y')\n[c.cell_contents for c in f.__closure__]   # [3, 4]\n```\n\nThis is mostly useful for debugging or teaching how closures actually\nstore state. The cells are shared live, so `nonlocal` rebinds are visible\nthrough `cell_contents`.\n",{"id":2669,"difficulty":162,"q":2670,"a":2671},"closures-vs-classes","When should you use a closure instead of a class?","Both bundle **behavior with state**. A **closure** is lighter and ideal\nwhen you need a *single* method and a little hidden state. A **class** wins\nwhen you need multiple methods, inheritance, or explicit, inspectable\nstate. Common closure uses include **factories, decorators, and callbacks**.\n\n```python\n# closure: tiny stateful function\ndef make_adder(n):\n    return lambda x: x + n\nadd10 = make_adder(10)\n\n# class: equivalent but heavier\nclass Adder:\n    def __init__(self, n): self.n = n\n    def __call__(self, x): return x + self.n\n```\n\nRule of thumb: one behavior + private state → closure; many behaviors or\nshared interface → class. Decorators are the canonical real-world closure.\n",{"id":2673,"difficulty":162,"q":2674,"a":2675},"closure-loop-fix","How do you fix closures in a loop capturing the same value?","The closures all reference the **same variable**, which holds its **final**\nvalue after the loop. Capture the current value with a **default argument**\n(bound at definition) or `functools.partial`.\n\n```python\nfns = [lambda: i for i in range(3)]\n[f() for f in fns]                   # [2, 2, 2]  -> all see final i\n\nfns = [lambda i=i: i for i in range(3)]\n[f() for f in fns]                   # [0, 1, 2]  -> default binds now\n```\n\nRule of thumb: bind the loop variable as a default argument (`x=x`) to capture\nits value at each iteration.\n",{"id":2677,"difficulty":162,"q":2678,"a":2679},"closure-vs-global","Why prefer a closure over a global variable for shared state?","A closure keeps state **encapsulated** in the enclosing scope — invisible to\nand unmodifiable by unrelated code — whereas a global is shared mutable state\nanyone can clobber. Each closure instance also gets its **own independent\nstate**.\n\n```python\ndef counter():\n    n = 0\n    def inc():\n        nonlocal n\n        n += 1\n        return n\n    return inc\n\na, b = counter(), counter()\na(); a(); b()            # a -> 2, b -> 1  (separate state)\n```\n\nRule of thumb: use closures to encapsulate private, per-instance state instead\nof leaking it into globals.\n",{"id":2662,"difficulty":150,"q":2681,"a":2682},"What does \"closures capture variables, not values\" mean?","A closure stores a **reference to the variable**, not a snapshot of its value\n— so it reads the variable's **current** value when **called**, not when\ndefined. This **late binding** is why loop closures surprise people and why\nmutating an enclosed variable later affects all closures over it.\n\n```python\nx = 10\nf = lambda: x\nx = 20\nf()                      # 20  -> reads x at call time\n```\n\nRule of thumb: closures see live variables; bind a value explicitly (default\narg) if you need it frozen.\n",{"id":2684,"difficulty":150,"q":2685,"a":2686},"cell-objects","How does Python implement closures under the hood?","Enclosed variables become **cell objects** shared between the outer and inner\nfunction. The inner function's **`__closure__`** is a tuple of these cells, and\nits code lists them in **`__code__.co_freevars`**. Reading\u002Fwriting the free\nvariable goes through the cell, which is how state stays shared and live.\n\n```python\ndef outer():\n    x = 1\n    def inner(): return x\n    return inner\n\nf = outer()\nf.__closure__[0].cell_contents       # 1\nf.__code__.co_freevars               # ('x',)\n```\n\nRule of thumb: free variables are stored in cells; inspect `__closure__` to\nsee what a function captured.\n",{"id":2688,"difficulty":162,"q":2689,"a":2690},"nonlocal-vs-global","What is the difference between nonlocal and global?","**`nonlocal`** rebinds a name in the **nearest enclosing function** scope;\n**`global`** rebinds a name at **module** scope. Without either, an assignment\ninside a function creates a **new local**, shadowing the outer name.\n\n```python\nx = \"module\"\ndef outer():\n    x = \"enclosing\"\n    def inner():\n        nonlocal x; x = \"changed\"   # affects outer's x\n        # global x  -> would affect the module-level x\n    inner()\n    return x                        # \"changed\"\n```\n\nRule of thumb: `nonlocal` for enclosing-function state, `global` for\nmodule-level state; assignment alone always makes a local.\n",{"id":2692,"difficulty":162,"q":2693,"a":2694},"closure-memoization","How do you build a simple memoizer with a closure?","Capture a **cache dict** in the enclosing scope; the inner function reads and\nwrites it across calls. This is the manual version of `functools.lru_cache`\nand a classic closure use.\n\n```python\ndef memoize(fn):\n    cache = {}\n    def wrapper(*args):\n        if args not in cache:\n            cache[args] = fn(*args)\n        return cache[args]\n    return wrapper\n\nslow = memoize(slow)\n```\n\nRule of thumb: a closure over a `cache` dict gives per-function memoization;\nreach for `lru_cache` for the battle-tested version.\n",{"id":2696,"difficulty":162,"q":2697,"a":2698},"closure-factory","What is a function factory?","A function that **returns a customized function**, with configuration captured\nin a closure. It lets you generate specialized functions from parameters\nwithout repeating code.\n\n```python\ndef power_of(exp):\n    def raise_(base):\n        return base ** exp\n    return raise_\n\nsquare, cube = power_of(2), power_of(3)\nsquare(5), cube(2)       # 25, 8\n```\n\nRule of thumb: use a factory closure to stamp out related functions\nparameterized by captured values.\n",{"id":2700,"difficulty":150,"q":2701,"a":2702},"closure-gotcha-rebind","Why does assigning to an enclosed variable without nonlocal raise UnboundLocalError?","Any **assignment** to a name inside a function makes it **local for the whole\nfunction body**, so reading it before the assignment — even to do `n += 1` —\nfails with `UnboundLocalError`. Declare it `nonlocal` (or `global`) to rebind\nthe outer one instead.\n\n```python\ndef counter():\n    n = 0\n    def inc():\n        n += 1           # UnboundLocalError: n treated as local\n        return n\n    return inc\n# fix: add `nonlocal n` at the top of inc\n```\n\nRule of thumb: to modify (not just read) an enclosing variable, you must\ndeclare it `nonlocal`.\n",{"id":2704,"difficulty":162,"q":2705,"a":2706},"closures-keep-alive","Do closures keep captured objects alive?","**Yes** — as long as the closure exists, the cells holding its free variables\nkeep those objects **referenced and uncollectable**. A long-lived closure\ncapturing a large object (or `self`) can therefore cause a **memory leak** if\nyou forget about it.\n\n```python\ndef make():\n    big = load_huge_data()\n    return lambda: len(big)      # `big` stays alive via the closure\n\nf = make()                       # huge data retained until f is dropped\n```\n\nRule of thumb: be mindful that closures pin their captured objects in memory\nfor as long as the closure lives.\n",{"id":2708,"difficulty":162,"q":2709,"a":2710},"closure-vs-default-arg","What's the difference between capturing via closure and via default argument?","A **closure** reads the variable **lazily at call time** (late binding); a\n**default argument** snapshots the value **eagerly at definition time** (early\nbinding). That distinction is exactly what fixes the loop-closure bug.\n\n```python\nx = 1\nlate  = lambda: x          # reads x when called\nearly = lambda x=x: x      # froze x = 1 at definition\nx = 99\nlate(), early()            # (99, 1)\n```\n\nRule of thumb: closure = live\u002Flate, default arg = frozen\u002Fearly — choose based\non whether you want the current or captured value.\n",{"description":148},"Python interview questions on closures and free variables, __closure__, the nonlocal keyword, late binding in loops and the default-argument fix, closures vs classes, and common closure uses.","python\u002Ffunctions\u002Fclosures","Closures & Scope","dycnafnvDGzWAqeFg0b1bxoQq3JviMM-t8FhmOZPOIc",{"id":2717,"title":2718,"body":2719,"description":148,"difficulty":253,"extension":151,"framework":10,"frameworkSlug":8,"meta":2723,"navigation":153,"order":11,"path":2724,"questions":2725,"questionsCount":1346,"related":218,"seo":2790,"seoDescription":2791,"stem":2792,"subtopic":2793,"topic":20,"topicSlug":21,"updated":223,"__hash__":2794},"qa\u002Fpython\u002Ffundamentals\u002Fnumbers-operators.md","Numbers Operators",{"type":145,"value":2720,"toc":2721},[],{"title":148,"searchDepth":29,"depth":29,"links":2722},[],{},"\u002Fpython\u002Ffundamentals\u002Fnumbers-operators",[2726,2730,2734,2738,2742,2746,2750,2754,2758,2762,2766,2770,2774,2778,2782,2786],{"id":2727,"difficulty":253,"q":2728,"a":2729},"numeric-types","What are Python's built-in numeric types?","Three: **`int`** (whole numbers, unlimited size), **`float`** (double-precision\nbinary floating point), and **`complex`** (a real + imaginary part written with\n`j`). `bool` is technically a subclass of `int` (`True == 1`).\n\n```python\na = 42          # int\nb = 3.14        # float\nc = 2 + 3j      # complex\nc.real, c.imag  # (2.0, 3.0)\nTrue + True     # 2  — bool is an int subclass\n```\n\nWhy it matters: mixing types **promotes** to the wider one (`int + float -> float`),\nand knowing the three types explains conversion and precision behavior.\n",{"id":2731,"difficulty":162,"q":2732,"a":2733},"division-floor-modulo","How do `\u002F`, `\u002F\u002F`, and `%` behave, especially with negatives?","`\u002F` is **true division** and always returns a `float`. `\u002F\u002F` is **floor division**\n— it rounds **toward negative infinity**, not toward zero. `%` is the matching\nmodulo, and in Python its **result takes the sign of the divisor**.\n\n```python\n7 \u002F 2       # 3.5    — always float\n7 \u002F\u002F 2      # 3\n-7 \u002F\u002F 2     # -4     — floors toward -infinity, not -3\n-7 % 2      # 1      — sign follows the divisor (2)\n7 % -2      # -1     — sign follows the divisor (-2)\n```\n\nThe identity always holds: `(a \u002F\u002F b) * b + (a % b) == a`. Rule of thumb: Python's\nfloor\u002Fmodulo differ from C\u002FJava for negatives — expect non-negative `%` when the\ndivisor is positive.\n",{"id":2735,"difficulty":253,"q":2736,"a":2737},"int-arbitrary-precision","Why don't Python integers overflow?","Python `int` has **arbitrary precision** — it grows to hold any value, limited only\nby available memory. There is no fixed 32\u002F64-bit width, so computations never\nsilently wrap around like in C or Java.\n\n```python\n2 ** 100        # 1267650600228229401496703205376\nx = 10 ** 1000  # a 1001-digit integer — no overflow\nimport sys\nsys.maxsize     # largest \"native\" int, but ints can exceed it freely\n```\n\nWhy it matters: you can compute huge factorials or cryptographic numbers directly,\nbut very large ints cost more memory and arithmetic gets slower. Rule of thumb:\ninteger overflow is simply not a concern in Python.\n",{"id":2739,"difficulty":162,"q":2740,"a":2741},"float-precision-decimal","Why is 0.1 + 0.2 not exactly 0.3?","Floats are stored in **binary (IEEE 754)**, and values like 0.1 and 0.2 have no\nexact binary representation — so tiny rounding errors accumulate. This is inherent\nto binary floating point, not a Python bug.\n\n```python\n0.1 + 0.2            # 0.30000000000000004\n0.1 + 0.2 == 0.3     # False\nround(0.1 + 0.2, 2)  # 0.3   — round for display\nimport math\nmath.isclose(0.1 + 0.2, 0.3)   # True — tolerant comparison\n\nfrom decimal import Decimal\nDecimal(\"0.1\") + Decimal(\"0.2\")   # Decimal('0.3')  — exact\n```\n\nRule of thumb: never compare floats with `==`; use `math.isclose` or round, and\nreach for **`Decimal`** when you need exact decimal arithmetic (e.g. money).\n",{"id":2743,"difficulty":162,"q":2744,"a":2745},"bitwise-operators","What are Python's bitwise operators?","Bitwise operators work on the binary representation of integers: **`&`** (and),\n**`|`** (or), **`^`** (xor), **`~`** (not\u002Finvert), **`\u003C\u003C`** (left shift), and\n**`>>`** (right shift). Shifting left by `n` multiplies by `2**n`.\n\n```python\n5 & 3    # 1   (0b101 & 0b011)\n5 | 3    # 7   (0b111)\n5 ^ 3    # 6   (0b110)\n~5       # -6  (~x == -(x+1))\n1 \u003C\u003C 4   # 16  (1 * 2**4)\n20 >> 2  # 5   (20 \u002F\u002F 4)\n```\n\nWhy it matters: bitwise ops power flags\u002Fbitmasks, fast power-of-two math, and\nlow-level protocols. Rule of thumb: `~x` equals `-(x + 1)` because of two's\ncomplement.\n",{"id":2747,"difficulty":253,"q":2748,"a":2749},"divmod-and-power","What do `divmod` and `**` do?","**`divmod(a, b)`** returns the quotient and remainder as a single tuple\n`(a \u002F\u002F b, a % b)` in one call. **`**`** is exponentiation; with a third argument,\nthe built-in `pow(base, exp, mod)` does efficient modular exponentiation.\n\n```python\ndivmod(17, 5)     # (3, 2)  — quotient and remainder together\n2 ** 10           # 1024\npow(2, 10)        # 1024  — same as **\npow(2, 10, 1000)  # 24    — (2**10) % 1000, computed efficiently\n```\n\nRule of thumb: use `divmod` when you need both results (e.g. converting seconds to\nminutes\u002Fseconds), and `pow(a, b, m)` for modular math instead of `(a ** b) % m`.\n",{"id":2751,"difficulty":253,"q":2752,"a":2753},"bool-is-int","How does `bool` relate to `int` in arithmetic?","`bool` is a **subclass of `int`**, so `True` behaves as `1` and `False` as `0` in\nany numeric context. This lets you sum booleans to count truthy items, but it can\nalso produce surprising results when bools sneak into math.\n\n```python\nTrue + True            # 2\nsum([True, False, True])   # 2  — counts the Trues\nisinstance(True, int)  # True\n[\"a\", \"b\"][True]       # \"b\"  — True indexes as 1\n```\n\nRule of thumb: `sum(condition for x in data)` is an idiomatic way to count matches,\nbut never rely on bool\u002Fint interchange where it harms readability.\n",{"id":2755,"difficulty":253,"q":2756,"a":2757},"int-float-conversion","How do `int()`, `float()`, and `round()` differ in converting numbers?","`int()` **truncates toward zero** (drops the fractional part). `round()` does\n**banker's rounding** (round-half-to-even). `float()` just widens to a float. They\nare not interchangeable for negatives or `.5` cases.\n\n```python\nint(2.9)      # 2    — truncates, no rounding\nint(-2.9)     # -2   — toward zero\nround(2.5)    # 2    — half to even\nround(3.5)    # 4    — half to even\nround(2.675, 2)  # 2.67 — float repr bites here\n```\n\nRule of thumb: `int()` truncates, `round()` rounds-half-to-even; for predictable\ndecimal rounding use `Decimal`.\n",{"id":2759,"difficulty":162,"q":2760,"a":2761},"chained-comparisons","How do chained comparisons like `a \u003C b \u003C c` work?","Python **chains** comparisons: `a \u003C b \u003C c` is evaluated as `a \u003C b and b \u003C c`, with\n`b` evaluated **once**. Each operator is independent, so unusual chains are legal\n(and sometimes confusing).\n\n```python\n1 \u003C 2 \u003C 3        # True  — like (1 \u003C 2) and (2 \u003C 3)\n5 \u003C 10 > 3       # True  — legal but unusual\nx = 5\n0 \u003C= x \u003C= 10     # idiomatic range check\n```\n\nRule of thumb: use chaining for readable range checks (`lo \u003C= x \u003C= hi`); avoid\nmixing operator directions which obscures intent.\n",{"id":2763,"difficulty":162,"q":2764,"a":2765},"augmented-assignment-numbers","Is `x += 1` the same as `x = x + 1`?","For **immutable** numbers, yes in effect — both rebind `x` to a new object (ints are\nimmutable, so `+=` cannot mutate in place). The distinction matters for mutable\ntypes, but numbers always produce a fresh object.\n\n```python\nx = 5\nid_before = id(x)\nx += 1\nid(x) == id_before   # False — new int object\n\n# contrast with a list, where += mutates in place:\nlst = [1]; before = id(lst); lst += [2]; id(lst) == before  # True\n```\n\nRule of thumb: `+=` on numbers\u002Fstrings\u002Ftuples rebinds; on lists\u002Fsets\u002Fdicts it\nmutates in place. The operator's meaning depends on the type's `__iadd__`.\n",{"id":2767,"difficulty":253,"q":2768,"a":2769},"math-vs-operators","When should you use the `math` module instead of operators?","Use `math` for functions operators don't provide and for **correctness on floats**:\n`math.sqrt`, `math.floor`\u002F`ceil` (return ints), `math.isnan`\u002F`isinf`, `math.gcd`,\nand `math.isclose`. Note `math` functions work on floats, not complex numbers.\n\n```python\nimport math\nmath.floor(-2.5)   # -3   — returns int, toward -inf\nmath.ceil(2.1)     # 3\nmath.gcd(12, 18)   # 6\nmath.isclose(0.1 + 0.2, 0.3)  # True\nmath.sqrt(2)       # 1.4142135623730951\n```\n\nRule of thumb: reach for `math` for floor\u002Fceil-as-int, gcd, and float-safe checks;\nuse `cmath` when you need complex-number math.\n",{"id":2771,"difficulty":150,"q":2772,"a":2773},"nan-behavior","What is special about `float('nan')`?","`nan` (\"not a number\") is **never equal to anything, including itself**. This breaks\nnaive equality and makes containers behave oddly. Use `math.isnan()` to detect it.\n\n```python\nnan = float('nan')\nnan == nan          # False\nnan != nan          # True  — the canonical nan test\nimport math\nmath.isnan(nan)     # True\n\nnan in [nan]        # True! — `in` uses identity-then-equality\nsorted([3, nan, 1]) # unreliable order — nan breaks comparisons\n```\n\nRule of thumb: detect nan with `math.isnan`, never `== float('nan')`, and scrub nans\nbefore sorting or deduping.\n",{"id":2775,"difficulty":162,"q":2776,"a":2777},"integer-caching","Why does `a is b` sometimes work for equal integers?","CPython **caches small integers** from **-5 to 256** as singletons, so identical\nsmall ints share one object and `is` happens to return `True`. This is an\nimplementation detail — never use `is` to compare numeric values.\n\n```python\na = 256; b = 256\na is b        # True  — cached\na = 257; b = 257\na is b        # False — not cached (in a script\u002FREPL line)\na == b        # True  — always the right test\n```\n\nRule of thumb: compare numbers with `==`. `is` is for identity (e.g. `is None`),\nand small-int caching is not something to rely on.\n",{"id":2779,"difficulty":253,"q":2780,"a":2781},"complex-numbers-usage","How do you work with complex numbers?","Write the imaginary part with a `j` suffix. Complex numbers support arithmetic and\nhave `.real`, `.imag`, `.conjugate()`, and `abs()` (the magnitude). Use the `cmath`\nmodule for complex-aware math functions.\n\n```python\nz = 3 + 4j\nz.real, z.imag     # (3.0, 4.0)\nabs(z)             # 5.0   — magnitude sqrt(3**2 + 4**2)\nz.conjugate()      # (3-4j)\nimport cmath\ncmath.sqrt(-1)     # 1j\n```\n\nRule of thumb: use `abs(z)` for magnitude and `cmath` (not `math`) for roots\u002Ftrig on\ncomplex values.\n",{"id":2783,"difficulty":162,"q":2784,"a":2785},"number-formatting","How do you format numbers for display?","Use **f-string format specs**: `,` for thousands separators, `.2f` for fixed\ndecimals, `%` for percentages, `e` for scientific, and `b`\u002F`o`\u002F`x` for binary\u002Foctal\u002F\nhex. The spec mini-language keeps formatting in one place.\n\n```python\nn = 1234567.891\nf\"{n:,.2f}\"     # '1,234,567.89'\nf\"{0.0825:.1%}\" # '8.2%'\nf\"{255:#x}\"     # '0xff'\nf\"{42:08b}\"     # '00101010'  — zero-padded binary\nf\"{n:.2e}\"      # '1.23e+06'\n```\n\nRule of thumb: format specs (`{value:,.2f}`) handle separators, padding, and bases\ndeclaratively — avoid manual string surgery.\n",{"id":2787,"difficulty":253,"q":2788,"a":2789},"underscore-numeric-literals","What do underscores in numeric literals do?","Underscores are **digit group separators** for readability — they're ignored by the\nparser. They work in int, float, and other-base literals, letting you write large\nconstants clearly.\n\n```python\nbudget = 1_000_000        # same as 1000000\npi = 3.14_159\nflags = 0b_1010_0001      # grouped binary\nhex_color = 0xFF_FF_FF\n```\n\nRule of thumb: use `_` to group large literals (millions, byte\u002Fnibble boundaries);\nit has zero effect on the value, only on readability.\n",{"description":148},"Python interview questions on int\u002Ffloat\u002Fcomplex, floor division and modulo with negatives, arbitrary precision, float precision, bitwise operators, and divmod.","python\u002Ffundamentals\u002Fnumbers-operators","Numbers & Operators","bgJ9Tts3Y4Lcan_dPJz0_4nXw5Lmak4G1HxjjS-2SmM",{"id":2796,"title":2797,"body":2798,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":2802,"navigation":153,"order":11,"path":2803,"questions":2804,"questionsCount":217,"related":218,"seo":2861,"seoDescription":2862,"stem":2863,"subtopic":2864,"topic":135,"topicSlug":137,"updated":223,"__hash__":2865},"qa\u002Fpython\u002Fidioms\u002Fgotchas.md","Gotchas",{"type":145,"value":2799,"toc":2800},[],{"title":148,"searchDepth":29,"depth":29,"links":2801},[],{},"\u002Fpython\u002Fidioms\u002Fgotchas",[2805,2808,2811,2815,2819,2822,2826,2830,2834,2838,2842,2846,2850,2854,2858],{"id":1611,"difficulty":150,"q":2806,"a":2807},"Why is a mutable default argument a gotcha?","A default argument is **evaluated once when the function is defined**, not on each\ncall. So a mutable default like `[]` or `{}` is **created a single time and shared**\nacross every call that doesn't override it — state leaks between calls.\n\n```python\ndef append_to(item, target=[]):   # the SAME list every call\n    target.append(item)\n    return target\n\nappend_to(1)   # [1]\nappend_to(2)   # [1, 2]  \u003C- surprise!\n\ndef append_to(item, target=None): # the fix: None sentinel\n    if target is None:\n        target = []               # fresh list per call\n    target.append(item)\n    return target\n```\n\nUse **`None` as the default** and create the real object inside the body. This is the\nsingle most famous Python footgun.\n",{"id":1677,"difficulty":150,"q":2809,"a":2810},"What is the late-binding closure gotcha in loops?","Closures in Python capture **variables by reference, not by value**. When you create\nfunctions in a loop, they all close over the **same** loop variable, which holds its\n**final value** by the time they're called.\n\n```python\nfuncs = [lambda: i for i in range(3)]\n[f() for f in funcs]          # [2, 2, 2]  — not [0, 1, 2]!\n\n# fix: bind the current value via a default argument\nfuncs = [lambda i=i: i for i in range(3)]\n[f() for f in funcs]          # [0, 1, 2]\n```\n\nThe default-argument trick captures `i`'s value **at definition time**. (`functools.\npartial` works too.) Remember: closures see the variable's *latest* value, not a\nsnapshot.\n",{"id":2812,"difficulty":162,"q":2813,"a":2814},"modify-while-iterating","What goes wrong when you modify a list while iterating it?","Changing a list's **size during iteration** shifts the internal index, causing\nelements to be **skipped or repeated**. With dicts and sets it's worse — Python\nraises `RuntimeError: dictionary changed size during iteration`.\n\n```python\nnums = [1, 2, 3, 4]\nfor n in nums:\n    if n % 2 == 0:\n        nums.remove(n)        # skips elements — buggy\nprint(nums)                   # [1, 3] for some inputs, wrong for others\n\nnums = [n for n in nums if n % 2]   # build a new list instead\n```\n\nFix it by iterating over a **copy** (`for n in nums[:]`) or, better, building a new\ncollection with a comprehension or `filter`. Never mutate a container's size while\nlooping over it.\n",{"id":2816,"difficulty":162,"q":2817,"a":2818},"is-vs-eq-cached","Why does `is` give surprising results on numbers and strings?","`is` tests **identity** (same object), while `==` tests **value**. CPython **caches**\nsmall integers (−5 to 256) and **interns** some strings, so `is` *coincidentally*\nreturns `True` for those — but fails for values outside the cache, making it look\nunreliable.\n\n```python\na = 256; b = 256\na is b          # True  — cached\n\nc = 1000; d = 1000\nc is d          # often False — not cached\nc == d          # True   — always correct\n\nx = \"hi\"; y = \"hi\"\nx is y          # True (interned) — don't rely on it\n```\n\nCaching is an **implementation detail**, not a guarantee. Rule: use `==` for value\ncomparison; reserve `is` for singletons (`None`, `True`, `False`).\n",{"id":1442,"difficulty":162,"q":2820,"a":2821},"Why is a bare except an anti-pattern?","A bare **`except:`** (or `except Exception:` used carelessly) catches **everything** —\nincluding `KeyboardInterrupt` and `SystemExit` — and **swallows the error silently**,\nhiding bugs and making programs impossible to interrupt or debug.\n\n```python\ntry:\n    risky()\nexcept:                  # catches EVERYTHING, even Ctrl-C\n    pass                 # error vanishes — undebuggable\n\ntry:\n    risky()\nexcept ValueError as e:  # catch only what you expect\n    log.error(\"bad value: %s\", e)\n    raise                # or handle it deliberately\n```\n\nCatch the **specific exceptions** you can actually handle, and avoid `pass` in an\n`except` (at minimum log it). If you must catch broadly, use `except Exception` (not\nbare) so system-exiting signals still propagate.\n",{"id":2823,"difficulty":150,"q":2824,"a":2825},"mutable-class-attr","Why is a mutable class attribute a common bug?","A mutable value assigned **at class level** (outside `__init__`) is **one object\nshared by every instance**. Mutating it through any instance affects **all** of them\n— usually not what you intend.\n\n```python\nclass Cart:\n    items = []                  # SHARED across all instances\n    def add(self, x):\n        self.items.append(x)\n\na, b = Cart(), Cart()\na.add(\"apple\")\nb.items                         # ['apple'] — leaked into b!\n\nclass Cart:\n    def __init__(self):\n        self.items = []         # per-instance — correct\n```\n\nInitialize mutable attributes **inside `__init__`** so each instance gets its own.\nClass-level attributes are fine for **immutable** constants\u002Fdefaults, but never for\nmutable per-instance state.\n",{"id":2827,"difficulty":253,"q":2828,"a":2829},"shadowing-builtins","What is the problem with shadowing builtins?","Naming a variable after a builtin — `list`, `dict`, `id`, `str`, `type`, `sum` —\n**shadows** it in that scope, so the original becomes unusable and you get confusing\nerrors later when you try to call it.\n\n```python\nlist = [1, 2, 3]          # shadows the built-in list type\nother = list((4, 5))      # TypeError: 'list' object is not callable\n\nid = 42                   # now id() is gone\nid(other)                 # TypeError: 'int' object is not callable\n```\n\nPick non-conflicting names: `items`\u002F`values` instead of `list`, `mapping` instead of\n`dict`, `user_id` instead of `id`. Linters flag builtin shadowing — heed the warning\nto avoid these baffling bugs.\n",{"id":2831,"difficulty":162,"q":2832,"a":2833},"integer-float-equality","Why does `0.1 + 0.2 == 0.3` return False?","Floats use **binary IEEE 754** representation, and 0.1\u002F0.2\u002F0.3 can't be stored\nexactly — tiny rounding errors make the sum slightly off. Comparing floats with `==`\nis a classic trap.\n\n```python\n0.1 + 0.2            # 0.30000000000000004\n0.1 + 0.2 == 0.3     # False!\n\nimport math\nmath.isclose(0.1 + 0.2, 0.3)   # True — tolerant comparison\nround(0.1 + 0.2, 10) == 0.3    # True\n```\n\nRule of thumb: never compare floats with `==`; use `math.isclose` (or `Decimal` for\nexact decimal arithmetic like money).\n",{"id":2835,"difficulty":150,"q":2836,"a":2837},"chained-augmented-assignment","Why does `a += b` behave differently for lists than `a = a + b`?","For lists, **`+=` mutates in place** (calls `__iadd__`\u002F`extend`), affecting all\nreferences to the same list, while `a = a + b` creates a **new** list. This surprises\npeople who alias a list.\n\n```python\na = [1, 2]\nb = a\na += [3]          # mutates the shared list\nb                 # [1, 2, 3] — b sees it too!\n\na = [1, 2]\nb = a\na = a + [3]       # new list bound to a\nb                 # [1, 2] — unchanged\n```\n\nRule of thumb: `+=` on a mutable object mutates in place (shared refs see it);\n`a = a + b` rebinds to a fresh object. Know which you want when aliases exist.\n",{"id":2839,"difficulty":162,"q":2840,"a":2841},"default-mutable-in-dict","What's wrong with `dict.fromkeys(keys, [])`?","`dict.fromkeys(keys, [])` gives **every key the same list object** — the default is\nevaluated once. Mutating one key's list mutates them all, just like the mutable\ndefault-argument gotcha.\n\n```python\nd = dict.fromkeys([\"a\", \"b\"], [])\nd[\"a\"].append(1)\nd                 # {'a': [1], 'b': [1]} — shared list!\n\n# fix: build per-key fresh values\nd = {k: [] for k in [\"a\", \"b\"]}\n```\n\nRule of thumb: never use a mutable default with `fromkeys`; use a dict comprehension\nso each key gets its own object.\n",{"id":2843,"difficulty":162,"q":2844,"a":2845},"truthiness-empty-check-bug","Why can `if not value:` be a subtle bug?","`if not value:` is true for **many** falsy values — `0`, `0.0`, `\"\"`, `[]`, `False` —\nnot just `None`. When you only meant \"is it None\u002Fmissing?\", valid zero\u002Fempty inputs\nget wrongly rejected.\n\n```python\ndef set_timeout(t=None):\n    if not t:           # BUG: t=0 (no timeout) also triggers this\n        t = 30\n    return t\nset_timeout(0)          # 30 — wanted 0!\n\n# fix:\nif t is None:\n    t = 30\n```\n\nRule of thumb: use `is None` to test for \"missing\"; reserve `if not value:` for\ngenuine emptiness checks where 0\u002F\"\"\u002F[] should count.\n",{"id":2847,"difficulty":162,"q":2848,"a":2849},"string-concatenation-loop","Why is building a string with `+=` in a loop a performance gotcha?","Strings are **immutable**, so each `+=` creates a **new string** and copies all\nprior characters — turning a loop into O(n²) work. It's a silent performance trap on\nlarge inputs.\n\n```python\n# slow: O(n^2), new string every iteration\ns = \"\"\nfor chunk in chunks:\n    s += chunk\n\n# fast: O(n), one allocation\ns = \"\".join(chunks)\n```\n\nRule of thumb: accumulate pieces in a list and `\"\".join()` once — never repeatedly\n`+=` strings in a loop.\n",{"id":2851,"difficulty":162,"q":2852,"a":2853},"copy-vs-reference","Why does copying a list with `=` not actually copy it?","`b = a` binds **another name to the same object** — it doesn't copy. Mutating through\neither name affects both. Use slicing, `list()`, or `copy` for a real (shallow) copy.\n\n```python\na = [1, 2, 3]\nb = a\nb.append(4)\na                 # [1, 2, 3, 4] — same object!\n\nb = a[:]          # or list(a), or a.copy() — shallow copy\nb = copy.deepcopy(a)   # for nested structures\n```\n\nRule of thumb: assignment never copies in Python; use `a[:]`\u002F`list(a)`\u002F`copy()` for a\nshallow copy and `deepcopy` for nested data.\n",{"id":2855,"difficulty":162,"q":2856,"a":2857},"default-argument-evaluation-time","Why doesn't a default like `time.time()` update on each call?","Defaults are evaluated **once at function-definition time**, so a default such as\n`now=time.time()` freezes the value at import and never changes — a subtler form of\nthe mutable-default trap.\n\n```python\nimport time\ndef log(msg, ts=time.time()):   # ts fixed at definition!\n    print(ts, msg)\n\n# fix: compute inside the body\ndef log(msg, ts=None):\n    if ts is None:\n        ts = time.time()        # fresh each call\n    print(ts, msg)\n```\n\nRule of thumb: any default that should be \"current\" (timestamp, fresh container,\ncomputed value) must use a `None` sentinel and be created in the body.\n",{"id":1415,"difficulty":253,"q":2859,"a":2860},"Why is `(1)` not a tuple?","Parentheses **group** expressions; it's the **comma** that makes a tuple. So `(1)` is\njust the integer `1`, while `(1,)` is a one-element tuple. Forgetting the trailing\ncomma is a common bug.\n\n```python\ntype((1))      # \u003Cclass 'int'> — just parenthesized\ntype((1,))     # \u003Cclass 'tuple'> — the comma makes it\ntype(1, 2)     # SyntaxError? no — but (1, 2) is a tuple\n\n# subtle: a stray comma turns a value into a tuple\nx = 5,         # (5,) — accidental tuple!\n```\n\nRule of thumb: tuples are defined by commas, not parentheses — write `(x,)` for a\nsingle-element tuple and watch for accidental trailing commas.\n",{"description":148},"Python interview questions on common gotchas: mutable default arguments, late-binding closures, modifying a list while iterating, is vs ==, bare except, mutable class attributes, and shadowing builtins.","python\u002Fidioms\u002Fgotchas","Common Gotchas & Anti-patterns","GQPVZ82cGgatRuZWa__QJ8wgzk8dZJwA9I77xThFnZg",{"id":2867,"title":2868,"body":2869,"description":148,"difficulty":150,"extension":151,"framework":10,"frameworkSlug":8,"meta":2873,"navigation":153,"order":11,"path":2874,"questions":2875,"questionsCount":217,"related":218,"seo":2936,"seoDescription":2937,"stem":2938,"subtopic":2939,"topic":99,"topicSlug":101,"updated":223,"__hash__":2940},"qa\u002Fpython\u002Finternals\u002Fcpython-model.md","Cpython Model",{"type":145,"value":2870,"toc":2871},[],{"title":148,"searchDepth":29,"depth":29,"links":2872},[],{},"\u002Fpython\u002Finternals\u002Fcpython-model",[2876,2880,2884,2888,2892,2896,2900,2904,2908,2912,2916,2920,2924,2928,2932],{"id":2877,"difficulty":150,"q":2878,"a":2879},"source-to-bytecode","How does CPython go from source to running code?","CPython first **compiles** your `.py` source into **bytecode** — a compact,\nplatform-independent instruction set for the Python **virtual machine**. That\nbytecode is then executed by the **interpreter loop** (a big evaluation loop,\nhistorically a giant `switch`), which runs one bytecode instruction at a time.\nCompiled bytecode is cached as **`.pyc`** files in `__pycache__`.\n\n```python\n# mymod.py  ->  compiled to  __pycache__\u002Fmymod.cpython-3xx.pyc\n# The .pyc is reused if the source is unchanged (matched by hash\u002Ftimestamp),\n# so imports skip recompiling. It is NOT machine code — still bytecode.\ndef add(a, b):\n    return a + b\n```\n\nThe key points: bytecode is an **intermediate** representation (not native\nmachine code), `.pyc` is just a **cache** to skip recompilation on import, and the\nVM interprets it at runtime. Rule of thumb: source -> bytecode -> interpreter\nloop, with `.pyc` caching the middle step.\n",{"id":2881,"difficulty":150,"q":2882,"a":2883},"cpython-vs-others","What is CPython, and how does it differ from PyPy and Jython?","**CPython** is the **reference implementation** written in C — it's what you get\nfrom python.org and what most people mean by \"Python.\" The **language** is a\nspec; an **implementation** runs it. Alternatives include **PyPy** (with a\n**JIT** that often runs much faster), **Jython** (runs on the **JVM**), and\n**IronPython** (on **.NET**).\n\n```python\nimport platform\nprint(platform.python_implementation())   # 'CPython', 'PyPy', 'Jython', ...\n```\n\nThey differ in performance and integration: PyPy speeds up long-running pure\nPython via JIT, Jython\u002FIronPython interoperate with Java\u002F.NET libraries, and\nCPython has the widest C-extension ecosystem (NumPy, etc.) plus the **GIL**. Rule\nof thumb: \"Python\" is the language; CPython is the dominant implementation, and\nothers trade ecosystem reach for speed or platform integration.\n",{"id":2885,"difficulty":162,"q":2886,"a":2887},"dis-module","How do you inspect bytecode with the dis module?","The **`dis`** (\"disassemble\") module shows the **bytecode instructions** a\nfunction compiles to — useful for understanding what Python actually does under\nthe hood and for comparing the cost of two approaches.\n\n```python\nimport dis\n\ndef add(a, b):\n    return a + b\n\ndis.dis(add)\n# Example output (abbreviated):\n#   LOAD_FAST   a\n#   LOAD_FAST   b\n#   BINARY_OP   +        # add the two\n#   RETURN_VALUE\n```\n\nEach line is one VM instruction the interpreter loop executes. `dis` is great for\nanswering \"is this comprehension really faster?\" or seeing how the compiler\ndesugars a construct. Rule of thumb: when you want to know what the interpreter\n*literally* runs, disassemble it with `dis`.\n",{"id":2889,"difficulty":150,"q":2890,"a":2891},"frames-call-stack","What are frames and the call stack?","Every time a function is called, CPython creates a **frame object** — a record\nholding that call's **local variables**, the current **instruction pointer**, and\na reference to the caller. Frames are pushed onto the **call stack** as functions\ncall each other and popped as they return. This is the structure a **traceback**\nwalks when printing an error.\n\n```python\nimport inspect\n\ndef inner():\n    frame = inspect.currentframe()\n    print(frame.f_code.co_name)        # 'inner'\n    print(frame.f_back.f_code.co_name) # 'outer' — the caller's frame\n\ndef outer():\n    inner()\n\nouter()\n```\n\nFrames are why each call has **isolated locals** and why exceptions can report the\nfull chain of calls. Deep\u002Finfinite recursion piles up frames until\n`RecursionError` (the stack limit). Rule of thumb: one frame per active call, all\nchained together as the call stack.\n",{"id":2893,"difficulty":162,"q":2894,"a":2895},"compiled-or-interpreted","Is Python compiled or interpreted?","**Both.** Python source is first **compiled** to **bytecode** (a real compilation\nstep), and that bytecode is then **interpreted** by the CPython virtual machine at\nruntime. So the common \"Python is interpreted\" is only half the story — there's a\ncompile phase, just to bytecode rather than to native machine code.\n\n```python\nimport py_compile\npy_compile.compile(\"mymod.py\")   # explicitly produces the .pyc bytecode\n\n# At runtime, the VM's interpreter loop executes that bytecode.\n```\n\nThe distinction that matters: Python compiles to **portable bytecode**, not to\nCPU-specific machine code (the way C does ahead-of-time). PyPy adds a **JIT** that\n*does* compile hot bytecode to machine code at runtime. Rule of thumb: Python is\ncompiled to bytecode and then interpreted.\n",{"id":2897,"difficulty":150,"q":2898,"a":2899},"gil-role","What role does the GIL play in CPython's execution model?","The **GIL** (Global Interpreter Lock) is a single mutex that lets **only one\nthread execute Python bytecode at a time** in a CPython process. The interpreter\nloop holds it while running instructions and periodically releases it (and during\nblocking I\u002FO), so threads take turns rather than running Python code in parallel.\n\n```python\n# Two threads doing CPU-bound Python work do NOT run in parallel:\n# the GIL serializes their bytecode execution on one core.\n# threads -> good for I\u002FO-bound (GIL released during I\u002FO)\n# processes -> needed for CPU-bound parallelism (each has its own GIL)\n```\n\nIt exists largely to make CPython's memory management (reference counting)\nsimpler and C extensions safer. The consequence is that **threads don't give CPU\nparallelism** — that's what multiprocessing is for (see the Concurrency &\nParallelism topic). Rule of thumb: the GIL means one-bytecode-at-a-time per\nprocess, so use processes for CPU-bound parallelism.\n",{"id":2901,"difficulty":150,"q":2902,"a":2903},"code-object","What is a code object and how does it relate to a function?","A **code object** holds the **compiled bytecode and metadata** (argument names,\nconstants, variable names) — the immutable \"what to run.\" A **function object** wraps\na code object plus runtime context: defaults, closure cells, and globals. One code\nobject can back many function objects.\n\n```python\ndef f(x):\n    return x + 1\n\nf.__code__                    # the code object\nf.__code__.co_varnames        # ('x',)\nf.__code__.co_consts          # (None, 1)\nf.__code__.co_argcount        # 1\n```\n\nRule of thumb: code object = the compiled instructions\u002Fmetadata; function object =\ncode + defaults\u002Fclosure\u002Fglobals that make it callable.\n",{"id":2905,"difficulty":162,"q":2906,"a":2907},"pyc-invalidation","How does CPython decide whether to reuse a `.pyc` file?","By default CPython stores the source's **mtime and size** in the `.pyc` header and\nrecompiles if they changed. Python 3.7+ also supports **hash-based** `.pyc` files\n(checked against the source hash) for reproducible builds.\n\n```python\n# timestamp-based (default): compares source mtime\u002Fsize\n# hash-based: compile with SOURCE_DATE_EPOCH or:\n#   python -m py_compile --invalidation-mode checked-hash mymod.py\n```\n\nRule of thumb: `.pyc` reuse is keyed on source mtime\u002Fsize (or a hash) — edit the\nsource and the cache is invalidated and rebuilt automatically.\n",{"id":2909,"difficulty":150,"q":2910,"a":2911},"peephole-optimizer","What compile-time optimizations does CPython apply?","CPython's compiler does **constant folding** and (in 3.11+) an enhanced peephole\u002F\noptimizer pass: it pre-computes constant expressions and simplifies bytecode. It does\n**not** do deep optimizations like inlining or type specialization at compile time\n(3.11+ adds a runtime *specializing adaptive* interpreter).\n\n```python\nimport dis\ndef f():\n    return 60 * 60 * 24       # folded to the constant 86400 at compile time\ndis.dis(f)                    # LOAD_CONST 86400 ; RETURN_VALUE\n```\n\nRule of thumb: constant expressions are folded at compile time, but Python relies on\nthe runtime (and 3.11+ adaptive specialization) for most speedups — don't expect\nC-compiler-level optimization.\n",{"id":2913,"difficulty":150,"q":2914,"a":2915},"specializing-interpreter","What did the 3.11+ \"specializing adaptive interpreter\" change?","Python 3.11 introduced an **adaptive interpreter** (PEP 659): hot bytecode\ninstructions are **specialized** at runtime based on observed types (e.g. a generic\n`BINARY_OP` becomes an int-specific fast path). It's not a JIT to machine code, but\nit meaningfully speeds up CPython.\n\n```python\n# a loop doing integer math gets BINARY_OP specialized to BINARY_OP_ADD_INT\n# after a few iterations, then falls back if it sees other types\ntotal = 0\nfor i in range(1_000_000):\n    total += i            # specialized hot path\n```\n\nRule of thumb: 3.11+ specializes hot, type-stable bytecode for speed; consistent\ntypes in hot loops let the adaptive interpreter optimize better.\n",{"id":2917,"difficulty":162,"q":2918,"a":2919},"stack-based-vm","Why is CPython called a \"stack-based\" virtual machine?","Its bytecode operates on an **evaluation stack**: instructions **push** operands and\n**pop** them to compute results, rather than using named registers. `LOAD_FAST`\npushes a value; `BINARY_OP` pops two and pushes the result.\n\n```python\nimport dis\ndis.dis(compile(\"a + b\", \"\u003Cs>\", \"eval\"))\n#   LOAD_NAME a      \u003C- push a\n#   LOAD_NAME b      \u003C- push b\n#   BINARY_OP +      \u003C- pop b, pop a, push a+b\n#   RETURN_VALUE     \u003C- pop result\n```\n\nRule of thumb: CPython evaluates expressions by pushing\u002Fpopping on a value stack —\nthat's the model `dis` output reflects.\n",{"id":2921,"difficulty":162,"q":2922,"a":2923},"globals-builtins-lookup","How does CPython resolve a name at the bytecode level?","The compiler picks the **opcode by scope**: `LOAD_FAST` for locals (array slot, very\nfast), `LOAD_GLOBAL` for module globals\u002Fbuiltins (dict lookups), `LOAD_DEREF` for\nclosure variables. This is why local access is faster than global.\n\n```python\nimport dis\ng = 10\ndef f(x):\n    return x + g\ndis.dis(f)\n#   LOAD_FAST   x     \u003C- local: fast array access\n#   LOAD_GLOBAL g     \u003C- global: dict lookup (slower)\n```\n\nRule of thumb: locals (`LOAD_FAST`) beat globals (`LOAD_GLOBAL`); in hot loops,\nbinding a global to a local can shave lookup time.\n",{"id":2925,"difficulty":162,"q":2926,"a":2927},"recursion-limit","Why does Python have a recursion limit, and how do you change it?","Each recursive call pushes a **frame** onto the call stack (which sits on the C\nstack). Python caps recursion (default ~1000) via `sys.setrecursionlimit` to raise a\nclean **`RecursionError`** instead of crashing with a C-level stack overflow.\n\n```python\nimport sys\nsys.getrecursionlimit()        # 1000 (default)\nsys.setrecursionlimit(5000)    # raise it — but risks a real segfault if too high\n\ndef deep(n):\n    return 1 if n == 0 else deep(n - 1)  # RecursionError past the limit\n```\n\nRule of thumb: the limit guards the C stack — raise it cautiously, or rewrite deep\nrecursion iteratively (Python has no tail-call optimization).\n",{"id":2929,"difficulty":150,"q":2930,"a":2931},"no-tail-call","Does CPython optimize tail recursion?","**No** — CPython deliberately has **no tail-call optimization**. Each recursive call\nadds a frame, so deep tail recursion still hits `RecursionError`. Guido has argued\nkeeping frames aids debugging (full tracebacks).\n\n```python\ndef factorial(n, acc=1):\n    if n == 0:\n        return acc\n    return factorial(n - 1, acc * n)   # NOT optimized — frame per call\n\n# iterative version avoids the stack growth:\ndef factorial_iter(n):\n    acc = 1\n    for i in range(2, n + 1):\n        acc *= i\n    return acc\n```\n\nRule of thumb: don't rely on tail recursion in Python; convert deep recursion to a\nloop or use an explicit stack.\n",{"id":2933,"difficulty":162,"q":2934,"a":2935},"interning-compile-time","What constants does the compiler deduplicate within a code object?","The compiler stores literals in the code object's **`co_consts`** and reuses\nidentical immutable constants within the same code object. Identical string\u002Fnumber\nliterals in one function often share one object — a compile-time optimization\ndistinct from runtime interning.\n\n```python\ndef f():\n    a = \"hello\"\n    b = \"hello\"\n    return a is b        # True — same constant in co_consts\n\nf.__code__.co_consts     # (None, 'hello') — stored once\n```\n\nRule of thumb: literals are deduped per code object at compile time; don't rely on\nthis across functions or for runtime-built values — use `==` for value checks.\n",{"description":148},"Python interview questions on the CPython execution model: source to bytecode to .pyc, the interpreter loop, the dis module, frames and the call stack, and whether Python is compiled or interpreted.","python\u002Finternals\u002Fcpython-model","The CPython Execution Model","lNnvsePmHblbHvF8p7gy4Onaz8MlueCXdnfhcCYzsBE",{"id":2942,"title":2943,"body":2944,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":2948,"navigation":153,"order":11,"path":2949,"questions":2950,"questionsCount":217,"related":218,"seo":3011,"seoDescription":3012,"stem":3013,"subtopic":3014,"topic":37,"topicSlug":38,"updated":223,"__hash__":3015},"qa\u002Fpython\u002Fiteration\u002Fiterators.md","Iterators",{"type":145,"value":2945,"toc":2946},[],{"title":148,"searchDepth":29,"depth":29,"links":2947},[],{},"\u002Fpython\u002Fiteration\u002Fiterators",[2951,2955,2959,2963,2967,2971,2975,2979,2983,2987,2991,2995,2999,3003,3007],{"id":2952,"difficulty":162,"q":2953,"a":2954},"iterable-vs-iterator","What is the difference between an iterable and an iterator?","An **iterable** is anything you can loop over — it knows how to produce an\niterator via `__iter__`. An **iterator** is the object that actually does\nthe walking: it has `__next__` and yields one value at a time, remembering\nits position. Every iterator is iterable (its `__iter__` returns itself),\nbut not every iterable is an iterator.\n\n```python\nnums = [1, 2, 3]          # list: iterable, NOT an iterator\nit = iter(nums)           # iterator over the list\nnext(it)                  # 1  — iterators track position\nnext(it)                  # 2\n```\n\nThink of the iterable as the *collection* and the iterator as a\n*cursor\u002Fbookmark* into it. You can create many independent iterators from\none iterable.\n",{"id":2956,"difficulty":162,"q":2957,"a":2958},"iter-next-protocol","What methods make up the iterator protocol?","The **iterator protocol** is two methods. `__iter__` must return the\niterator object itself, and `__next__` returns the next value or raises\n**`StopIteration`** when exhausted. That exception is the agreed signal\nthat there are no more items.\n\n```python\nit = iter([10, 20])\nit.__next__()      # 10\nit.__next__()      # 20\nit.__next__()      # raises StopIteration\n```\n\nAn **iterable** only needs `__iter__` (returning a fresh iterator). An\n**iterator** needs both. The `StopIteration` raise is what lets `for` loops\nknow when to stop — they catch it silently.\n",{"id":2960,"difficulty":253,"q":2961,"a":2962},"iter-next-builtins","What do the built-in iter() and next() functions do?","`iter(obj)` calls `obj.__iter__()` to get an iterator; `next(it)` calls\n`it.__next__()` to advance it. `next()` accepts an optional **default** that\nis returned instead of raising `StopIteration` when the iterator is\nexhausted — handy for safe peeking.\n\n```python\nit = iter(\"ab\")\nnext(it)            # 'a'\nnext(it)            # 'b'\nnext(it, \"done\")    # 'done'  — default instead of StopIteration\n\n# iter() also has a two-arg sentinel form:\n# iter(callable, sentinel) calls until it returns sentinel\n```\n\nUse the default argument whenever you want to drain or sample an iterator\nwithout wrapping `next()` in a `try`\u002F`except StopIteration`.\n",{"id":2964,"difficulty":162,"q":2965,"a":2966},"custom-iterator-class","How do you build a custom iterator class?","Implement `__iter__` (return `self`) and `__next__` (return the next value\nor raise `StopIteration`). The instance holds its own state between calls.\n\n```python\nclass Countdown:\n    def __init__(self, start):\n        self.n = start\n    def __iter__(self):\n        return self\n    def __next__(self):\n        if self.n \u003C= 0:\n            raise StopIteration\n        self.n -= 1\n        return self.n + 1\n\nlist(Countdown(3))     # [3, 2, 1]\n```\n\nThis works, but for most cases a **generator function** (using `yield`) is\nfar less boilerplate — it builds the `__iter__`\u002F`__next__`\u002F`StopIteration`\nmachinery for you. Reach for a class only when you need extra methods or\nexplicit state.\n",{"id":2968,"difficulty":150,"q":2969,"a":2970},"for-under-the-hood","How does a for loop work under the hood?","A `for` loop is sugar over the iterator protocol. Python calls `iter()` on\nthe iterable once to get an iterator, then repeatedly calls `next()` on it,\nbinding each result to the loop variable, until `StopIteration` is raised —\nwhich it catches to end the loop.\n\n```python\nfor x in [1, 2, 3]:\n    print(x)\n\n# is roughly equivalent to:\n_it = iter([1, 2, 3])\nwhile True:\n    try:\n        x = next(_it)\n    except StopIteration:\n        break\n    print(x)\n```\n\nThis is why any object implementing the protocol \"just works\" in a `for`\nloop, comprehension, or `*`-unpacking. The `StopIteration` is the hidden\nhandshake that terminates the loop.\n",{"id":2972,"difficulty":162,"q":2973,"a":2974},"iterator-exhaustion","Why can you only iterate an iterator once?","An iterator is **single-use \u002F exhaustible**: once `__next__` has walked to\nthe end and raised `StopIteration`, it stays exhausted — there is no reset.\nRe-iterating yields nothing. This trips people up with generators and\n`zip`\u002F`map` objects.\n\n```python\nit = iter([1, 2, 3])\nlist(it)     # [1, 2, 3]\nlist(it)     # []  — already exhausted!\n\ngen = (x for x in range(3))\nsum(gen)     # 3\nsum(gen)     # 0  — the generator is spent\n```\n\nA **list** (an iterable, not an iterator) can be looped many times because\neach loop calls `iter()` to get a *fresh* iterator. If you need to reuse an\nexhaustible result, materialize it into a list first.\n",{"id":2976,"difficulty":150,"q":2977,"a":2978},"iter-sentinel-form","What does the two-argument form `iter(callable, sentinel)` do?","`iter(func, sentinel)` builds an iterator that calls **`func()` repeatedly** until it\nreturns the **sentinel** value, then stops. It's perfect for reading streams in\nfixed-size chunks without a `while True`\u002F`break`.\n\n```python\n# read a file in 1024-byte blocks until EOF (b''):\nwith open(\"data.bin\", \"rb\") as f:\n    for chunk in iter(lambda: f.read(1024), b\"\"):\n        process(chunk)\n\n# roll a die until you get a 6:\nimport random\nlist(iter(lambda: random.randint(1, 6), 6))\n```\n\nRule of thumb: use `iter(callable, sentinel)` to turn a \"call until you see X\" loop\ninto a clean iterator, especially for chunked I\u002FO.\n",{"id":2980,"difficulty":162,"q":2981,"a":2982},"reusable-iterable-class","How do you make a class that can be iterated multiple times?","Make `__iter__` return a **fresh iterator** each call (a generator or a new iterator\nobject) instead of `self`. Then the class is a **reusable iterable**, not a\nsingle-use iterator.\n\n```python\nclass Squares:\n    def __init__(self, n): self.n = n\n    def __iter__(self):\n        return (i * i for i in range(self.n))   # new generator each time\n\nsq = Squares(3)\nlist(sq)     # [0, 1, 4]\nlist(sq)     # [0, 1, 4] — works again!\n```\n\nRule of thumb: return `self` from `__iter__` for one-shot iterators; return a new\ngenerator\u002Fiterator for collections meant to be looped repeatedly.\n",{"id":2984,"difficulty":162,"q":2985,"a":2986},"itertools-islice","How do you slice an iterator without converting it to a list?","Regular slicing (`it[1:3]`) doesn't work on iterators. Use **`itertools.islice`**,\nwhich lazily takes a range of items — essential for infinite or huge iterators.\n\n```python\nfrom itertools import islice, count\n\nislice(count(), 2, 5)            # lazy: 2, 3, 4\nlist(islice(count(), 2, 5))      # [2, 3, 4]\nlist(islice(range(100), 10))     # first 10\n```\n\nRule of thumb: `islice` is the iterator-friendly slice; it consumes (and discards)\nskipped items and never materializes the whole sequence.\n",{"id":2988,"difficulty":150,"q":2989,"a":2990},"tee-iterator","How does `itertools.tee` let you iterate a source more than once?","`tee(iterable, n)` returns **n independent iterators** over the same source. It\nbuffers items already consumed by one branch until the others catch up — useful when\nyou can't re-create the source.\n\n```python\nfrom itertools import tee\n\nit = (x * x for x in range(5))\na, b = tee(it, 2)\nlist(a)     # [0, 1, 4, 9, 16]\nlist(b)     # [0, 1, 4, 9, 16] — independent copy\n\n# warning: don't keep using the original `it` after tee-ing it\n```\n\nRule of thumb: use `tee` for limited multi-pass over a one-shot iterator, but if the\nbranches diverge a lot it buffers heavily — then a `list` is simpler.\n",{"id":2992,"difficulty":162,"q":2993,"a":2994},"peeking-iterator","How can you peek at the next value of an iterator without consuming it?","Iterators have no built-in peek. Pull the value with `next()`, then **push it back**\nby chaining it in front with `itertools.chain` — a common \"lookahead\" pattern.\n\n```python\nfrom itertools import chain\n\nit = iter([1, 2, 3])\nfirst = next(it)              # 1 — consumed\nit = chain([first], it)       # put it back on the front\nlist(it)                      # [1, 2, 3] — nothing lost\n```\n\nRule of thumb: emulate peeking by `next()` + `chain([val], it)`; for repeated\nlookahead, wrap the iterator in a small buffering class.\n",{"id":2996,"difficulty":162,"q":2997,"a":2998},"empty-iterator-check","How do you check whether an iterator is empty?","You can't test emptiness without consuming an item. Use `next(it, sentinel)` and\ncompare against a unique sentinel; if you need the item, re-attach it with `chain`.\n\n```python\nfrom itertools import chain\n_sentinel = object()\n\ndef is_empty(it):\n    first = next(it, _sentinel)\n    if first is _sentinel:\n        return True, it\n    return False, chain([first], it)   # give the item back\n\nempty, it = is_empty(iter([]))     # (True, ...)\n```\n\nRule of thumb: there's no peek-free emptiness check — pull one item with a sentinel\ndefault and rebuild the iterator if it wasn't empty.\n",{"id":3000,"difficulty":162,"q":3001,"a":3002},"reversed-builtin","How does the `reversed()` built-in work and what does it require?","`reversed(seq)` returns an iterator that walks a sequence **backwards**. It needs a\nsequence that supports **`__reversed__`** or both `__len__` and `__getitem__` — so it\nworks on lists\u002Ftuples\u002Franges but **not** on plain generators or sets.\n\n```python\nlist(reversed([1, 2, 3]))     # [3, 2, 1]\nlist(reversed(range(3)))      # [2, 1, 0] — ranges are sized\nreversed(x for x in range(3)) # TypeError — generators aren't reversible\n\nclass C:\n    def __reversed__(self): return iter([9, 8, 7])\nlist(reversed(C()))           # [9, 8, 7] — custom hook\n```\n\nRule of thumb: `reversed` needs a sized, indexable sequence (or `__reversed__`); for\none-shot iterables, materialize to a list first.\n",{"id":3004,"difficulty":150,"q":3005,"a":3006},"stopiteration-in-generator","Why shouldn't you let `StopIteration` leak out of a generator?","Since PEP 479 (default in 3.7+), a `StopIteration` that bubbles out of a generator\nbody is converted into a **`RuntimeError`**. This prevents a bug where an inner\n`next()` raising `StopIteration` would silently end the generator.\n\n```python\ndef gen(it):\n    while True:\n        yield next(it)      # when `it` is exhausted, next() raises StopIteration\n\nlist(gen(iter([1, 2])))     # RuntimeError: generator raised StopIteration\n\ndef safe(it):\n    for x in it:            # use a for-loop, which handles StopIteration\n        yield x\n```\n\nRule of thumb: inside generators, consume sub-iterators with `for`\u002F`yield from` (or\n`next(it, default)`), never a bare `next()` that can raise `StopIteration`.\n",{"id":3008,"difficulty":253,"q":3009,"a":3010},"lazy-map-filter","Are `map` and `filter` iterators in Python 3?","Yes — in Python 3 `map`, `filter`, and `zip` return **lazy iterators**, not lists.\nThey compute on demand and are **single-pass**, so you must wrap them in `list()` to\nsee or reuse all results.\n\n```python\nm = map(str.upper, [\"a\", \"b\"])\nnext(m)        # 'A' — lazy, one at a time\nlist(m)        # ['B'] — 'A' already consumed\n\nlist(filter(lambda x: x > 0, [-1, 2, -3, 4]))   # [2, 4]\n```\n\nRule of thumb: `map`\u002F`filter`\u002F`zip` are one-shot iterators in Python 3 — materialize\nwith `list()` if you need indexing, length, or multiple passes.\n",{"description":148},"Python interview questions on iterables vs iterators, the iterator protocol, __iter__ and __next__, StopIteration, building custom iterators, and how for loops work under the hood.","python\u002Fiteration\u002Fiterators","Iterators & the Iterator Protocol","tLsg7cnMj82Dx33jzUjtSWjGcLcR0GmozLh9oyOGvts",{"id":3017,"title":3018,"body":3019,"description":148,"difficulty":253,"extension":151,"framework":10,"frameworkSlug":8,"meta":3023,"navigation":153,"order":11,"path":3024,"questions":3025,"questionsCount":217,"related":218,"seo":3084,"seoDescription":3085,"stem":3086,"subtopic":3087,"topic":81,"topicSlug":83,"updated":223,"__hash__":3088},"qa\u002Fpython\u002Fmodules\u002Fvirtual-environments.md","Virtual Environments",{"type":145,"value":3020,"toc":3021},[],{"title":148,"searchDepth":29,"depth":29,"links":3022},[],{},"\u002Fpython\u002Fmodules\u002Fvirtual-environments",[3026,3030,3034,3038,3042,3045,3048,3052,3056,3060,3064,3068,3072,3076,3080],{"id":3027,"difficulty":253,"q":3028,"a":3029},"what-is-venv","What is a virtual environment and why does isolation matter?","A **virtual environment** is a self-contained directory with its own Python\ninterpreter and its own `site-packages`, so each project gets an **isolated** set of\ndependencies. Isolation matters because two projects often need **different\nversions** of the same package — without venvs, installing for one breaks the other,\nand installing globally can even break system tools.\n\n```bash\n# Project A needs Django 3, Project B needs Django 5.\n# Separate venvs let both coexist without conflict.\npython -m venv .venv        # creates an isolated environment\n```\n\nA venv keeps dependencies **per project** and out of the global interpreter, making\nbuilds reproducible and avoiding \"works on my machine\" version clashes. Use one for\nevery project.\n",{"id":3031,"difficulty":253,"q":3032,"a":3033},"create-activate","How do you create and activate a virtual environment?","Create one with the standard-library **`venv`** module, then **activate** it so the\nshell uses the venv's `python` and `pip`. The activation command differs by OS;\n**`deactivate`** returns to the global interpreter.\n\n```bash\npython -m venv .venv            # create (folder named .venv)\n\nsource .venv\u002Fbin\u002Factivate       # activate on macOS \u002F Linux\n.venv\\Scripts\\activate          # activate on Windows\n\nwhich python                    # -> ...\u002F.venv\u002Fbin\u002Fpython while active\ndeactivate                      # leave the venv\n```\n\nOnce active, `pip install` puts packages **inside** the venv only. Add `.venv\u002F` to\n`.gitignore` — you commit the dependency list, not the environment itself.\n",{"id":3035,"difficulty":253,"q":3036,"a":3037},"pip-requirements","How do you install packages and what is requirements.txt?","**`pip install`** fetches packages from PyPI into the active environment. A\n**`requirements.txt`** lists a project's dependencies (often with pinned versions)\nso anyone can recreate the same environment with one command.\n\n```bash\npip install requests            # install latest\npip install \"requests==2.31.0\"  # install a specific version\n\npip install -r requirements.txt # install everything listed in the file\n```\n\n```text\n# requirements.txt\nrequests==2.31.0\nrich>=13.0\n```\n\nCommitting `requirements.txt` makes installs **reproducible** across machines and\nCI. Install into an **activated venv**, never globally with `sudo pip`.\n",{"id":3039,"difficulty":162,"q":3040,"a":3041},"pip-freeze","What does pip freeze do?","`pip freeze` prints every installed package with its **exact pinned version** in\n`requirements.txt` format, so you can capture the current environment. Redirect it\nto a file to snapshot dependencies.\n\n```bash\npip freeze                          # list installed pkgs == versions\npip freeze > requirements.txt       # snapshot the current environment\npip list                            # similar, but human-readable table\n```\n\nA caveat: `pip freeze` records **everything installed, including transitive\ndependencies**, which can make the file noisy. Many teams instead hand-curate\ndirect dependencies (or use a lock-file tool) and keep `pip freeze` for capturing a\nknown-good full snapshot.\n",{"id":1987,"difficulty":162,"q":3043,"a":3044},"What is an editable install (`pip install -e`)?","An **editable install** links your project into the environment **in place** instead\nof copying it, so edits to the source take effect **immediately** without\nreinstalling. It's the standard way to work on a package you're developing locally.\n\n```bash\npip install -e .                # install the current project, editable\npip install -e \".[dev]\"         # editable + optional 'dev' extras\n```\n\nBecause the install points at your working tree, changing the code updates the\nimported package right away — no rebuild needed. Use `-e` for **your own package\nunder development**, and a normal `pip install` for third-party dependencies.\n",{"id":1983,"difficulty":162,"q":3046,"a":3047},"What is pyproject.toml?","`pyproject.toml` is the **modern, standardized** config file (PEP 518\u002F621) for a\nPython project — it declares the **build system**, project **metadata**, and\n**dependencies** in one place, replacing the older `setup.py`\u002F`setup.cfg` split. Most\nmodern tools (build, pip, linters, formatters) read it.\n\n```toml\n[project]\nname = \"myapp\"\nversion = \"0.1.0\"\ndependencies = [\"requests>=2.31\", \"rich\"]\n\n[build-system]\nrequires = [\"hatchling\"]\nbuild-backend = \"hatchling.build\"\n```\n\nWith dependencies declared here, `pip install .` (or `-e .`) reads them directly, so\na separate `requirements.txt` becomes optional. It's the recommended starting point\nfor any new packaged project.\n",{"id":3049,"difficulty":162,"q":3050,"a":3051},"how-venv-works","How does activating a venv actually change your shell?","Activation **prepends the venv's `bin`\u002F`Scripts` directory to `PATH`** and sets\n`VIRTUAL_ENV`, so `python` and `pip` resolve to the venv's copies first. The venv's\n`pyvenv.cfg` points back to the base interpreter; there's no magic beyond `PATH`.\n\n```bash\nsource .venv\u002Fbin\u002Factivate\necho $PATH            # .venv\u002Fbin is now first\nwhich python          # ...\u002F.venv\u002Fbin\u002Fpython\necho $VIRTUAL_ENV     # path to the venv\n```\n\nRule of thumb: a venv is mostly a `PATH` trick + isolated `site-packages`; you can\neven skip activation by calling `.venv\u002Fbin\u002Fpython` directly.\n",{"id":3053,"difficulty":162,"q":3054,"a":3055},"venv-vs-virtualenv-conda","How do `venv`, `virtualenv`, and `conda` differ?","**`venv`** is built into the stdlib and manages **Python packages** for one Python\nversion. **`virtualenv`** is a faster third-party superset (supports older Pythons,\nmore features). **`conda`** is a separate ecosystem that also manages **non-Python\nbinaries and Python itself**.\n\n```bash\npython -m venv .venv          # stdlib, simplest\nvirtualenv .venv              # third-party, more features\nconda create -n myenv python=3.12 numpy   # manages Python + native deps\n```\n\nRule of thumb: use `venv` for most projects; `conda` when you need complex native\u002F\nscientific binaries or to manage the Python version itself.\n",{"id":3057,"difficulty":162,"q":3058,"a":3059},"version-specifiers","What do version specifiers like `==`, `>=`, `~=` mean in pip?","Specifiers constrain acceptable versions: **`==`** exact, **`>=`** minimum, **`~=`**\n\"compatible release\" (allows the last digit to grow), and `!=` to exclude. They\nbalance reproducibility against getting fixes.\n\n```text\nrequests==2.31.0     # exactly this version\nrequests>=2.28       # this or newer\nrequests~=2.31.0     # >=2.31.0, \u003C2.32.0  (compatible release)\nrequests>=2.0,\u003C3.0   # range\n```\n\nRule of thumb: pin exact (`==`) versions in a lock\u002Fdeploy file for reproducibility;\nuse ranges (`>=`, `~=`) for libraries to stay compatible with users' other deps.\n",{"id":3061,"difficulty":162,"q":3062,"a":3063},"pip-vs-pipx","When should you use `pipx` instead of `pip`?","**`pipx`** installs Python **applications** (CLI tools) each into their **own isolated\nvenv** while exposing the command globally — avoiding dependency clashes in your base\nenvironment. Use `pip` for **libraries** your project imports.\n\n```bash\npipx install black           # global `black` command, isolated deps\npipx install httpie\npip install requests         # a library to import in your code\n```\n\nRule of thumb: `pipx` for standalone tools you run (linters, formatters, CLIs);\n`pip` (inside a venv) for packages you `import`.\n",{"id":3065,"difficulty":150,"q":3066,"a":3067},"dependency-resolution","What does pip's dependency resolver do, and what are lock files for?","Pip's resolver finds a set of versions satisfying **all** constraints (direct and\ntransitive); it errors on conflicts. Because `requirements.txt` may not pin\neverything, **lock files** (pip-tools, Poetry, uv) record the **exact resolved\nversions** for fully reproducible installs.\n\n```bash\npip install -r requirements.txt   # resolver picks compatible versions\npip-compile requirements.in       # -> pinned requirements.txt (lock)\nuv lock                           # modern lock-file workflow\n```\n\nRule of thumb: use a lock file (compiled pins) for apps\u002Fdeploys so every install is\nidentical; loose ranges are for libraries that must coexist with others.\n",{"id":3069,"difficulty":253,"q":3070,"a":3071},"no-sudo-pip","Why should you avoid `sudo pip install`?","Installing globally as root can **overwrite or break system packages** that the OS\ndepends on, and mixes project deps into the system Python. Use a **venv** (or\n`pip install --user`) instead so installs stay isolated and reversible.\n\n```bash\nsudo pip install foo        # risky: can clobber system Python packages\npython -m venv .venv && source .venv\u002Fbin\u002Factivate && pip install foo  # safe\n```\n\nRule of thumb: never `sudo pip`; isolate with a venv. Modern Linux even blocks global\npip installs (PEP 668 \"externally-managed-environment\") to prevent this.\n",{"id":3073,"difficulty":253,"q":3074,"a":3075},"upgrade-and-uninstall","How do you upgrade or remove packages with pip?","Use **`pip install --upgrade`** (or `-U`) to update, and **`pip uninstall`** to\nremove. `pip show` inspects a package; `pip list --outdated` finds upgradable ones.\n\n```bash\npip install --upgrade requests     # update to the latest allowed\npip install -U pip                 # upgrade pip itself\npip uninstall requests             # remove (asks to confirm)\npip list --outdated                # what can be upgraded\npip show requests                  # version, location, dependencies\n```\n\nRule of thumb: `pip uninstall` doesn't remove a package's **dependencies** — use a\nlock\u002Fclean reinstall or a tool like `pip-autoremove` to prune orphans.\n",{"id":3077,"difficulty":162,"q":3078,"a":3079},"requirements-vs-pyproject","When do you use `requirements.txt` versus declaring deps in `pyproject.toml`?","Put **abstract dependencies** (ranges) in `pyproject.toml` for a **library\u002Fpackage**\nothers install. Use **`requirements.txt`** (often pinned) for an **application\u002F\ndeployment** environment you control. They serve different audiences.\n\n```toml\n# pyproject.toml — library deps, loose\ndependencies = [\"requests>=2.28\", \"click\"]\n```\n```text\n# requirements.txt — app deploy, pinned\nrequests==2.31.0\nclick==8.1.7\n```\n\nRule of thumb: `pyproject.toml` declares what your package *needs*; `requirements.txt`\n(or a lock file) records the exact environment to *reproduce* for an app.\n",{"id":3081,"difficulty":253,"q":3082,"a":3083},"pip-cache-and-offline","What is the pip cache and how do you do offline installs?","Pip caches downloaded wheels so repeat installs are fast and don't re-download. You\ncan build a **local wheelhouse** with `pip download` and install from it offline with\n`--no-index --find-links`.\n\n```bash\npip cache dir                       # where wheels are cached\npip install --no-cache-dir foo      # bypass the cache\npip download -r requirements.txt -d wheels\u002F    # fetch wheels for offline use\npip install --no-index --find-links=wheels\u002F -r requirements.txt\n```\n\nRule of thumb: rely on the cache for speed; use `pip download` + `--find-links` to\ninstall on air-gapped\u002Foffline machines reproducibly.\n",{"description":148},"Python interview questions on virtual environments and pip — what a venv is and why isolation matters, creating and activating one, requirements.txt, pip freeze, editable installs, and pyproject.toml.","python\u002Fmodules\u002Fvirtual-environments","Virtual Environments & pip","7hbLk74u95Vd-nyOwu4tbkV7JqxM5Hr49eYaI4H3xAI",{"id":3090,"title":3091,"body":3092,"description":148,"difficulty":150,"extension":151,"framework":10,"frameworkSlug":8,"meta":3096,"navigation":153,"order":11,"path":3097,"questions":3098,"questionsCount":217,"related":218,"seo":3157,"seoDescription":3158,"stem":3159,"subtopic":3160,"topic":54,"topicSlug":56,"updated":223,"__hash__":3161},"qa\u002Fpython\u002Foop\u002Fdunder-methods.md","Dunder Methods",{"type":145,"value":3093,"toc":3094},[],{"title":148,"searchDepth":29,"depth":29,"links":3095},[],{},"\u002Fpython\u002Foop\u002Fdunder-methods",[3099,3103,3107,3111,3115,3119,3123,3125,3129,3133,3137,3141,3145,3149,3153],{"id":3100,"difficulty":162,"q":3101,"a":3102},"what-are-dunder-methods","What are dunder (magic) methods?","**Dunder methods** (\"double underscore\", e.g. `__init__`, `__len__`) are special\nhooks Python calls **implicitly** to make your objects work with built-in syntax\nand functions. They are how Python implements **operator overloading and\nprotocols** — `len(x)` calls `x.__len__()`, `x + y` calls `x.__add__(y)`,\n`x[0]` calls `x.__getitem__(0)`.\n\n```python\nclass Box:\n    def __init__(self, items):\n        self.items = items\n    def __len__(self):\n        return len(self.items)   # makes len(box) work\n\nb = Box([1, 2, 3])\nlen(b)        # 3 — Python calls b.__len__()\n```\n\nThey're sometimes called \"magic methods\" but there's no magic — they're a\ndocumented protocol. Implementing the right dunders makes your objects feel like\nnative Python types.\n",{"id":3104,"difficulty":162,"q":3105,"a":3106},"repr-str-dunder","How do __repr__ and __str__ control how an object prints?","`__repr__` defines the **unambiguous developer representation** (REPL output,\ncontainers, `repr()`); `__str__` defines the **human-readable** form (`print()`,\n`str()`). When `__str__` is absent, Python **falls back to `__repr__`** — so\n`__repr__` is the one to always implement.\n\n```python\nclass Temperature:\n    def __init__(self, c):\n        self.c = c\n    def __repr__(self):\n        return f\"Temperature({self.c})\"     # eval-friendly\n    def __str__(self):\n        return f\"{self.c}°C\"                 # friendly\n\nt = Temperature(20)\nstr(t)    # '20°C'\nrepr(t)   # 'Temperature(20)'\n```\n\nA good `__repr__` ideally lets `eval(repr(obj))` reconstruct the object. Always\ndefine `__repr__`; add `__str__` only when a separate user-facing string helps.\n",{"id":3108,"difficulty":150,"q":3109,"a":3110},"eq-and-hash","How do __eq__ and __hash__ work together?","`__eq__` defines **value equality** (`==`); `__hash__` returns the integer used\nby **dicts and sets**. They have a **contract**: if `a == b` then\n`hash(a) == hash(b)`. Defining `__eq__` **alone** sets `__hash__ = None`, making\ninstances **unhashable** — so define both, derived from the **same immutable\nfields**.\n\n```python\nclass Point:\n    def __init__(self, x, y):\n        self.x, self.y = x, y\n    def __eq__(self, other):\n        return (self.x, self.y) == (other.x, other.y)\n    def __hash__(self):\n        return hash((self.x, self.y))    # same fields as __eq__\n\n{Point(1, 2), Point(1, 2)}   # one element — treated as equal\n```\n\nOnly hash on fields that never change after creation. If you break the contract,\nobjects you store in a set\u002Fdict become unfindable.\n",{"id":3112,"difficulty":150,"q":3113,"a":3114},"operator-overloading","How do you overload operators like + and *?","Implement the matching **arithmetic dunder**: `__add__` for `+`, `__sub__` for\n`-`, `__mul__` for `*`, `__lt__` for `\u003C`, and so on. Python calls them when the\noperator is used. Return a **new object** (or `NotImplemented` if you can't\nhandle the other operand, so Python can try the reflected method like\n`__radd__`).\n\n```python\nclass Vector:\n    def __init__(self, x, y):\n        self.x, self.y = x, y\n    def __add__(self, other):\n        return Vector(self.x + other.x, self.y + other.y)   # v1 + v2\n    def __mul__(self, k):\n        return Vector(self.x * k, self.y * k)               # v * 3\n    def __repr__(self):\n        return f\"Vector({self.x}, {self.y})\"\n\nVector(1, 2) + Vector(3, 4)   # Vector(4, 6)\nVector(1, 2) * 3              # Vector(3, 6)\n```\n\nReturn `NotImplemented` (not raise) for unsupported types so Python can fall\nback gracefully. Don't overload operators in surprising ways — keep semantics\nintuitive.\n",{"id":3116,"difficulty":150,"q":3117,"a":3118},"sequence-protocol","How do __len__ and __getitem__ make an object behave like a sequence?","Implementing `__len__` makes `len(obj)` work, and `__getitem__` makes\n**indexing, slicing, and iteration** work — Python can iterate by calling\n`__getitem__(0)`, `__getitem__(1)`, ... until `IndexError`, even without an\n`__iter__`. Together they form the **sequence protocol**.\n\n```python\nclass Playlist:\n    def __init__(self, songs):\n        self.songs = songs\n    def __len__(self):\n        return len(self.songs)         # len(pl)\n    def __getitem__(self, i):\n        return self.songs[i]           # pl[0], pl[1:3], and iteration\n\npl = Playlist([\"a\", \"b\", \"c\"])\nlen(pl)          # 3\npl[1]            # 'b'\nfor s in pl:     # iterates via __getitem__\n    print(s)\n```\n\nAdd `__contains__` for `in` and `__setitem__` for assignment. This duck-typed\nprotocol is why custom containers feel like lists.\n",{"id":3120,"difficulty":162,"q":3121,"a":3122},"call-dunder","What does __call__ do?","`__call__` makes an **instance itself callable** like a function — `obj()`\ninvokes `obj.__call__()`. This lets objects **carry state between calls**, which\na plain function can't do as cleanly. It's the basis of function objects and\nmany decorators.\n\n```python\nclass Multiplier:\n    def __init__(self, factor):\n        self.factor = factor       # remembered state\n    def __call__(self, x):\n        return x * self.factor     # obj(x)\n\ndouble = Multiplier(2)\ndouble(5)         # 10 — calling the instance\ncallable(double)  # True\n```\n\nUse it for **stateful callables** — configurable functions, accumulators, or\nclass-based decorators. If you just need behavior without state, a closure or\nplain function is simpler.\n",{"id":415,"difficulty":162,"q":416,"a":3124},"`@functools.total_ordering` is a class decorator that **fills in the missing\ncomparison operators** from the ones you define. You provide `__eq__` plus **one**\nof `__lt__`, `__le__`, `__gt__`, `__ge__`, and it generates the rest — saving you\nfrom writing all six.\n\n```python\nfrom functools import total_ordering\n\n@total_ordering\nclass Version:\n    def __init__(self, n):\n        self.n = n\n    def __eq__(self, other):\n        return self.n == other.n\n    def __lt__(self, other):\n        return self.n \u003C other.n     # only this + __eq__ needed\n\nVersion(1) \u003C Version(2)    # True\nVersion(2) >= Version(1)   # True — generated by total_ordering\nsorted([Version(3), Version(1), Version(2)])  # works\n```\n\nThe trade-off is a small performance cost from derived comparisons; for\nhot-path code, define all operators explicitly. Otherwise it's a clean way to\nget full ordering with minimal boilerplate.\n",{"id":3126,"difficulty":150,"q":3127,"a":3128},"getattr-vs-getattribute","What is the difference between `__getattr__` and `__getattribute__`?","`__getattribute__` is called on **every** attribute access (easy to recurse\ninfinitely if misused). `__getattr__` is the **fallback**, called **only when normal\nlookup fails**. You almost always want `__getattr__`.\n\n```python\nclass Proxy:\n    def __init__(self, data):\n        self._data = data\n    def __getattr__(self, name):          # only for missing attrs\n        return self._data.get(name, f\"\u003Cno {name}>\")\n\np = Proxy({\"x\": 1})\np.x          # 1 — normal lookup, __getattr__ NOT called\np.missing    # '\u003Cno missing>' — fallback fires\n```\n\nRule of thumb: use `__getattr__` for lazy\u002Fproxy\u002Fdefault attributes; touch\n`__getattribute__` only for advanced interception (and call `super()` to avoid\ninfinite recursion).\n",{"id":3130,"difficulty":162,"q":3131,"a":3132},"setattr-dunder","How does `__setattr__` work and how do you avoid infinite recursion?","`__setattr__` intercepts **every** attribute assignment. Assigning `self.x = v`\ninside it would call `__setattr__` again — infinite recursion. Route the actual\nwrite through **`super().__setattr__`** or `self.__dict__`.\n\n```python\nclass Validated:\n    def __setattr__(self, name, value):\n        if name == \"age\" and value \u003C 0:\n            raise ValueError(\"age must be >= 0\")\n        super().__setattr__(name, value)   # the real assignment\n\nv = Validated()\nv.age = 5          # ok\nv.age = -1         # ValueError\n```\n\nRule of thumb: in `__setattr__`, always delegate the final write to\n`super().__setattr__` (never `self.name = ...`) to prevent recursion.\n",{"id":3134,"difficulty":162,"q":3135,"a":3136},"context-manager-dunders","Which dunders make an object usable in a `with` statement?","**`__enter__`** (runs on entry, its return value is bound by `as`) and\n**`__exit__`** (runs on exit, even on exceptions). `__exit__` receives the exception\ninfo and returning `True` from it **suppresses** the exception.\n\n```python\nclass Timer:\n    def __enter__(self):\n        import time; self.t = time.time()\n        return self                      # bound to `as t`\n    def __exit__(self, exc_type, exc, tb):\n        import time; self.elapsed = time.time() - self.t\n        return False                     # don't suppress exceptions\n\nwith Timer() as t:\n    do_work()\nprint(t.elapsed)\n```\n\nRule of thumb: implement `__enter__`\u002F`__exit__` for setup\u002Fteardown pairs; return a\nfalsy value from `__exit__` unless you deliberately want to swallow errors.\n",{"id":3138,"difficulty":162,"q":3139,"a":3140},"iter-next-dunders","What is the difference between `__iter__` and `__next__`?","`__iter__` returns an **iterator** (often `self` or a fresh helper); `__next__`\nproduces the **next value** and raises **`StopIteration`** when exhausted. An object\nwith both is an **iterator**; one with only `__iter__` is an **iterable**.\n\n```python\nclass Countdown:\n    def __init__(self, n): self.n = n\n    def __iter__(self): return self        # it's its own iterator\n    def __next__(self):\n        if self.n \u003C= 0:\n            raise StopIteration\n        self.n -= 1\n        return self.n + 1\n\nlist(Countdown(3))    # [3, 2, 1]\n```\n\nRule of thumb: `__iter__` gives you something to iterate; `__next__` advances it.\nFor reusable iteration, make `__iter__` return a **new** iterator each time.\n",{"id":3142,"difficulty":253,"q":3143,"a":3144},"bool-and-len-dunder","How do `__bool__` and `__len__` affect truthiness in `if obj:`?","`if obj:` calls **`__bool__`** if defined; otherwise it falls back to **`__len__`**\n(zero = falsy). With neither, the object is always truthy. `__bool__` takes priority.\n\n```python\nclass Cart:\n    def __init__(self, items): self.items = items\n    def __len__(self):\n        return len(self.items)        # truthiness from length\n\nbool(Cart([]))      # False\nbool(Cart([1]))     # True\nif Cart([1, 2]):    # True\n    ...\n```\n\nRule of thumb: define `__len__` for containers (truthiness comes free) or `__bool__`\nfor non-container objects with a notion of \"empty\u002Fdisabled.\"\n",{"id":3146,"difficulty":150,"q":3147,"a":3148},"reflected-operators","When are reflected operators like `__radd__` called?","When `a + b` and `a.__add__(b)` returns `NotImplemented` (or `a` lacks `__add__`),\nPython tries **`b.__radd__(a)`**. This lets your type work on the **right side** of\nan operator with types it doesn't control (e.g. `3 * vector`).\n\n```python\nclass Money:\n    def __init__(self, amt): self.amt = amt\n    def __add__(self, other):\n        return Money(self.amt + other.amt)\n    def __radd__(self, other):           # handles sum() starting from 0\n        return self if other == 0 else Money(self.amt + other)\n\nsum([Money(5), Money(10)])   # uses __radd__ for the initial 0 + Money\n```\n\nRule of thumb: implement `__radd__`\u002F`__rmul__` etc. so your type composes with\nbuilt-ins and `sum()`; return `NotImplemented` for truly unsupported operands.\n",{"id":3150,"difficulty":162,"q":3151,"a":3152},"slots-dunder","How does `__index__` differ from `__int__`?","`__int__` backs `int(obj)` (lossy conversions allowed, e.g. floats). `__index__`\nbacks **lossless integer use** — slicing, `bin()`, `hex()`, `range()` — and signals\n\"this *is* an integer.\" Define `__index__` for true integer-like types.\n\n```python\nclass Hours:\n    def __init__(self, n): self.n = n\n    def __index__(self):\n        return self.n          # enables list[Hours(2)] and range\u002Fbin\u002Fhex\n\n\"abcdef\"[Hours(2)]     # 'c' — slicing uses __index__\nbin(Hours(5))          # '0b101'\n```\n\nRule of thumb: implement `__index__` (not just `__int__`) when your type should be\nusable as an index or in bit\u002Fformat operations without lossy conversion.\n",{"id":3154,"difficulty":253,"q":3155,"a":3156},"contains-dunder","How does `__contains__` customize the `in` operator?","`x in obj` calls **`__contains__(x)`** if defined. Without it, Python falls back to\niterating (`__iter__`) or the sequence protocol (`__getitem__`). Defining it lets you\nprovide fast or custom membership logic.\n\n```python\nclass Range2D:\n    def __init__(self, w, h): self.w, self.h = w, h\n    def __contains__(self, point):\n        x, y = point\n        return 0 \u003C= x \u003C self.w and 0 \u003C= y \u003C self.h\n\ngrid = Range2D(3, 3)\n(1, 2) in grid     # True\n(5, 0) in grid     # False\n```\n\nRule of thumb: add `__contains__` for O(1)\u002Fcustom membership; otherwise `in` works\nbut does a linear scan via iteration.\n",{"description":148},"Python interview questions on dunder\u002Fmagic methods — __repr__\u002F__str__, __eq__ and __hash__, operator overloading with __add__, the sequence protocol (__len__\u002F__getitem__), __call__, and functools.total_ordering.","python\u002Foop\u002Fdunder-methods","Dunder \u002F Magic Methods","M_-qItFTjd3GztnNaRoiKmk5Wc_IJEcE2wb3mR8QV6o",{"id":3163,"title":3164,"body":3165,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":3169,"navigation":153,"order":11,"path":3170,"questions":3171,"questionsCount":217,"related":218,"seo":3232,"seoDescription":3233,"stem":3234,"subtopic":3235,"topic":117,"topicSlug":119,"updated":223,"__hash__":3236},"qa\u002Fpython\u002Fstdlib\u002Fdatetime.md","Datetime",{"type":145,"value":3166,"toc":3167},[],{"title":148,"searchDepth":29,"depth":29,"links":3168},[],{},"\u002Fpython\u002Fstdlib\u002Fdatetime",[3172,3176,3180,3184,3188,3192,3196,3200,3204,3208,3212,3216,3220,3224,3228],{"id":3173,"difficulty":253,"q":3174,"a":3175},"datetime-date-time","What is the difference between datetime, date, and time?","The `datetime` module provides three core types. **`date`** holds a calendar date\n(year, month, day) with **no time**. **`time`** holds a time of day (hour, minute,\nsecond, microsecond) with **no date**. **`datetime`** combines **both** into a\nsingle timestamp.\n\n```python\nfrom datetime import date, time, datetime\n\nd = date(2026, 6, 18)               # just the date\nt = time(14, 30, 0)                 # just the time of day\ndt = datetime(2026, 6, 18, 14, 30)  # date + time together\n\ndt.date()                           # -> date(2026, 6, 18)\ndt.time()                           # -> time(14, 30)\ndate.today()                        # current date\n```\n\nUse `date` for things like birthdays or due dates where time is irrelevant, `time`\nfor a recurring clock time, and `datetime` for actual events\u002Ftimestamps. Most\nreal-world work uses `datetime`.\n",{"id":3177,"difficulty":150,"q":3178,"a":3179},"naive-aware-zoneinfo","What is the difference between naive and timezone-aware datetimes?","A **naive** datetime has **no timezone info** (`tzinfo` is `None`) — it's just wall\nclock numbers with no reference point, so it's ambiguous. An **aware** datetime\ncarries a `tzinfo`, pinning it to an actual instant. Use the stdlib **`zoneinfo`**\nmodule (Python 3.9+) to attach real IANA timezones.\n\n```python\nfrom datetime import datetime\nfrom zoneinfo import ZoneInfo\n\nnaive = datetime(2026, 6, 18, 14, 30)          # ambiguous — no tz\naware = datetime(2026, 6, 18, 14, 30,\n                 tzinfo=ZoneInfo(\"America\u002FNew_York\"))\n\nutc = aware.astimezone(ZoneInfo(\"UTC\"))        # convert between zones\n```\n\nYou **can't compare or subtract** a naive and an aware datetime — it raises\n`TypeError`. Best practice: store and compute in **UTC-aware** datetimes, and convert\nto local zones only for display.\n",{"id":3181,"difficulty":162,"q":3182,"a":3183},"strftime-strptime","What is the difference between strftime and strptime?","They are inverses. **`strftime`** (\"string **f**rom time\") **formats** a datetime\n**into** a string using format codes. **`strptime`** (\"string **p**arse time\")\n**parses** a string **into** a datetime using a matching format.\n\n```python\nfrom datetime import datetime\n\ndt = datetime(2026, 6, 18, 14, 30)\ns = dt.strftime(\"%Y-%m-%d %H:%M\")       # datetime -> \"2026-06-18 14:30\"\n\nparsed = datetime.strptime(\"2026-06-18 14:30\",\n                           \"%Y-%m-%d %H:%M\")  # str -> datetime\n```\n\nCommon codes: `%Y` (4-digit year), `%m` (month), `%d` (day), `%H` (24-hour),\n`%M` (minute), `%S` (second). To remember: **f** = format (out), **p** = parse (in).\nFor standard ISO strings, `datetime.fromisoformat()` \u002F `.isoformat()` are simpler.\n",{"id":3185,"difficulty":162,"q":3186,"a":3187},"timedelta-arithmetic","How does timedelta arithmetic work?","A **`timedelta`** represents a **duration** — a difference between two points in time.\nSubtracting two datetimes yields a `timedelta`; adding a `timedelta` to a datetime\nshifts it. A `timedelta` stores days, seconds, and microseconds.\n\n```python\nfrom datetime import datetime, timedelta\n\nstart = datetime(2026, 6, 18, 9, 0)\nend   = datetime(2026, 6, 18, 17, 30)\n\nworked = end - start              # timedelta(seconds=30600)\nworked.total_seconds()            # 30600.0\nworked.seconds \u002F\u002F 3600            # 8 (hours portion)\n\ntomorrow = start + timedelta(days=1)      # shift forward\nweek_ago = start - timedelta(weeks=1)     # shift back\n```\n\nUse `total_seconds()` to get the whole duration as a number (the `.seconds`\nattribute is only the sub-day part). `timedelta` makes date math safe — it correctly\nrolls over months and years.\n",{"id":3189,"difficulty":150,"q":3190,"a":3191},"now-vs-utcnow","What is the pitfall with datetime.now() vs utcnow()?","The big trap: **both `datetime.now()` and the old `datetime.utcnow()` return *naive*\ndatetimes**. `now()` gives local wall time, `utcnow()` gives the UTC wall time — but\n**neither attaches a tzinfo**, so a `utcnow()` value silently *looks* like local\ntime and corrupts later conversions. `utcnow()` is **deprecated** in modern Python.\n\n```python\nfrom datetime import datetime\nfrom zoneinfo import ZoneInfo\n\ndatetime.now()                       # naive, local time — ambiguous\ndatetime.utcnow()                    # naive, but labelled nothing! (deprecated)\n\n# correct: an AWARE UTC timestamp\nnow_utc = datetime.now(ZoneInfo(\"UTC\"))\nlocal = datetime.now(ZoneInfo(\"America\u002FNew_York\"))\n```\n\nRule of thumb: **always pass a timezone to `now()`** to get an aware datetime, and\navoid `utcnow()` entirely. Store timestamps as UTC-aware and convert for display.\n",{"id":3193,"difficulty":253,"q":3194,"a":3195},"fromisoformat","How do you parse and produce ISO 8601 datetime strings?","Use **`datetime.fromisoformat()`** to parse and **`.isoformat()`** to produce ISO 8601\nstrings — simpler and faster than `strptime`\u002F`strftime` for the standard format.\nPython 3.11+ parses a much wider range (including `Z` suffix).\n\n```python\nfrom datetime import datetime\n\ndt = datetime.fromisoformat(\"2026-06-18T14:30:00+00:00\")\ndt.isoformat()        # '2026-06-18T14:30:00+00:00'\ndatetime.fromisoformat(\"2026-06-18T14:30:00Z\")   # 3.11+ accepts 'Z'\n```\n\nRule of thumb: prefer `fromisoformat`\u002F`isoformat` for ISO strings (APIs, JSON,\ndatabases); reserve `strptime`\u002F`strftime` for non-standard custom formats.\n",{"id":3197,"difficulty":162,"q":3198,"a":3199},"timestamp-epoch","How do you convert between datetimes and Unix timestamps?","`.timestamp()` converts an **aware** datetime to a Unix epoch float (seconds since\n1970 UTC); `datetime.fromtimestamp(ts, tz)` converts back. For naive datetimes,\n`.timestamp()` assumes local time — another reason to stay aware.\n\n```python\nfrom datetime import datetime\nfrom zoneinfo import ZoneInfo\n\ndt = datetime(2026, 6, 18, 14, 30, tzinfo=ZoneInfo(\"UTC\"))\nts = dt.timestamp()                          # 1781879400.0\ndatetime.fromtimestamp(ts, ZoneInfo(\"UTC\"))  # back to the datetime\n```\n\nRule of thumb: always pass a tz to `fromtimestamp` and use aware datetimes with\n`.timestamp()` to avoid silent local-time assumptions.\n",{"id":3201,"difficulty":150,"q":3202,"a":3203},"dst-handling","How does `zoneinfo` handle Daylight Saving Time transitions?","`zoneinfo` applies the correct UTC offset for the date, so DST is handled\nautomatically — but **arithmetic in local time** can land on ambiguous (fall-back) or\nnonexistent (spring-forward) times. Do math in UTC, then convert.\n\n```python\nfrom datetime import datetime, timedelta\nfrom zoneinfo import ZoneInfo\n\nny = ZoneInfo(\"America\u002FNew_York\")\nbefore = datetime(2026, 3, 8, 1, 30, tzinfo=ny)   # just before spring-forward\n# adding 1 hour in local time may skip the 2 AM that doesn't exist\nutc = before.astimezone(ZoneInfo(\"UTC\")) + timedelta(hours=1)\nutc.astimezone(ny)                                 # correct wall time\n```\n\nRule of thumb: store\u002Fcompute in UTC and convert to local only for display — never do\nduration math directly on DST-affected local datetimes.\n",{"id":3205,"difficulty":162,"q":3206,"a":3207},"date-parsing-formats","What are the most common `strftime`\u002F`strptime` format codes?","Memorize the everyday codes: `%Y`\u002F`%y` (4-\u002F2-digit year), `%m` month, `%d` day, `%H`\n24-hour \u002F `%I` 12-hour, `%M` minute, `%S` second, `%p` AM\u002FPM, `%A`\u002F`%a` weekday name,\n`%B`\u002F`%b` month name, `%z` UTC offset, `%j` day-of-year.\n\n```python\nfrom datetime import datetime\ndt = datetime(2026, 6, 18, 14, 30)\ndt.strftime(\"%A, %B %d, %Y\")     # 'Thursday, June 18, 2026'\ndt.strftime(\"%I:%M %p\")          # '02:30 PM'\ndatetime.strptime(\"06\u002F18\u002F26\", \"%m\u002F%d\u002F%y\")\n```\n\nRule of thumb: `%Y-%m-%d %H:%M:%S` covers most needs; case matters (`%M` minute vs\n`%m` month, `%H` 24h vs `%I` 12h).\n",{"id":3209,"difficulty":162,"q":3210,"a":3211},"replace-and-immutability","Why are datetime objects immutable and how do you \"modify\" one?","`date`\u002F`time`\u002F`datetime` are **immutable** (and hashable, so usable as dict keys). To\nget a changed version, use **`.replace()`**, which returns a **new** object with the\ngiven fields swapped.\n\n```python\nfrom datetime import datetime\ndt = datetime(2026, 6, 18, 14, 30)\nmidnight = dt.replace(hour=0, minute=0)   # new datetime\ndt                                         # unchanged\n\n# attach a timezone to a naive datetime:\nfrom zoneinfo import ZoneInfo\naware = dt.replace(tzinfo=ZoneInfo(\"UTC\"))\n```\n\nRule of thumb: datetimes never mutate — use `.replace()` for tweaks and `timedelta`\narithmetic for shifts; note `.replace(tzinfo=...)` *labels* without converting.\n",{"id":3213,"difficulty":162,"q":3214,"a":3215},"utc-now-modern","What's the modern replacement for `datetime.utcnow()`?","Use **`datetime.now(timezone.utc)`** (or `ZoneInfo(\"UTC\")`) to get an **aware** UTC\ntimestamp. `utcnow()`\u002F`utcfromtimestamp()` are deprecated in 3.12+ precisely because\nthey return naive values that misrepresent UTC as local.\n\n```python\nfrom datetime import datetime, timezone\n\ndatetime.now(timezone.utc)        # aware UTC — recommended\n# NOT: datetime.utcnow()          # deprecated, returns naive\n```\n\nRule of thumb: replace every `utcnow()` with `now(timezone.utc)` to get a correct,\ntz-aware UTC timestamp.\n",{"id":3217,"difficulty":253,"q":3218,"a":3219},"comparing-sorting-dates","How do you compare and sort datetimes?","Datetimes support the comparison operators and sort chronologically, so `sorted()`,\n`min`, and `max` work directly. The only rule: you **cannot compare naive with\naware** datetimes — it raises `TypeError`.\n\n```python\nfrom datetime import datetime\nevents = [datetime(2026, 6, 18), datetime(2026, 1, 1), datetime(2026, 12, 31)]\nsorted(events)        # chronological order\nmax(events)           # latest\n\n# datetime(2026,1,1) \u003C datetime(2026,1,1, tzinfo=utc)  # TypeError!\n```\n\nRule of thumb: keep all datetimes consistently naive or consistently aware before\ncomparing\u002Fsorting — mixing the two errors out.\n",{"id":3221,"difficulty":162,"q":3222,"a":3223},"time-module-vs-datetime","When do you use the `time` module instead of `datetime`?","Use the lower-level **`time`** module for **epoch seconds**, sleeping, and\n**performance measurement**. `time.time()` gives wall-clock epoch; `time.perf_counter()`\nand `time.monotonic()` are for measuring durations (immune to clock changes).\n\n```python\nimport time\nstart = time.perf_counter()\ntime.sleep(0.1)\nelapsed = time.perf_counter() - start   # high-resolution duration\n\ntime.time()          # Unix epoch seconds (wall clock)\ntime.monotonic()     # never goes backwards — good for timeouts\n```\n\nRule of thumb: `datetime` for calendar dates\u002Ftimes; `time.perf_counter`\u002F`monotonic`\nfor measuring elapsed time, and `time.sleep` to pause.\n",{"id":3225,"difficulty":253,"q":3226,"a":3227},"calendar-and-weekday","How do you get the weekday or work with calendar info?","`date.weekday()` returns 0=Monday..6=Sunday (`isoweekday()` is 1=Monday..7=Sunday).\nThe **`calendar`** module gives month\u002Fyear grids, leap-year checks, and month lengths.\n\n```python\nfrom datetime import date\nimport calendar\n\nd = date(2026, 6, 18)\nd.weekday()                 # 3 (Thursday)\nd.isoweekday()              # 4\ncalendar.isleap(2024)       # True\ncalendar.monthrange(2026, 6)  # (0, 30) -> (first weekday, days in month)\n```\n\nRule of thumb: `weekday()`\u002F`isoweekday()` for day-of-week logic; reach for the\n`calendar` module for month lengths, leap years, and calendar layouts.\n",{"id":3229,"difficulty":150,"q":3230,"a":3231},"parsing-timezone-aware","How do you parse a string into a timezone-aware datetime?","Either parse an offset-bearing ISO string with `fromisoformat`, or parse a naive\ndatetime and **attach** a zone with `.replace(tzinfo=...)` (label) or `.astimezone()`\n(convert). `strptime` with `%z` reads explicit offsets.\n\n```python\nfrom datetime import datetime\nfrom zoneinfo import ZoneInfo\n\ndatetime.fromisoformat(\"2026-06-18T14:30+05:30\")     # aware directly\ndatetime.strptime(\"2026-06-18 +0530\", \"%Y-%m-%d %z\") # %z reads the offset\n\nnaive = datetime(2026, 6, 18, 14, 30)\nnaive.replace(tzinfo=ZoneInfo(\"Asia\u002FKolkata\"))       # label as that zone\n```\n\nRule of thumb: use `%z`\u002F`fromisoformat` when the string carries an offset; use\n`.replace(tzinfo=...)` to label a naive value as a known zone (no time shift).\n",{"description":148},"Python interview questions on datetime vs date vs time, naive vs timezone-aware datetimes with zoneinfo, strftime\u002Fstrptime, timedelta arithmetic, and the now() vs utcnow() pitfall.","python\u002Fstdlib\u002Fdatetime","datetime","EI8ax31jKUY4k64DRHqpnQMDESppT0Qm5qHb83-8rk8",{"id":3238,"title":3239,"body":3240,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":3244,"navigation":153,"order":46,"path":3245,"questions":3246,"questionsCount":217,"related":218,"seo":3306,"seoDescription":3307,"stem":3308,"subtopic":3309,"topic":90,"topicSlug":92,"updated":223,"__hash__":3310},"qa\u002Fpython\u002Fconcurrency\u002Fconcurrent-futures.md","Concurrent Futures",{"type":145,"value":3241,"toc":3242},[],{"title":148,"searchDepth":29,"depth":29,"links":3243},[],{},"\u002Fpython\u002Fconcurrency\u002Fconcurrent-futures",[3247,3251,3254,3258,3262,3266,3270,3274,3278,3282,3286,3290,3294,3298,3302],{"id":3248,"difficulty":162,"q":3249,"a":3250},"executor-abstraction","What is the Executor abstraction in concurrent.futures?","`concurrent.futures` provides a **high-level**, uniform interface for running\ncallables asynchronously. An **`Executor`** manages a **pool of workers** and\nhands you back **`Future`** objects representing pending results — and the **same\nAPI** works whether the workers are threads or processes, so you can swap one for\nthe other with a one-line change.\n\n```python\nfrom concurrent.futures import ThreadPoolExecutor\n\ndef work(x):\n    return x * 2\n\nwith ThreadPoolExecutor(max_workers=4) as ex:   # context manager auto-shuts down\n    future = ex.submit(work, 10)                # schedule the call\n    print(future.result())                      # 20\n\n# swap to processes by changing only the class name:\n# with ProcessPoolExecutor() as ex: ...\n```\n\nThe context manager (`with`) cleanly handles worker shutdown and waits for\npending work on exit. Rule of thumb: prefer `concurrent.futures` over raw\n`threading`\u002F`multiprocessing` when you just want to run a function over a pool\nand collect results.\n",{"id":3252,"difficulty":162,"q":175,"a":3253},"thread-vs-process-pool","Both share the Executor API but differ in workers. **`ThreadPoolExecutor`** runs\ntasks in **threads** within one process — cheap, shared memory, but bound by the\n**GIL**, so it only helps **I\u002FO-bound** work. **`ProcessPoolExecutor`** runs tasks\nin **separate processes**, each with its own GIL, giving **true parallelism** for\n**CPU-bound** work (at the cost of pickling arguments\u002Fresults).\n\n```python\nfrom concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor\n\n# I\u002FO-bound: many network\u002Fdisk waits -> threads\nwith ThreadPoolExecutor() as ex:\n    ex.map(download, urls)\n\n# CPU-bound: number crunching -> processes\nwith ProcessPoolExecutor() as ex:\n    ex.map(crunch, datasets)\n```\n\nBecause the API is identical, you can prototype with threads and switch to\nprocesses if the GIL becomes the bottleneck. Rule of thumb: I\u002FO-bound ->\n`ThreadPoolExecutor`; CPU-bound -> `ProcessPoolExecutor`.\n",{"id":3255,"difficulty":162,"q":3256,"a":3257},"submit-and-futures","What does submit() return, and what is a Future?","**`submit(fn, *args)`** schedules `fn` to run in the pool and **immediately**\nreturns a **`Future`** — a handle to a result that may not exist yet. The Future\nlets you check status (`done()`, `running()`), **block for the result**\n(`result()`), retrieve an exception (`exception()`), `cancel()`, or attach a\ncallback (`add_done_callback`).\n\n```python\nfrom concurrent.futures import ThreadPoolExecutor\n\ndef slow_double(x):\n    return x * 2\n\nwith ThreadPoolExecutor() as ex:\n    fut = ex.submit(slow_double, 21)   # returns instantly\n    print(fut.done())                  # False — probably still running\n    print(fut.result())                # 42 — blocks until ready\n```\n\n`result(timeout=...)` blocks (up to an optional timeout) until the value is\nready. A Future decouples **starting** the work from **collecting** it, which is\nwhat makes overlapping multiple calls possible.\n",{"id":3259,"difficulty":253,"q":3260,"a":3261},"executor-map","How does executor.map work and how does it differ from submit?","**`executor.map(fn, iterable)`** is the convenient bulk form: it applies `fn` to\nevery item concurrently and returns an **iterator of results in input order** —\nanalogous to the built-in `map`, but parallel. **`submit`** is lower level,\ngiving you a Future per call for fine-grained control.\n\n```python\nfrom concurrent.futures import ThreadPoolExecutor\n\ndef fetch(url):\n    return len(url)\n\nurls = [\"a\", \"bb\", \"ccc\"]\nwith ThreadPoolExecutor() as ex:\n    for result in ex.map(fetch, urls):   # results stream back in order\n        print(result)                    # 1, 2, 3\n```\n\n`map` is great when you have a clean iterable and want **ordered** results with\nminimal code. Use `submit` (often with `as_completed`) when you need results\n**as they finish**, per-task error handling, or cancellation. Note `map` raises\nthe first exception when you iterate to that result.\n",{"id":3263,"difficulty":162,"q":3264,"a":3265},"as-completed","What does as_completed do?","**`as_completed(futures)`** yields each Future **as soon as it finishes**,\nregardless of submission order — so you can process results the moment they're\nready instead of waiting for the slowest task to keep its place (as `map`'s\nordered output would).\n\n```python\nfrom concurrent.futures import ThreadPoolExecutor, as_completed\n\ndef work(n):\n    return n * n\n\nwith ThreadPoolExecutor() as ex:\n    futures = [ex.submit(work, i) for i in range(5)]\n    for fut in as_completed(futures):     # whichever finishes first\n        print(fut.result())               # order is non-deterministic\n```\n\nThis is ideal for **responsiveness** — show progress as tasks complete, or\nhandle failures immediately. Use `map` when you want results in input order; use\n`as_completed` when you want them in **completion** order.\n",{"id":3267,"difficulty":162,"q":3268,"a":3269},"exception-propagation","How are exceptions handled with futures?","An exception raised inside a worker is **captured and stored** in its Future,\nnot raised at submit time. It **re-raises when you call `future.result()`** (or\niterate to that item in `map`). You can also inspect it without raising via\n**`future.exception()`**.\n\n```python\nfrom concurrent.futures import ThreadPoolExecutor\n\ndef boom(x):\n    raise ValueError(f\"bad: {x}\")\n\nwith ThreadPoolExecutor() as ex:\n    fut = ex.submit(boom, 1)\n    err = fut.exception()        # returns the ValueError, doesn't raise\n    print(err)                   # bad: 1\n    fut.result()                 # NOW it re-raises ValueError\n```\n\nThe implication: if you never call `result()` (or check `exception()`), an\nerror can **pass silently**. Always retrieve results — typically in a `try\u002Fexcept`\naround `result()` — so worker failures surface in the main thread.\n",{"id":3271,"difficulty":162,"q":3272,"a":3273},"with-statement-shutdown","Why use an Executor as a context manager?","The **`with` block calls `shutdown(wait=True)`** on exit, which **blocks\nuntil all submitted work finishes** and releases the worker threads\u002Fprocesses.\nWithout it you must call `shutdown()` yourself or risk the program exiting\nwith work still pending or threads leaking.\n\n```python\nfrom concurrent.futures import ThreadPoolExecutor\n\nwith ThreadPoolExecutor(max_workers=4) as ex:\n    futures = [ex.submit(work, i) for i in range(10)]\n# \u003C- here the pool is fully drained and cleaned up\n\n# equivalent without `with`:\nex = ThreadPoolExecutor()\n...\nex.shutdown(wait=True)\n```\n\nRule of thumb: always use the `with` form so you never leak a pool or exit\nbefore background work completes.\n",{"id":3275,"difficulty":162,"q":3276,"a":3277},"max-workers-default","What is the default max_workers for the executors?","They differ. **`ThreadPoolExecutor`** defaults to `min(32, os.cpu_count() +\n4)` — generous because threads are cheap and mostly used for I\u002FO.\n**`ProcessPoolExecutor`** defaults to **`os.cpu_count()`** — one worker per\ncore, since more processes than cores won't speed up CPU-bound work.\n\n```python\nimport os\nfrom concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor\n\nThreadPoolExecutor()           # ~ min(32, cpu_count()+4) threads\nProcessPoolExecutor()          # ~ cpu_count() processes\n```\n\nRule of thumb: tune `max_workers` to the workload — high for I\u002FO-bound\nthreads, roughly core-count for CPU-bound processes.\n",{"id":3279,"difficulty":162,"q":3280,"a":3281},"map-timeout-chunksize","What do the timeout and chunksize arguments to map do?","**`timeout`** caps how long you'll wait when iterating results — if exceeded,\niteration raises `TimeoutError`. **`chunksize`** (ProcessPoolExecutor only)\nbatches that many items per task sent to a worker, cutting **pickling\u002FIPC\noverhead** for large inputs; it has no effect on ThreadPoolExecutor.\n\n```python\nwith ProcessPoolExecutor() as ex:\n    # send work in batches of 100 -> far fewer IPC round trips\n    for r in ex.map(f, big_iterable, chunksize=100):\n        ...\n```\n\nRule of thumb: for many small CPU tasks, raise `chunksize` to amortize\nper-task overhead; profile to find a good value.\n",{"id":3283,"difficulty":162,"q":3284,"a":3285},"cancel-future","Can you cancel a submitted future?","Only if it **hasn't started running**. `future.cancel()` returns `True` and\ncancels a task still **queued**, but returns `False` once a worker has picked\nit up — there is **no preemption** of running work. `future.cancelled()`\ntells you the outcome.\n\n```python\nwith ThreadPoolExecutor(max_workers=1) as ex:\n    a = ex.submit(slow)      # starts immediately\n    b = ex.submit(slow)      # queued behind a\n    b.cancel()               # True  -> b never runs\n    a.cancel()               # False -> already running\n```\n\nRule of thumb: `cancel()` only helps for not-yet-started tasks; to stop\nrunning work you need your own cooperative flag\u002Fevent.\n",{"id":3287,"difficulty":162,"q":3288,"a":3289},"add-done-callback","What does add_done_callback do?","It registers a function that fires **when the Future completes** (success,\nexception, or cancellation), receiving the Future as its argument. It lets\nyou react to results **without blocking** on `result()`. The callback runs in\nthe thread that completed the future, so keep it quick.\n\n```python\ndef on_done(fut):\n    if fut.exception():\n        log.error(fut.exception())\n    else:\n        print(\"got\", fut.result())\n\nfut = ex.submit(work)\nfut.add_done_callback(on_done)     # called automatically when finished\n```\n\nRule of thumb: use callbacks for fire-and-forget reactions; use\n`as_completed`\u002F`result()` when you need to gather results in the main flow.\n",{"id":3291,"difficulty":150,"q":3292,"a":3293},"futures-vs-asyncio","How does concurrent.futures relate to asyncio?","`concurrent.futures` is **thread\u002Fprocess-based** with **blocking**\n`Future.result()`. asyncio is **single-threaded** with **awaitable**\n`asyncio.Future`. They bridge via **`loop.run_in_executor(pool, fn)`** (or\n`asyncio.to_thread`), which runs a blocking function in a `concurrent.futures`\npool and hands back an awaitable.\n\n```python\nimport asyncio\nfrom concurrent.futures import ProcessPoolExecutor\n\nasync def main():\n    loop = asyncio.get_running_loop()\n    with ProcessPoolExecutor() as pool:\n        result = await loop.run_in_executor(pool, cpu_heavy, data)\n```\n\nRule of thumb: use a ProcessPoolExecutor via `run_in_executor` to offload\nCPU-bound work from an asyncio program without blocking the loop.\n",{"id":3295,"difficulty":150,"q":3296,"a":3297},"deadlock-nested-pools","What concurrency pitfall comes from submitting to a pool from within a pool task?","A worker that **blocks on a Future from the same bounded pool** can\n**deadlock**: if all workers are busy waiting on tasks that can't be\nscheduled (no free workers), nothing progresses. This is a classic with a\nsingle-worker or fully saturated pool.\n\n```python\nwith ThreadPoolExecutor(max_workers=1) as ex:\n    def outer():\n        return ex.submit(inner).result()   # waits for a worker that's \"me\"\n    ex.submit(outer).result()              # deadlock\n```\n\nRule of thumb: don't have pool tasks block on results from the **same**\npool — restructure the work or use a separate pool \u002F different design.\n",{"id":3299,"difficulty":162,"q":3300,"a":3301},"initializer","What is the initializer argument used for?","**`initializer`\u002F`initargs`** run a setup function **once per worker** when it\nstarts, before it handles any task. It's used to set up expensive per-worker\nstate — a DB connection, a model, a logging config — that you don't want to\nrecreate on every call.\n\n```python\ndef setup(dsn):\n    global conn\n    conn = connect(dsn)            # one connection per worker\n\nwith ProcessPoolExecutor(initializer=setup, initargs=(dsn,)) as ex:\n    ex.map(query, ids)             # each worker reuses its own conn\n```\n\nRule of thumb: use `initializer` for one-time, per-worker resource setup\ninstead of building it inside every task.\n",{"id":3303,"difficulty":162,"q":3304,"a":3305},"wait-returnwhen","What does the return_when argument of concurrent.futures.wait do?","**`wait(futures, return_when=...)`** blocks until a condition is met and\nreturns `(done, not_done)` sets. The options are **`ALL_COMPLETED`** (default\n— wait for everything), **`FIRST_COMPLETED`** (return as soon as any finishes),\nand **`FIRST_EXCEPTION`** (return when one raises, else all done).\n\n```python\nfrom concurrent.futures import wait, FIRST_COMPLETED\n\ndone, pending = wait(futures, timeout=5, return_when=FIRST_COMPLETED)\nfor f in done:\n    print(f.result())          # handle whichever finished first\n```\n\nRule of thumb: use `as_completed` to stream results in completion order, and\n`wait(return_when=...)` when you need \"first done\" or \"first error\" semantics.\n",{"description":148},"Python interview questions on concurrent.futures: the Executor abstraction, ThreadPoolExecutor vs ProcessPoolExecutor, submit and Future objects, map, as_completed, and exception handling.","python\u002Fconcurrency\u002Fconcurrent-futures","concurrent.futures","nDcWv4KmLjv8FYG2PHk_22fMfq002mlAoKPrQ2WRWe8",{"id":3312,"title":3313,"body":3314,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":3318,"navigation":153,"order":46,"path":3319,"questions":3320,"questionsCount":1346,"related":218,"seo":3384,"seoDescription":3385,"stem":3386,"subtopic":3387,"topic":28,"topicSlug":30,"updated":223,"__hash__":3388},"qa\u002Fpython\u002Fdata-structures\u002Fsets.md","Sets",{"type":145,"value":3315,"toc":3316},[],{"title":148,"searchDepth":29,"depth":29,"links":3317},[],{},"\u002Fpython\u002Fdata-structures\u002Fsets",[3321,3325,3329,3333,3337,3340,3344,3348,3352,3356,3360,3364,3368,3372,3376,3380],{"id":3322,"difficulty":253,"q":3323,"a":3324},"set-operations","What are the main set operations in Python?","Sets support the classic mathematical operations, each with an **operator** and\nan equivalent **method**: **union** (`|`), **intersection** (`&`),\n**difference** (`-`), and **symmetric difference** (`^`, items in exactly one\nset).\n\n```python\na = {1, 2, 3}\nb = {2, 3, 4}\n\na | b    # {1, 2, 3, 4}      union — in either\na & b    # {2, 3}            intersection — in both\na - b    # {1}               difference — in a, not b\na ^ b    # {1, 4}            symmetric difference — in one, not both\n\na.union(b)              # method form, accepts any iterable\na.intersection([2, 3])  # b can be a list here\n```\n\nThe **operator** forms require both operands to be sets, while the **method**\nforms accept any iterable. Use them for fast \"what's common \u002F unique \u002F missing\"\nquestions instead of nested loops.\n",{"id":3326,"difficulty":162,"q":3327,"a":3328},"set-vs-list-membership","Why is membership testing faster in a set than a list?","A `set` is backed by a **hash table**, so `x in s` is **average O(1)** — it\nhashes `x` and checks one bucket. A `list` has no such index, so `x in lst` is\n**O(n)** — it scans elements one by one until it finds a match or reaches the\nend.\n\n```python\nbig_list = list(range(1_000_000))\nbig_set  = set(big_list)\n\n999_999 in big_list   # O(n) — scans up to a million items\n999_999 in big_set    # O(1) — single hash lookup\n```\n\nFor repeated membership checks over a large collection, converting to a set\nfirst is a huge win. The trade-off is that sets are **unordered** and elements\nmust be **hashable**. Rule of thumb: if you mostly ask \"is X in here?\", use a\nset, not a list.\n",{"id":3330,"difficulty":253,"q":3331,"a":3332},"dedup-with-set","How do you remove duplicates from a list using a set?","Wrapping a list in **`set()`** removes duplicates instantly, since a set can't\nhold repeated values. The catch is that a set is **unordered**, so this doesn't\npreserve the original order.\n\n```python\nnums = [3, 1, 2, 3, 1]\nunique = list(set(nums))      # e.g. [1, 2, 3] — order NOT guaranteed\n\n# order-preserving dedup (dict keys are unique AND ordered since 3.7):\nordered = list(dict.fromkeys(nums))   # [3, 1, 2]\n```\n\nUse `set()` when you only care about the **distinct values**; when order\nmatters, use **`dict.fromkeys()`**, which keeps first-seen order thanks to\nguaranteed dict ordering. Both require the elements to be hashable.\n",{"id":3334,"difficulty":253,"q":3335,"a":3336},"add-discard-remove","What is the difference between add, discard, and remove on a set?","**`add(x)`** inserts an element (a no-op if it's already present). To delete,\n**`remove(x)`** raises `KeyError` if the element is missing, while\n**`discard(x)`** removes it **silently** if present and does nothing otherwise.\n\n```python\ns = {1, 2, 3}\ns.add(2)          # already there — no change\ns.add(4)          # {1, 2, 3, 4}\n\ns.remove(4)       # {1, 2, 3}\ns.remove(99)      # KeyError — not in set\ns.discard(99)     # no error, no change\ns.pop()           # removes and returns an arbitrary element\n```\n\nChoose **`discard`** when \"remove if it's there\" is the intent (no need to guard\nwith a membership check), and **`remove`** when a missing element is genuinely an\nerror you want surfaced. `pop()` removes an arbitrary element since sets are\nunordered.\n",{"id":659,"difficulty":162,"q":3338,"a":3339},"What is a frozenset and when do you need one?","A **`frozenset`** is the **immutable** version of `set` — it supports all the\nread operations (union, intersection, membership) but has **no `add`\u002F`remove`**.\nBecause it's immutable, it's **hashable**, so it can be a **dict key** or an\n**element of another set**.\n\n```python\nfs = frozenset([1, 2, 3])\nfs.add(4)              # AttributeError — immutable\nfs & {2, 3, 4}         # frozenset({2, 3}) — set ops still work\n\n{fs: \"a group\"}        # usable as a dict key\n{frozenset({1, 2}), frozenset({3, 4})}   # a set OF sets\n```\n\nThe classic use is a **set of sets**: regular sets are unhashable, so the inner\nones must be frozensets. Also use a frozenset for a constant collection you want\nto guarantee can't be mutated. Reach for it whenever you need a set-like value\nthat must be hashable.\n",{"id":3341,"difficulty":253,"q":3342,"a":3343},"set-comprehension","What is a set comprehension?","A **set comprehension** builds a set in one expression with\n`{expr for item in iterable}`, automatically **deduplicating** the results. It's\nthe set sibling of list and dict comprehensions, with curly braces and no\n`key:value` pair.\n\n```python\nsquares = {n * n for n in range(-3, 4)}\n# {0, 1, 4, 9} — note 9 appears once even though -3 and 3 both map to it\n\nwords = [\"Hi\", \"hi\", \"HEY\"]\nlowered = {w.lower() for w in words}   # {'hi', 'hey'}\n```\n\nIt's ideal when you want **unique transformed values** in a single readable step.\nWatch out that `{}` alone is an **empty dict**, not an empty set — use `set()`\nfor an empty set. Use a set comprehension when both transformation and\ndeduplication are the goal.\n",{"id":3345,"difficulty":253,"q":3346,"a":3347},"empty-set-literal","How do you create an empty set?","With **`set()`**, not `{}` — curly braces with nothing inside create an\n**empty dict**. `{}` only becomes a set literal when it contains elements.\n\n```python\ntype({})         # \u003Cclass 'dict'>\ntype(set())      # \u003Cclass 'set'>\ntype({1, 2})     # \u003Cclass 'set'>\n```\n\nRule of thumb: remember `{}` is a dict; always use `set()` for an empty set.\n",{"id":3349,"difficulty":162,"q":3350,"a":3351},"set-element-hashable","What types can go into a set?","Only **hashable** (effectively immutable) objects — numbers, strings, tuples\nof hashables, frozensets. **Lists, dicts, and sets are unhashable** and raise\n`TypeError`. That's also why sets can't contain other (mutable) sets, but\n**can** contain frozensets.\n\n```python\n{1, \"a\", (2, 3)}         # fine\n{[1, 2]}                 # TypeError: unhashable type: 'list'\n{frozenset({1, 2})}      # fine -> frozenset is hashable\n```\n\nRule of thumb: only immutable\u002Fhashable values belong in a set; convert lists\nto tuples or sets to frozensets first.\n",{"id":3353,"difficulty":253,"q":3354,"a":3355},"set-unordered","Are sets ordered or indexable?","**Neither.** Sets are **unordered** and don't support indexing or slicing —\n`s[0]` raises `TypeError`. Iteration order is an implementation detail you\nshouldn't rely on. If you need order, sort into a list (`sorted(s)`) or keep a\nseparate list.\n\n```python\ns = {3, 1, 2}\ns[0]              # TypeError: 'set' object is not subscriptable\nsorted(s)         # [1, 2, 3]  -> get a defined order\n```\n\nRule of thumb: treat sets as bags for membership\u002Funiqueness, not as ordered\nsequences.\n",{"id":3357,"difficulty":162,"q":3358,"a":3359},"subset-superset","How do you test subset and superset relationships?","Use **`\u003C=`\u002F`.issubset()`** and **`>=`\u002F`.issuperset()`**; the strict `\u003C`\u002F`>`\nrequire **proper** (not equal) relationships. **`.isdisjoint()`** checks for\nno common elements without building an intersection.\n\n```python\n{1, 2} \u003C= {1, 2, 3}          # True  (subset)\n{1, 2} \u003C {1, 2}              # False (not proper)\n{1, 2}.isdisjoint({3, 4})    # True\n```\n\nRule of thumb: use the operator\u002Fmethod forms for readable set-relation checks;\n`isdisjoint` is cheaper than `a & b` when you only need \"do they overlap\".\n",{"id":3361,"difficulty":162,"q":3362,"a":3363},"set-mutating-ops","What is the difference between | and |= (and friends) on sets?","The operators (`|`, `&`, `-`, `^`) return a **new set**; the augmented forms\n(`|=`, `&=`, `-=`, `^=`) and named methods (`update`, `intersection_update`,\n…) **mutate in place**. Also, operators require both operands to be **sets**,\nwhile the methods accept any iterable.\n\n```python\na = {1, 2}\na | [3]                  # TypeError: needs a set\na.union([3])             # {1, 2, 3}  -> method takes any iterable\na |= {3}                 # in-place -> a == {1, 2, 3}\n```\n\nRule of thumb: use methods when the other operand is an arbitrary iterable;\nuse operators for set-to-set expressions.\n",{"id":3365,"difficulty":162,"q":3366,"a":3367},"symmetric-difference","What does symmetric difference do?","**`a ^ b`** (or `a.symmetric_difference(b)`) returns elements in **exactly one**\nof the two sets — everything except their intersection. It's the set\nequivalent of \"XOR\" and is handy for finding what changed between two\ncollections.\n\n```python\nold, new = {1, 2, 3}, {2, 3, 4}\nold ^ new                # {1, 4}  -> removed 1, added 4\n```\n\nRule of thumb: use `^` to find items that differ between two sets (added or\nremoved but not common).\n",{"id":3369,"difficulty":162,"q":3370,"a":3371},"set-vs-frozenset-perf","Do sets and frozensets have the same performance?","Yes for lookups — both give **O(1) average membership** via hashing. The\ndifference is mutability: `frozenset` is **immutable and hashable**, so it can\nbe a **dict key or a set element** and is safe to share. `set` supports\nadd\u002Fremove but can't be hashed.\n\n```python\ncache = {frozenset({1, 2}): \"result\"}    # frozenset as dict key\n{frozenset({1}), frozenset({2})}         # set of frozensets\n```\n\nRule of thumb: use `set` for mutable working data, `frozenset` when you need\nan immutable, hashable set (keys, set elements, constants).\n",{"id":3373,"difficulty":162,"q":3374,"a":3375},"dedupe-preserve-order","How do you dedupe a list but preserve order?","A plain `set()` loses order. Since dict preserves insertion order (3.7+), use\n**`dict.fromkeys(seq)`** to dedupe while keeping first-seen order, then convert\nto a list.\n\n```python\nitems = [3, 1, 3, 2, 1]\nlist(dict.fromkeys(items))     # [3, 1, 2]  -> order preserved\nlist(set(items))               # order undefined\n```\n\nRule of thumb: `set()` to dedupe when order doesn't matter; `dict.fromkeys`\nwhen it does.\n",{"id":3377,"difficulty":162,"q":3378,"a":3379},"set-complexity","What are the complexities of common set operations?","`add`, `remove`, and `in` are **O(1) average** (O(n) worst case with bad\nhashes). Union\u002Fintersection\u002Fdifference are roughly **O(len of the smaller\u002F\nlarger set)**. This is why sets beat lists for membership and dedup on large\ndata.\n\n```text\nx in s        -> O(1) avg\ns.add(x)      -> O(1) avg\na & b         -> O(min(len(a), len(b)))\na | b         -> O(len(a) + len(b))\n```\n\nRule of thumb: prefer sets when you do many membership tests or set algebra;\nlists for ordered, indexable sequences.\n",{"id":3381,"difficulty":253,"q":3382,"a":3383},"set-copy","Is set iteration\u002Fcopy shallow, and how do you copy a set?","`s.copy()` or `set(s)` make a **shallow** copy — a new set holding the **same\nelement objects**. Since set elements must be immutable\u002Fhashable, shallow is\nusually all you need; there's no nested-mutation concern like with lists.\n\n```python\na = {1, 2, 3}\nb = a.copy()\nb.add(4)                 # a unchanged -> {1, 2, 3}\n```\n\nRule of thumb: `set(s)`\u002F`s.copy()` is enough for sets; deep copy is rarely\nrelevant because elements are immutable.\n",{"description":148},"Python interview questions on set operations, O(1) membership vs lists, deduplication, add\u002Fdiscard\u002Fremove, frozensets, and set comprehensions.","python\u002Fdata-structures\u002Fsets","Sets & Frozensets","25yNN0KBEHYZnTNTo09LYVFeKMTJLOQ__8s-u5Jo-ho",{"id":3390,"title":3391,"body":3392,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":3396,"navigation":153,"order":46,"path":3397,"questions":3398,"questionsCount":217,"related":218,"seo":3459,"seoDescription":3460,"stem":3461,"subtopic":3462,"topic":45,"topicSlug":47,"updated":223,"__hash__":3463},"qa\u002Fpython\u002Ffunctions\u002Flambdas.md","Lambdas",{"type":145,"value":3393,"toc":3394},[],{"title":148,"searchDepth":29,"depth":29,"links":3395},[],{},"\u002Fpython\u002Ffunctions\u002Flambdas",[3399,3403,3407,3411,3415,3419,3423,3427,3431,3435,3439,3443,3447,3451,3455],{"id":3400,"difficulty":253,"q":3401,"a":3402},"lambda-syntax","What is a lambda and what are its limitations?","A **lambda** is an anonymous, single-**expression** function:\n`lambda args: expression`. It returns the expression's value automatically\n(no `return`). Its limitation is exactly that — it can hold only **one\nexpression**, no statements, assignments, loops, or annotations.\n\n```python\nsquare = lambda x: x * x\nsquare(5)                  # 25\n\nadd = lambda a, b=1: a + b # defaults allowed\nadd(10)                    # 11\n\n# NOT allowed: lambda x: (y = x; return y)  — no statements\n```\n\nBecause it's an expression, a lambda can be passed inline wherever a\nfunction is expected. Keep them short; anything needing multiple lines or a\ndocstring should be a named `def`.\n",{"id":3404,"difficulty":162,"q":3405,"a":3406},"lambda-vs-def","When should you use a lambda versus a def?","Use a **lambda** for a tiny throwaway function passed **inline** as an\nargument (a sort key, a callback). Use **`def`** for anything reused, named,\ndocumented, or non-trivial. A named function gives a useful `__name__` in\ntracebacks; a lambda just shows `\u003Clambda>`.\n\n```python\n# good lambda: inline, one-off\nsorted(words, key=lambda w: len(w))\n\n# prefer def: reused \u002F needs a name\ndef by_length(w):\n    return len(w)\n```\n\nPEP 8 even discourages **assigning** a lambda to a name\n(`f = lambda x: ...`) — if you need a name, just use `def`. Lambdas shine as\narguments, not as definitions.\n",{"id":3408,"difficulty":162,"q":3409,"a":3410},"higher-order-functions","What is a higher-order function?","A **higher-order function** is one that **takes a function as an argument\nand\u002For returns a function**. They enable composing and parameterizing\nbehavior. Built-in examples include `map`, `filter`, `sorted`, and the\n`functools` tools.\n\n```python\ndef apply_twice(fn, x):\n    return fn(fn(x))           # takes a function\n\napply_twice(lambda n: n + 3, 10)   # 16\n\nlist(map(str.upper, [\"a\", \"b\"]))   # ['A', 'B']\nlist(filter(lambda n: n > 0, [-1, 2, -3, 4]))  # [2, 4]\n```\n\nHigher-order functions are the foundation of functional-style Python and of\ndecorators (which both take and return functions). They let you pass\n*behavior* around as data.\n",{"id":3412,"difficulty":162,"q":3413,"a":3414},"key-argument","How does the key argument work in sorted, max, and min?","`key` takes a **function** applied to each element to derive the value used\nfor **comparison** — the elements themselves aren't changed, only how\nthey're ranked. It's used by `sorted`, `list.sort`, `max`, and `min`.\n\n```python\nwords = [\"banana\", \"kiwi\", \"apple\"]\n\nsorted(words, key=len)                 # ['kiwi', 'apple', 'banana']\nmax(words, key=len)                    # 'banana'\nsorted(words, key=str.lower)           # case-insensitive\n\npeople = [(\"ada\", 36), (\"grace\", 45)]\nmax(people, key=lambda p: p[1])        # ('grace', 45)\n```\n\n`key` is called **once per element** (efficient), unlike the old `cmp`\nstyle. For multi-level sorts, return a **tuple**:\n`key=lambda p: (p.last, p.first)`.\n",{"id":3416,"difficulty":162,"q":3417,"a":3418},"first-class-functions","What does it mean that functions are first-class objects?","In Python, functions are **first-class objects**: they can be assigned to\nvariables, stored in data structures, passed as arguments, and **returned**\nfrom other functions — just like any value. This is what makes higher-order\nfunctions and closures possible.\n\n```python\ndef shout(s): return s.upper() + \"!\"\n\nf = shout                 # assign to a variable\nf(\"hi\")                   # 'HI!'\n\ndispatch = {\"loud\": shout}        # store in a dict\ndispatch[\"loud\"](\"hey\")           # 'HEY!'\n\ndef make_op(op):                  # return a function\n    return (lambda a, b: a + b) if op == \"+\" else (lambda a, b: a - b)\nmake_op(\"+\")(2, 3)                # 5\n```\n\nTreating functions as values enables strategy\u002Fdispatch tables, callbacks,\nand decorators. There's no separate \"function pointer\" concept — the\nfunction *is* the object.\n",{"id":3420,"difficulty":253,"q":3421,"a":3422},"lambda-single-expression","Why can't a lambda contain statements?","A lambda's body is a **single expression** whose value is returned implicitly\n— it can't hold statements like assignments, `for`, `try`, or `return`. This\nkeeps lambdas small; anything needing statements should be a `def`.\n\n```python\nsquare = lambda x: x * x          # fine, an expression\nf = lambda x: (x += 1)            # SyntaxError: assignment is a statement\n# use a def instead when you need statements\n```\n\nRule of thumb: if a lambda won't fit in one expression, write a named `def`.\n",{"id":3424,"difficulty":162,"q":3425,"a":3426},"lambda-walrus","Can a lambda use conditionals or the walrus operator?","Yes — within the single-expression rule. A **conditional expression**\n(`a if cond else b`) and the **walrus** `:=` are expressions, so they're\nallowed; full `if\u002Felif` blocks are not.\n\n```python\ngrade = lambda s: \"pass\" if s >= 60 else \"fail\"\nf = lambda xs: (n := len(xs)) and sum(xs) \u002F n      # uses walrus\n```\n\nRule of thumb: ternaries and `:=` are fine in a lambda; branching logic beyond\nthat wants a `def`.\n",{"id":3428,"difficulty":162,"q":3429,"a":3430},"lambda-naming-pep8","Why does PEP 8 discourage assigning a lambda to a name?","Because `name = lambda ...` gives you a function with the **generic name\n`\u003Clambda>`** (worse tracebacks, no clear identity) yet none of the benefits of\n`def`. If you need a name, use `def`; reserve lambdas for **anonymous,\ninline** use.\n\n```python\nf = lambda x: x + 1          # PEP 8 flags this\ndef f(x): return x + 1       # preferred when naming\n```\n\nRule of thumb: lambdas for throwaway inline callables; `def` whenever the\nfunction gets a name.\n",{"id":3432,"difficulty":253,"q":3433,"a":3434},"lambda-as-callback","What are good uses for a lambda?","Short **inline callables** passed to higher-order functions: a `key=` for\n`sorted`\u002F`max`\u002F`min`, a quick predicate for `filter`, a callback for GUI\nevents, or a default factory. They shine when the logic is trivial and used\nonce.\n\n```python\nsorted(words, key=lambda w: len(w))\nmax(items, key=lambda x: x.score)\nbutton.on_click(lambda e: save())\n```\n\nRule of thumb: use a lambda when a one-line function is needed right where\nit's passed.\n",{"id":3436,"difficulty":162,"q":3437,"a":3438},"lambda-closure-capture","Do lambdas capture variables the same way as nested functions?","Yes — a lambda is just an anonymous function and forms a **closure** with the\nsame **late binding**. In a loop, all lambdas share the loop variable's final\nvalue; bind it with a default argument to capture per-iteration.\n\n```python\nhandlers = [lambda: i for i in range(3)]      # all -> 2\nhandlers = [lambda i=i: i for i in range(3)]  # -> 0, 1, 2\n```\n\nRule of thumb: lambdas inherit closure\u002Flate-binding rules; use `x=x` defaults\nto freeze loop values.\n",{"id":3440,"difficulty":162,"q":3441,"a":3442},"operator-module-vs-lambda","When should you use the operator module instead of a lambda?","For common \"get a field \u002F apply an operator\" keys, **`operator.itemgetter`,\n`attrgetter`, and `methodcaller`** are **faster and clearer** than equivalent\nlambdas (they're C-implemented and picklable). Reach for them in `sorted`,\n`max`, `map`, etc.\n\n```python\nfrom operator import itemgetter, attrgetter\nsorted(rows, key=itemgetter(1))            # vs lambda r: r[1]\nsorted(people, key=attrgetter(\"age\"))      # vs lambda p: p.age\n```\n\nRule of thumb: prefer `operator.*` helpers over lambdas for plain\nfield\u002Findex\u002Fmethod access.\n",{"id":3444,"difficulty":162,"q":3445,"a":3446},"hof-returning","What does it mean for a higher-order function to return a function?","A higher-order function can **take or return** functions. Returning one (a\nclosure\u002Ffactory) lets you build customized callables at runtime — the basis of\ndecorators, partial application, and strategy patterns.\n\n```python\ndef multiplier(n):\n    return lambda x: x * n       # returns a function\n\ntriple = multiplier(3)\ntriple(10)                       # 30\n```\n\nRule of thumb: returning functions enables configuration-by-closure;\ndecorators are the most common example.\n",{"id":3448,"difficulty":253,"q":3449,"a":3450},"map-filter-with-lambda","Should you pair lambdas with map and filter?","You *can*, but a **comprehension is usually clearer** when the transform is an\nexpression. Lambdas with `map`\u002F`filter` add visual noise; `map`\u002F`filter` are\nbest with **existing named functions**.\n\n```python\nlist(map(lambda x: x * 2, xs))        # noisy\n[x * 2 for x in xs]                    # clearer\nlist(map(str.upper, words))           # good: named function\n```\n\nRule of thumb: comprehension over `map`\u002F`filter`+lambda; use `map`\u002F`filter`\nwith named functions.\n",{"id":3452,"difficulty":162,"q":3453,"a":3454},"first-class-uses","What practical patterns do first-class functions enable?","Because functions are objects, you can **store them in data structures**, pass\nthem as **callbacks\u002Fstrategies**, build **dispatch tables**, and return them\nfrom factories. A dispatch dict replaces long if\u002Felif chains cleanly.\n\n```python\nops = {\"+\": lambda a, b: a + b, \"-\": lambda a, b: a - b}\nops[\"+\"](2, 3)                  # 5  -> dispatch table\n```\n\nRule of thumb: use function objects in dicts\u002Flists to replace branching with\nlookup-and-call.\n",{"id":3456,"difficulty":162,"q":3457,"a":3458},"callable-objects","How can an object behave like a function?","Define **`__call__`** so instances are **callable**. Unlike a plain function\nor lambda, a callable object can carry **state and configuration** on `self`\nacross calls — useful for stateful strategies, accumulators, or configurable\ntransforms.\n\n```python\nclass Adder:\n    def __init__(self, n): self.n = n\n    def __call__(self, x): return x + self.n\n\nadd5 = Adder(5)\nadd5(10)                        # 15\n```\n\nRule of thumb: use `__call__` when you need a \"function\" that also keeps\nmutable state or configuration.\n",{"description":148},"Python interview questions on lambda syntax and limitations, lambda vs def, higher-order functions, the key argument in sorted\u002Fmax\u002Fmin, and functions as first-class objects.","python\u002Ffunctions\u002Flambdas","Lambdas & Higher-Order Functions","JqrM4u5EjdcndHF1ywU8uQ6hUjENY0YQxbrhNxHl-8o",{"id":3465,"title":3466,"body":3467,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":3471,"navigation":153,"order":46,"path":3472,"questions":3473,"questionsCount":217,"related":218,"seo":3533,"seoDescription":3534,"stem":3535,"subtopic":3536,"topic":20,"topicSlug":21,"updated":223,"__hash__":3537},"qa\u002Fpython\u002Ffundamentals\u002Fstrings-formatting.md","Strings Formatting",{"type":145,"value":3468,"toc":3469},[],{"title":148,"searchDepth":29,"depth":29,"links":3470},[],{},"\u002Fpython\u002Ffundamentals\u002Fstrings-formatting",[3474,3478,3482,3486,3490,3494,3498,3501,3505,3509,3513,3517,3521,3525,3529],{"id":3475,"difficulty":162,"q":3476,"a":3477},"fstring-vs-format-vs-percent","What is the difference between f-strings, .format(), and % formatting?","All three interpolate values into strings. **`%`** is the oldest C-style syntax.\n**`str.format()`** uses `{}` placeholders and is more flexible. **f-strings**\n(Python 3.6+) embed expressions **inline** and are the fastest and most readable.\n\n```python\nname, n = \"Ada\", 3\n\"Hi %s, %d items\" % (name, n)        # old % style\n\"Hi {}, {} items\".format(name, n)    # str.format\nf\"Hi {name}, {n} items\"              # f-string — preferred\nf\"{n * 2 = }\"                        # \"n * 2 = 6\"  — self-documenting\n```\n\nRule of thumb: prefer **f-strings** for new code — they evaluate expressions\ndirectly and avoid the argument-ordering errors of `%` and `.format()`.\n",{"id":3479,"difficulty":162,"q":3480,"a":3481},"str-vs-bytes","What is the difference between str and bytes?","A **`str`** is a sequence of **Unicode code points** (text); **`bytes`** is a\nsequence of **raw bytes** (0-255). You convert between them with **`encode`**\n(str -> bytes) and **`decode`** (bytes -> str), specifying an encoding like UTF-8.\n\n```python\ns = \"café\"\nb = s.encode(\"utf-8\")    # b'caf\\xc3\\xa9'  — 5 bytes (é is 2)\nb.decode(\"utf-8\")        # \"café\"  — back to text\nlen(s), len(b)           # (4, 5)\ns + b                    # TypeError — can't mix str and bytes\n```\n\nWhy it matters: files and network sockets deal in **bytes**, your program logic in\n**str**. Rule of thumb: decode bytes to str as early as possible and encode back to\nbytes only at the I\u002FO boundary.\n",{"id":3483,"difficulty":253,"q":3484,"a":3485},"common-string-methods","What are the common string methods like split, strip, and join?","**`split`** breaks a string into a list on a separator; **`strip`** removes\nleading\u002Ftrailing whitespace (or given characters); **`join`** glues an iterable of\nstrings together with a separator. All return **new** strings since `str` is\nimmutable.\n\n```python\n\"  a,b,c  \".strip()            # \"a,b,c\"\n\"a,b,c\".split(\",\")            # [\"a\", \"b\", \"c\"]\n\",\".join([\"a\", \"b\", \"c\"])     # \"a,b,c\"\n\"Hello\".lower(), \"Hi\".upper() # (\"hello\", \"HI\")\n\"hello\".replace(\"l\", \"L\")     # \"heLLo\"\n```\n\nRule of thumb: `sep.join(list)` is the inverse of `text.split(sep)`, and chaining\nthese covers most everyday text wrangling.\n",{"id":3487,"difficulty":162,"q":3488,"a":3489},"join-vs-concat-loop","Why use join instead of += to build a string in a loop?","Strings are **immutable**, so each `+=` creates a **brand-new string** and copies\neverything so far — turning a loop into O(n^2) work and lots of garbage. **`join`**\nallocates the result **once**, making it O(n).\n\n```python\n# Slow — new string every iteration\nresult = \"\"\nfor word in words:\n    result += word\n\n# Fast — single allocation\nresult = \"\".join(words)\n```\n\nRule of thumb: collect pieces in a list (or generator) and call `\"\".join(...)` —\nit's the Python equivalent of a `StringBuilder`.\n",{"id":3491,"difficulty":253,"q":3492,"a":3493},"raw-strings","What is a raw string and when do you use it?","A **raw string** (`r\"...\"`) tells Python **not to process backslash escapes**, so\n`\\n`, `\\t`, etc. stay as literal backslash-plus-character. They're ideal for\n**regular expressions** and **Windows file paths**.\n\n```python\nprint(\"a\\tb\")    # a    b   — \\t is a tab\nprint(r\"a\\tb\")   # a\\tb     — backslash kept literally\n\nimport re\nre.findall(r\"\\d+\", \"x12y3\")   # ['12', '3']  — no double-backslashing\npath = r\"C:\\Users\\name\"       # backslashes stay intact\n```\n\nRule of thumb: reach for `r\"...\"` whenever your string is full of backslashes —\nit avoids the noise and bugs of escaping every one.\n",{"id":3495,"difficulty":162,"q":3496,"a":3497},"format-spec-mini-language","How does the format spec mini-language work (padding, :.2f)?","Inside `{}` (or after `:` in `format`), a **format spec** controls alignment,\nwidth, and precision: `{value:[fill][align][width][,][.precision][type]}`. It\nworks in f-strings and `str.format` alike.\n\n```python\nf\"{42:5}\"        # \"   42\"    — width 5, right-aligned (default for numbers)\nf\"{'hi':\u003C5}|\"    # \"hi   |\"   — left-align in width 5\nf\"{'hi':^5}|\"    # \" hi  |\"   — center\nf\"{42:05}\"       # \"00042\"    — zero-padded\nf\"{3.14159:.2f}\" # \"3.14\"     — 2 decimal places\nf\"{1234567:,}\"   # \"1,234,567\"  — thousands separator\nf\"{0.25:.1%}\"    # \"25.0%\"    — percentage\n```\n\nRule of thumb: `.2f` controls decimals, a number sets width, and `\u003C`\u002F`>`\u002F`^` set\nalignment — combine them for clean tabular output.\n",{"id":600,"difficulty":253,"q":3499,"a":3500},"What does it mean that strings are immutable?","A `str` object **cannot be changed in place** — every \"modification\" returns a new\nstring. You can't assign to an index. This enables string hashing (so strings can be\ndict keys\u002Fset members) and safe sharing.\n\n```python\ns = \"hello\"\ns[0] = \"H\"          # TypeError — item assignment not allowed\ns = \"H\" + s[1:]     # \"Hello\" — build a new string instead\n{\"key\": 1}          # works because str is hashable\u002Fimmutable\n```\n\nRule of thumb: treat strings as read-only values; to \"edit\" one, create a new string\n(or work in a list of chars and `\"\".join` at the end).\n",{"id":3502,"difficulty":253,"q":3503,"a":3504},"string-membership-and-search","How do you check for substrings and find their positions?","Use **`in`** for a boolean membership test, **`find`**\u002F`index` for positions, and\n`startswith`\u002F`endswith` for prefixes\u002Fsuffixes. `find` returns `-1` when missing;\n`index` raises `ValueError`.\n\n```python\n\"ell\" in \"hello\"          # True\n\"hello\".find(\"l\")         # 2   — first index, -1 if absent\n\"hello\".index(\"z\")        # ValueError\n\"hello\".count(\"l\")        # 2\n\"file.txt\".endswith((\".txt\", \".md\"))  # True — tuple of options\n```\n\nRule of thumb: use `in` for yes\u002Fno, `find` when \"not found\" is normal (it returns\n-1), and `index` when absence is an error.\n",{"id":3506,"difficulty":253,"q":3507,"a":3508},"string-case-and-test-methods","What string methods test or change case and content type?","Beyond `lower`\u002F`upper`, there's `title`, `capitalize`, `casefold` (aggressive\nlowercasing for caseless matching), and a family of `is*` predicates: `isdigit`,\n`isalpha`, `isalnum`, `isspace`, `isidentifier`.\n\n```python\n\"hello world\".title()    # \"Hello World\"\n\"ß\".casefold()           # \"ss\" — better than lower() for matching\n\"123\".isdigit()          # True\n\"abc1\".isalnum()         # True\n\"  \".isspace()           # True\n```\n\nRule of thumb: use `casefold()` for case-insensitive comparison and the `is*`\nmethods for quick input validation.\n",{"id":3510,"difficulty":150,"q":3511,"a":3512},"unicode-normalization","Why might two visually identical strings compare unequal?","Unicode lets the **same glyph** be encoded differently — e.g. \"é\" as one code point\nor as \"e\" + combining accent. They look identical but differ byte-for-byte. Use\n**`unicodedata.normalize`** to canonicalize before comparing.\n\n```python\nimport unicodedata\na = \"café\"                       # precomposed é\nb = \"café\"                 # e + combining accent\na == b                           # False!\nunicodedata.normalize(\"NFC\", a) == unicodedata.normalize(\"NFC\", b)  # True\n```\n\nRule of thumb: normalize user\u002Ftext input to a canonical form (NFC) before comparing,\ndeduping, or using strings as keys.\n",{"id":3514,"difficulty":253,"q":3515,"a":3516},"string-slicing","How does string slicing work, including negative steps?","Slicing uses `s[start:stop:step]` and returns a **new** string. Omitted bounds\ndefault to the ends; a negative `step` walks backwards (the classic reverse trick).\n\n```python\ns = \"abcdef\"\ns[1:4]     # \"bcd\"\ns[:3]      # \"abc\"\ns[-2:]     # \"ef\"\ns[::2]     # \"ace\"  — every other char\ns[::-1]    # \"fedcba\"  — reversed\n```\n\nRule of thumb: `s[::-1]` reverses, `s[a:b]` never errors on out-of-range bounds, and\nslices always copy (cheap for strings).\n",{"id":3518,"difficulty":162,"q":3519,"a":3520},"fstring-conversion-and-debug","What do `!r`, `!s`, and `=` do inside f-strings?","Conversion flags pick which dunder to call: **`!r`** uses `repr()`, **`!s`** uses\n`str()`, **`!a`** uses `ascii()`. The **`=`** specifier (3.8+) prints both the\nexpression text and its value — great for debugging.\n\n```python\nname = \"Ada\"\nf\"{name!r}\"      # \"'Ada'\"   — repr, keeps the quotes\nf\"{name!s}\"      # \"Ada\"     — str\nx = 5\nf\"{x = }\"        # \"x = 5\"   — self-documenting debug\nf\"{x=:.2f}\"      # \"x=5.00\"  — combine with a format spec\n```\n\nRule of thumb: use `!r` in logs\u002Ferrors to show quoting clearly, and `f\"{var=}\"` for\nquick print-debugging.\n",{"id":3522,"difficulty":253,"q":3523,"a":3524},"multiline-and-implicit-concat","How do triple-quoted strings and implicit concatenation work?","**Triple quotes** (`\"\"\"...\"\"\"`) span multiple lines and preserve newlines — used for\ndocstrings and block text. **Adjacent string literals** are joined at compile time\n(implicit concatenation), handy for splitting long literals.\n\n```python\ndoc = \"\"\"line 1\nline 2\"\"\"              # contains a real newline\n\nmsg = (\"part one \"     # implicit concatenation —\n       \"part two\")      # becomes \"part one part two\"\n\n# gotcha: a forgotten comma in a list silently concatenates!\nitems = [\"a\" \"b\", \"c\"]  # ['ab', 'c']\n```\n\nRule of thumb: triple quotes for multi-line\u002Fdocstrings; implicit concat for readable\nlong literals — but watch for missing commas in lists.\n",{"id":3526,"difficulty":162,"q":3527,"a":3528},"translate-and-maketrans","How do you efficiently replace or delete many characters at once?","Build a translation table with **`str.maketrans`** and apply it with **`translate`**\n— one pass for many character mappings or deletions, faster and cleaner than chained\n`replace` calls.\n\n```python\ntable = str.maketrans(\"aeiou\", \"AEIOU\")\n\"hello world\".translate(table)        # \"hEllO wOrld\"\n\ndrop = str.maketrans(\"\", \"\", \"!?.\")   # third arg = chars to delete\n\"h!e?l.lo\".translate(drop)            # \"hello\"\n```\n\nRule of thumb: for multiple single-char substitutions\u002Fremovals, `translate` beats\nstacking `.replace()`; for substrings or patterns, use `replace` or `re.sub`.\n",{"id":3530,"difficulty":162,"q":3531,"a":3532},"partition-vs-split","When would you use `partition` instead of `split`?","**`partition(sep)`** splits on the **first** occurrence and always returns a\n**3-tuple** `(before, sep, after)` — even when the separator is missing (then `sep`\nand `after` are empty). This avoids the unpacking errors `split` can cause.\n\n```python\n\"key=value=x\".split(\"=\", 1)   # ['key', 'value=x']\n\"key=value\".partition(\"=\")    # ('key', '=', 'value')\n\"novalue\".partition(\"=\")      # ('novalue', '', '')  — safe, no error\n\"a.b.c\".rpartition(\".\")       # ('a.b', '.', 'c')  — from the right\n```\n\nRule of thumb: use `partition`\u002F`rpartition` for clean \"split once into head\u002Fsep\u002Ftail\"\nwith guaranteed three parts; use `split` when you want a variable-length list.\n",{"description":148},"Python interview questions on f-strings vs .format vs %, str vs bytes, common string methods, join vs +=, raw strings, and the format spec mini-language.","python\u002Ffundamentals\u002Fstrings-formatting","Strings & String Formatting","IzjmjR8ZlNI5H0R-CDWbPbcRhkNzTTi73qsApKGSQIM",{"id":3539,"title":3540,"body":3541,"description":148,"difficulty":253,"extension":151,"framework":10,"frameworkSlug":8,"meta":3545,"navigation":153,"order":46,"path":3546,"questions":3547,"questionsCount":217,"related":218,"seo":3607,"seoDescription":3608,"stem":3609,"subtopic":3610,"topic":37,"topicSlug":38,"updated":223,"__hash__":3611},"qa\u002Fpython\u002Fiteration\u002Fenumerate-zip.md","Enumerate Zip",{"type":145,"value":3542,"toc":3543},[],{"title":148,"searchDepth":29,"depth":29,"links":3544},[],{},"\u002Fpython\u002Fiteration\u002Fenumerate-zip",[3548,3552,3556,3560,3564,3568,3572,3576,3580,3584,3588,3592,3596,3600,3603],{"id":3549,"difficulty":253,"q":3550,"a":3551},"enumerate-basics","What does enumerate do and how do you set its start?","`enumerate(iterable)` pairs each item with a running **index**, yielding\n`(index, value)` tuples. It saves you from the error-prone manual counter\nor `range(len(...))` pattern. The optional **`start`** argument sets the\nfirst index (default `0`).\n\n```python\ncolors = [\"red\", \"green\", \"blue\"]\n\nfor i, c in enumerate(colors):\n    print(i, c)            # 0 red \u002F 1 green \u002F 2 blue\n\nfor rank, c in enumerate(colors, start=1):\n    print(rank, c)         # 1 red \u002F 2 green \u002F 3 blue\n```\n\nPrefer `enumerate` over `range(len(seq))` whenever you need both the index\nand the item — it's more readable and works on any iterable, not just\nindexable sequences.\n",{"id":3553,"difficulty":253,"q":3554,"a":3555},"zip-basics","What does zip do and how does zip_longest differ?","`zip(a, b, ...)` walks several iterables **in parallel**, yielding tuples of\naligned elements. It stops at the **shortest** input, silently dropping\nextras. `itertools.zip_longest` instead runs to the **longest**, filling\nmissing slots with a `fillvalue`.\n\n```python\nnames = [\"ada\", \"grace\", \"edsger\"]\nages  = [36, 45]\n\nlist(zip(names, ages))     # [('ada', 36), ('grace', 45)]  — 'edsger' dropped\n\nfrom itertools import zip_longest\nlist(zip_longest(names, ages, fillvalue=0))\n# [('ada', 36), ('grace', 45), ('edsger', 0)]\n```\n\nUse plain `zip` when lengths match (or truncation is intended); reach for\n`zip_longest` when you must not lose data from the longer iterable.\n",{"id":3557,"difficulty":162,"q":3558,"a":3559},"unzip-with-star","How do you unzip a list of pairs with zip(*)?","`zip` is its own inverse. Applying `zip(*pairs)` **unpacks** the list of\ntuples as separate arguments, so `zip` re-groups them column-wise —\neffectively transposing rows into columns.\n\n```python\npairs = [(\"ada\", 36), (\"grace\", 45)]\n\nnames, ages = zip(*pairs)\nnames    # ('ada', 'grace')\nages     # (36, 45)\n```\n\nThis also transposes a matrix: `list(zip(*matrix))`. Note the results are\n**tuples**, not lists, and the input is consumed if it's an iterator — wrap\nin `list(...)` if you need list results.\n",{"id":3561,"difficulty":253,"q":3562,"a":3563},"extended-unpacking","How does extended (star) unpacking work?","A starred target in an assignment captures **\"the rest\"** as a list. You can\nplace `*name` at the start, middle, or end, and the non-starred names take\nfixed positions — Python figures out the split.\n\n```python\nfirst, *rest = [1, 2, 3, 4]      # first=1, rest=[2, 3, 4]\n*init, last = [1, 2, 3, 4]       # init=[1, 2, 3], last=4\nhead, *mid, tail = [1, 2, 3, 4]  # head=1, mid=[2, 3], tail=4\n```\n\nExactly **one** starred target is allowed per assignment, and it always\nbecomes a `list` (even if empty). This is cleaner than slicing for grabbing\nthe head\u002Ftail of a sequence.\n",{"id":3565,"difficulty":162,"q":3566,"a":3567},"zip-dict-star-args","How do you build a dict from zip and pass star-args in a call?","Pairing two sequences with `zip` and feeding them to `dict()` is the\nidiomatic way to build a mapping. The `*` and `**` operators also unpack\niterables\u002Fdicts **into function calls** as positional and keyword\narguments.\n\n```python\nkeys = [\"x\", \"y\", \"z\"]\nvals = [1, 2, 3]\nd = dict(zip(keys, vals))        # {'x': 1, 'y': 2, 'z': 3}\n\ndef point(x, y, z): return (x, y, z)\nargs = [1, 2, 3]\npoint(*args)                     # unpack list -> positional\npoint(**d)                       # unpack dict -> keyword args\n```\n\nUse `*` to spread a sequence into positional parameters and `**` to spread a\ndict into keyword parameters — the call-site mirror of `*args`\u002F`**kwargs` in\na function signature.\n",{"id":3569,"difficulty":162,"q":3570,"a":3571},"zip-strict","What does `zip(..., strict=True)` do?","In Python 3.10+, `zip(a, b, strict=True)` raises **`ValueError`** if the iterables\nhave **different lengths**, instead of silently truncating. It catches a class of\nbugs where you assumed two sequences were aligned.\n\n```python\nlist(zip([1, 2, 3], [\"a\", \"b\"]))                 # [(1,'a'),(2,'b')] — silent loss\nlist(zip([1, 2, 3], [\"a\", \"b\"], strict=True))    # ValueError — length mismatch\n```\n\nRule of thumb: pass `strict=True` when equal lengths are an invariant — fail loudly\nrather than silently dropping data from the longer iterable.\n",{"id":3573,"difficulty":253,"q":3574,"a":3575},"enumerate-vs-range-len","Why is `enumerate` preferred over `range(len(seq))`?","`enumerate` gives you the index **and** the item directly, works on **any iterable**\n(not just indexable sequences), and avoids repeated `seq[i]` lookups. `range(len())`\nis verbose, error-prone, and breaks on generators.\n\n```python\n# clunky and index-only:\nfor i in range(len(colors)):\n    print(i, colors[i])\n\n# clear, item in hand, works on any iterable:\nfor i, c in enumerate(colors):\n    print(i, c)\n```\n\nRule of thumb: if you find yourself writing `range(len(x))`, you almost always want\n`enumerate(x)` instead.\n",{"id":3577,"difficulty":162,"q":3578,"a":3579},"zip-with-comprehension","How do you combine `zip` with a comprehension for parallel processing?","`zip` shines inside comprehensions to combine aligned sequences element-wise —\nsumming pairs, building records, or comparing columns — in one readable expression.\n\n```python\nprices = [10, 20, 30]\nqtys   = [2, 1, 5]\n\ntotals = [p * q for p, q in zip(prices, qtys)]   # [20, 20, 150]\nrecords = [{\"price\": p, \"qty\": q} for p, q in zip(prices, qtys)]\ngrand = sum(p * q for p, q in zip(prices, qtys)) # 190\n```\n\nRule of thumb: `for a, b in zip(xs, ys)` inside a comprehension is the idiom for\nelement-wise combination of parallel sequences.\n",{"id":3581,"difficulty":162,"q":3582,"a":3583},"nested-unpacking","How does nested unpacking work in a for loop?","You can destructure **nested structure** directly in the loop target, mirroring the\nshape of each item. This is common with `enumerate` over pairs or zipped tuples.\n\n```python\npairs = [(1, (\"a\", \"b\")), (2, (\"c\", \"d\"))]\nfor num, (left, right) in pairs:\n    print(num, left, right)     # 1 a b \u002F 2 c d\n\nfor i, (name, age) in enumerate(zip(names, ages)):\n    ...                         # index + destructured pair\n```\n\nRule of thumb: match the loop target's shape to the item's shape — nested parentheses\nunpack nested tuples without manual indexing.\n",{"id":3585,"difficulty":162,"q":3586,"a":3587},"star-in-middle-call","Can you mix `*args` unpacking with regular arguments in a call?","Yes — you can interleave unpacked iterables with explicit positional args, and even\nuse **multiple** `*` unpackings in one call (3.5+). The same applies to `**` for\nkeywords. They're expanded left to right.\n\n```python\ndef f(a, b, c, d): return (a, b, c, d)\n\npair = [2, 3]\nf(1, *pair, 4)            # (1, 2, 3, 4) — star in the middle\nf(*[1, 2], *[3, 4])       # (1, 2, 3, 4) — multiple unpackings\n\nd1 = {\"a\": 1}; d2 = {\"b\": 2}\ndict(**d1, **d2)          # {'a': 1, 'b': 2} — merge via **\n```\n\nRule of thumb: `*`\u002F`**` can be combined with literals and each other in a call;\nPython flattens them positionally\u002Fby-keyword in order.\n",{"id":3589,"difficulty":162,"q":3590,"a":3591},"unpacking-in-literals","How do you use `*` and `**` to merge collections in literals?","Since 3.5, `*` unpacks iterables into list\u002Ftuple\u002Fset literals and `**` unpacks dicts\ninto dict literals — a clean way to **merge or extend** without `+`, `update`, or\n`extend`.\n\n```python\na = [1, 2]; b = [3, 4]\n[*a, *b, 5]              # [1, 2, 3, 4, 5]\n{*a, *b}                # {1, 2, 3, 4}\n\nd1 = {\"x\": 1}; d2 = {\"y\": 2}\n{**d1, **d2, \"z\": 3}    # {'x': 1, 'y': 2, 'z': 3}  — later keys win\n```\n\nRule of thumb: `[*a, *b]` and `{**d1, **d2}` are the idiomatic literal-merge forms;\nwith dicts, rightmost duplicate keys override earlier ones.\n",{"id":3593,"difficulty":162,"q":3594,"a":3595},"zip-is-lazy","Is `zip` lazy, and what's the consequence?","Yes — in Python 3 `zip` returns a **lazy iterator**, not a list. It produces tuples\non demand and is **single-pass**: once consumed, it's exhausted. Wrap in `list()` to\nmaterialize or reuse.\n\n```python\nz = zip([1, 2], [\"a\", \"b\"])\nlist(z)        # [(1, 'a'), (2, 'b')]\nlist(z)        # []  — already exhausted!\n\npairs = list(zip(xs, ys))   # materialize if you need it twice\n```\n\nRule of thumb: `zip` is a one-shot iterator — convert to a list if you must iterate\nmore than once or index into it.\n",{"id":3597,"difficulty":253,"q":3598,"a":3599},"enumerate-on-any-iterable","Does `enumerate` work on generators and other non-sequences?","Yes — `enumerate` accepts **any iterable**, including generators, files, and\n`zip`\u002F`map` objects, because it only pulls items one at a time. This is a key\nadvantage over `range(len())`, which needs a sized, indexable sequence.\n\n```python\nwith open(\"data.txt\") as f:\n    for lineno, line in enumerate(f, start=1):   # works on a file iterator\n        print(lineno, line.rstrip())\n\nfor i, sq in enumerate(n * n for n in range(5)):  # works on a generator\n    ...\n```\n\nRule of thumb: `enumerate` is universal across iterables; `len`-based indexing is not\n— prefer `enumerate` for streaming sources.\n",{"id":3601,"difficulty":253,"q":1400,"a":3602},"swap-with-unpacking","`a, b = b, a` works because the right side is evaluated into a **tuple first**, then\nunpacked into the targets. No temporary variable is needed, and it generalizes to\nany number of names.\n\n```python\na, b = 1, 2\na, b = b, a            # a=2, b=1\n\nx, y, z = 1, 2, 3\nx, y, z = z, x, y      # rotate: x=3, y=1, z=2\n\n# common in algorithms (e.g. Fibonacci):\na, b = b, a + b\n```\n\nRule of thumb: the right-hand side is fully evaluated before assignment, so\nmulti-target swaps\u002Frotations are safe and atomic-looking.\n",{"id":3604,"difficulty":162,"q":3605,"a":3606},"zip-uneven-data-loss","What's a subtle bug when zipping iterables of unknown length?","Plain `zip` **silently truncates** to the shortest iterable, so mismatched lengths\nlose data without any warning. If one source is an **iterator**, zip also consumes\none extra element from the longer one while detecting the stop.\n\n```python\na = iter([1, 2, 3])\nb = [10, 20]\nlist(zip(a, b))    # [(1, 10), (2, 20)]\nlist(a)            # [] or [3]? — 3 may already be consumed by zip\n```\n\nRule of thumb: when lengths should match, use `strict=True`; when they shouldn't,\nuse `zip_longest` — don't rely on silent truncation, and beware consuming iterators.\n",{"description":148},"Python interview questions on enumerate with start, zip and zip_longest, unzipping with zip(*), extended iterable unpacking, and star-args at the call site.","python\u002Fiteration\u002Fenumerate-zip","enumerate, zip & Unpacking","v8LV3znL6JWiwljIoYw-rCoEbunvbEHChcjvnwNsAxA",{"id":3613,"title":3614,"body":3615,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":3619,"navigation":153,"order":46,"path":3620,"questions":3621,"questionsCount":217,"related":218,"seo":3682,"seoDescription":3683,"stem":3684,"subtopic":3685,"topic":54,"topicSlug":56,"updated":223,"__hash__":3686},"qa\u002Fpython\u002Foop\u002Fmethods-properties.md","Methods Properties",{"type":145,"value":3616,"toc":3617},[],{"title":148,"searchDepth":29,"depth":29,"links":3618},[],{},"\u002Fpython\u002Foop\u002Fmethods-properties",[3622,3626,3630,3634,3638,3642,3646,3650,3654,3658,3662,3666,3670,3674,3678],{"id":3623,"difficulty":162,"q":3624,"a":3625},"instance-class-static-methods","What is the difference between an instance method, @classmethod, and @staticmethod?","An **instance method** takes `self` and operates on a specific object. A\n**`@classmethod`** takes `cls` (the class, not an instance) and works on\n**class-level state** or builds instances. A **`@staticmethod`** takes\n**neither** — it's a plain function namespaced inside the class, with no access\nto instance or class state.\n\n```python\nclass Pizza:\n    base_price = 10\n    def __init__(self, toppings):\n        self.toppings = toppings\n    def total(self):                    # instance method — uses self\n        return self.base_price + len(self.toppings)\n    @classmethod\n    def margherita(cls):                # classmethod — uses cls\n        return cls([\"mozzarella\", \"basil\"])\n    @staticmethod\n    def is_valid_topping(name):         # staticmethod — no self\u002Fcls\n        return name.isalpha()\n\nPizza.is_valid_topping(\"ham\")   # True — no instance needed\nPizza.margherita().total()      # 12\n```\n\nUse an instance method for per-object behavior, a classmethod for\nclass-aware logic (e.g. alternative constructors), and a staticmethod for a\nrelated helper that happens to live on the class.\n",{"id":3627,"difficulty":162,"q":3628,"a":3629},"classmethod-constructor","How do you use a classmethod as an alternative constructor?","A classmethod receives `cls`, so it can **build and return a new instance** from\na different input format — giving you multiple named constructors beyond\n`__init__`. Using `cls` (not the hard-coded class name) means **subclasses get\nthe right type** automatically.\n\n```python\nclass Date:\n    def __init__(self, year, month, day):\n        self.year, self.month, self.day = year, month, day\n    @classmethod\n    def from_string(cls, text):\n        y, m, d = map(int, text.split(\"-\"))\n        return cls(y, m, d)             # returns the (sub)class instance\n    @classmethod\n    def today(cls):\n        import datetime\n        t = datetime.date.today()\n        return cls(t.year, t.month, t.day)\n\nDate.from_string(\"2026-06-18\")   # alternative constructor\nDate.today()\n```\n\nThis is the idiomatic Python pattern (`dict.fromkeys`, `datetime.fromtimestamp`)\nfor \"make one of these, but from X\" — clearer than overloading `__init__` with\nflags.\n",{"id":3631,"difficulty":162,"q":3632,"a":3633},"property-getter-setter","How does @property work with a getter and setter?","`@property` turns a method into a **managed attribute** — you access it like\n`obj.x` (no parentheses), but a method runs behind the scenes. The matching\n`@x.setter` runs on assignment, letting you add **validation** without changing\nthe public interface.\n\n```python\nclass Account:\n    def __init__(self, balance):\n        self._balance = balance         # \"private\" backing field\n    @property\n    def balance(self):                  # getter — runs on read\n        return self._balance\n    @balance.setter\n    def balance(self, value):           # setter — runs on write\n        if value \u003C 0:\n            raise ValueError(\"balance cannot be negative\")\n        self._balance = value\n\nacc = Account(100)\nacc.balance          # 100 — looks like an attribute, calls the getter\nacc.balance = 50     # calls the setter (validated)\nacc.balance = -1     # ValueError\n```\n\nThe convention is a `_name` backing attribute behind a public property. Omit the\nsetter to make the property read-only.\n",{"id":3635,"difficulty":162,"q":3636,"a":3637},"why-properties-over-getters","Why do properties beat Java-style getter\u002Fsetter methods?","In Python you **start with a plain attribute** and only convert it to a\n`@property` later **if** you need validation or computation — **without changing\nthe call sites**. So you avoid the Java habit of pre-emptively writing\n`getX()`\u002F`setX()` \"just in case\". The public API stays `obj.x`.\n\n```python\n# Start simple — public attribute:\nclass Circle:\n    def __init__(self, radius):\n        self.radius = radius\n\nc = Circle(5)\nc.radius            # direct access — no ceremony\n\n# Later, add validation transparently — callers don't change:\nclass Circle:\n    def __init__(self, radius):\n        self.radius = radius\n    @property\n    def radius(self):\n        return self._radius\n    @radius.setter\n    def radius(self, value):\n        if value \u003C= 0:\n            raise ValueError(\"radius must be positive\")\n        self._radius = value\n```\n\nThis is the \"uniform access principle\": callers can't tell whether `obj.x` is a\nstored value or computed. Don't write getters\u002Fsetters upfront — reach for a\nproperty only when behavior is needed.\n",{"id":3639,"difficulty":253,"q":3640,"a":3641},"computed-readonly-properties","How do you create a computed or read-only property?","A property with **only a getter** (no setter) is **read-only** — assigning to it\nraises `AttributeError`. A **computed property** derives its value from other\nattributes on each access, so it stays in sync automatically rather than being\nstored.\n\n```python\nclass Rectangle:\n    def __init__(self, width, height):\n        self.width = width\n        self.height = height\n    @property\n    def area(self):                 # computed — derived on each read\n        return self.width * self.height\n\nr = Rectangle(3, 4)\nr.area           # 12 — computed\nr.width = 5\nr.area           # 20 — automatically reflects the change\nr.area = 99      # AttributeError — read-only (no setter)\n```\n\nFor an expensive computation you only want to run once, use\n`functools.cached_property` instead, which caches the result on first access.\nUse a plain read-only property for cheap derived values.\n",{"id":3643,"difficulty":162,"q":3644,"a":3645},"cached-property","How does `functools.cached_property` differ from `@property`?","`@cached_property` computes the value on **first access**, then **stores it in the\ninstance `__dict__`** so later reads are free — no recomputation. A plain `@property`\nrecomputes every time. The cache lives until you `del` the attribute.\n\n```python\nfrom functools import cached_property\n\nclass Dataset:\n    def __init__(self, rows): self.rows = rows\n    @cached_property\n    def stats(self):\n        print(\"computing...\")          # runs only once\n        return sum(self.rows) \u002F len(self.rows)\n\nd = Dataset([1, 2, 3])\nd.stats        # computing... -> 2.0\nd.stats        # 2.0 (no recompute)\ndel d.stats    # clears the cache; next access recomputes\n```\n\nRule of thumb: use `cached_property` for expensive, **stable** derived values; stick\nwith `@property` when the underlying data changes and must stay fresh. Note it needs\na writable `__dict__` (won't work with `__slots__` unless you add `__dict__`).\n",{"id":3647,"difficulty":162,"q":3648,"a":3649},"property-deleter","What does the `@x.deleter` of a property do?","The deleter runs when you do **`del obj.x`**, letting you hook into attribute\ndeletion (cleanup, resetting a cache, or forbidding deletion). It's the third piece\nalongside getter and setter.\n\n```python\nclass Resource:\n    def __init__(self): self._handle = \"open\"\n    @property\n    def handle(self): return self._handle\n    @handle.deleter\n    def handle(self):\n        print(\"closing\")\n        self._handle = None\n\nr = Resource()\ndel r.handle       # prints \"closing\"; runs cleanup logic\n```\n\nRule of thumb: add a `@x.deleter` when `del obj.x` should trigger behavior; omit it\nand deletion raises `AttributeError`.\n",{"id":3651,"difficulty":150,"q":3652,"a":3653},"property-as-descriptor","How is `@property` related to descriptors?","`property` is just a built-in **data descriptor** — a class implementing `__get__`,\n`__set__`, `__delete__`. That's why properties are defined **on the class** (not the\ninstance) and why they intercept attribute access. You can write your own descriptor\nto reuse get\u002Fset logic across many attributes.\n\n```python\nclass Positive:                       # reusable descriptor\n    def __set_name__(self, owner, name): self.name = \"_\" + name\n    def __get__(self, obj, owner):\n        return getattr(obj, self.name)\n    def __set__(self, obj, value):\n        if value \u003C= 0: raise ValueError(\"must be positive\")\n        setattr(obj, self.name, value)\n\nclass Product:\n    price = Positive()                # one descriptor, reusable\n    weight = Positive()\n```\n\nRule of thumb: a property is a per-attribute descriptor; write a custom descriptor\nclass when the same validation\u002Flogic should apply to many attributes.\n",{"id":3655,"difficulty":162,"q":3656,"a":3657},"setter-validation-init","Why does setting `self.x = value` in `__init__` trigger the property setter?","Because the property is defined on the **class**, any assignment to `self.x` —\nincluding inside `__init__` — goes through the setter. This is useful: validation\nruns even at construction, so you don't duplicate checks.\n\n```python\nclass Temperature:\n    def __init__(self, celsius):\n        self.celsius = celsius        # goes through the setter -> validated\n    @property\n    def celsius(self): return self._c\n    @celsius.setter\n    def celsius(self, value):\n        if value \u003C -273.15: raise ValueError(\"below absolute zero\")\n        self._c = value\n\nTemperature(-300)    # ValueError, raised from __init__\n```\n\nRule of thumb: assign to the public property name in `__init__` (not the `_backing`\nfield) so constructor values get the same validation as later writes.\n",{"id":3659,"difficulty":162,"q":3660,"a":3661},"method-types-access","Can a `@staticmethod` or `@classmethod` be called on an instance?","Yes — both are accessible via an **instance or the class**. A classmethod always\nreceives the class as `cls` regardless; a staticmethod receives nothing automatic\neither way. Calling on an instance doesn't pass `self`.\n\n```python\nclass C:\n    @classmethod\n    def cm(cls): return cls.__name__\n    @staticmethod\n    def sm(x): return x * 2\n\nc = C()\nc.cm()       # 'C' — cls is C, even called on instance\nC.cm()       # 'C'\nc.sm(5)      # 10 — self not passed\nC.sm(5)      # 10\n```\n\nRule of thumb: class\u002Fstatic methods work the same called on the class or an instance;\nthe difference is only what implicit first argument (if any) they receive.\n",{"id":3663,"difficulty":162,"q":3664,"a":3665},"bound-method-as-callback","What happens when you store a bound method as a callback?","A **bound method keeps a reference to its instance**, so storing `obj.method` as a\ncallback keeps `obj` alive (prevents garbage collection) and always operates on that\ninstance. This is usually desired, but can cause unexpected lifetime extension.\n\n```python\nclass Handler:\n    def __init__(self, name): self.name = name\n    def on_event(self): return f\"handled by {self.name}\"\n\nh = Handler(\"A\")\ncallbacks = [h.on_event]     # bound method holds h\ndel h                        # h NOT collected — callback still references it\ncallbacks[0]()               # 'handled by A'\n```\n\nRule of thumb: storing `obj.method` pins `obj` in memory; for caches\u002Fobservers that\nshouldn't extend lifetime, use `weakref.WeakMethod`.\n",{"id":3667,"difficulty":150,"q":3668,"a":3669},"classmethod-vs-staticmethod-subclass","Why prefer a classmethod over a staticmethod for a factory?","A classmethod uses **`cls`**, so when a subclass calls the factory it builds an\ninstance of the **subclass**. A staticmethod hard-codes the class name, so it always\nbuilds the base type — breaking subclassing.\n\n```python\nclass Shape:\n    @classmethod\n    def square(cls, size):\n        return cls(size, size)        # cls -> correct subclass\n    @staticmethod\n    def square_bad(size):\n        return Shape(size, size)      # always Shape, ignores subclass\n    def __init__(self, w, h): self.w, self.h = w, h\n\nclass Tile(Shape): pass\ntype(Tile.square(5))      # Tile  — polymorphic\ntype(Tile.square_bad(5))  # Shape — wrong\n```\n\nRule of thumb: use `classmethod` for constructors so they respect subclasses; reserve\n`staticmethod` for helpers that genuinely never need the class.\n",{"id":3671,"difficulty":253,"q":3672,"a":3673},"property-vs-method-call","When should something be a property versus a regular method?","Make it a **property** when it's a **cheap, attribute-like value** with no side\neffects and no arguments (`obj.area`). Make it a **method** when it takes arguments,\nis expensive, or performs an action (`obj.calculate()`, `obj.save()`).\n\n```python\nclass Circle:\n    def __init__(self, r): self.r = r\n    @property\n    def area(self):                 # noun, cheap, no args -> property\n        return 3.14159 * self.r ** 2\n    def scaled(self, factor):       # takes an arg -> method\n        return Circle(self.r * factor)\n```\n\nRule of thumb: properties read like nouns\u002Fdata (`obj.x`); methods read like verbs\u002F\nactions (`obj.do()`). Don't hide expensive or mutating work behind a property.\n",{"id":3675,"difficulty":162,"q":3676,"a":3677},"abstract-property-override","How do you override a property in a subclass?","Redefine the whole property in the subclass (you can't override just the getter or\nsetter in isolation). To reuse the parent's logic, call it via\n**`SuperClass.prop.fget(self)`**.\n\n```python\nclass Base:\n    @property\n    def value(self): return 10\n\nclass Child(Base):\n    @property\n    def value(self):\n        return Base.value.fget(self) * 2    # reuse parent getter\n\nChild().value     # 20\n```\n\nRule of thumb: overriding a property means redefining it; reach into\n`Base.prop.fget`\u002F`fset` when you want to build on the parent's accessor.\n",{"id":3679,"difficulty":150,"q":3680,"a":3681},"instance-method-on-class","What is the difference between `obj.method()` and `Class.method(obj)`?","They're **equivalent** for instance methods. `obj.method()` looks up `method` on the\nclass via the descriptor protocol, binds `obj` as `self`, and calls it.\n`Class.method(obj)` does the binding manually. The dotted form is just sugar.\n\n```python\nclass C:\n    def greet(self, name): return f\"hi {name}\"\n\nc = C()\nc.greet(\"Ada\")        # 'hi Ada' — self=c automatically\nC.greet(c, \"Ada\")     # 'hi Ada' — pass self explicitly\n```\n\nRule of thumb: `obj.method(args)` ≡ `type(obj).method(obj, args)`; the explicit form\nis occasionally handy to call a specific class's version directly.\n",{"description":148},"Python interview questions on instance vs classmethod vs staticmethod, classmethods as alternative constructors, the @property getter\u002Fsetter, why properties beat Java-style accessors, and computed\u002Fread-only properties.","python\u002Foop\u002Fmethods-properties","Methods & Properties","U4nKe81F0s6ab4R-0szLIUHbKWz1vnpolvGNKsqUFKE",{"id":3688,"title":3689,"body":3690,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":3694,"navigation":153,"order":46,"path":3695,"questions":3696,"questionsCount":217,"related":218,"seo":3757,"seoDescription":3758,"stem":3759,"subtopic":3760,"topic":117,"topicSlug":119,"updated":223,"__hash__":3761},"qa\u002Fpython\u002Fstdlib\u002Fserialization.md","Serialization",{"type":145,"value":3691,"toc":3692},[],{"title":148,"searchDepth":29,"depth":29,"links":3693},[],{},"\u002Fpython\u002Fstdlib\u002Fserialization",[3697,3701,3705,3709,3713,3717,3721,3725,3729,3733,3737,3741,3745,3749,3753],{"id":3698,"difficulty":253,"q":3699,"a":3700},"json-dumps-loads","How do json.dumps and json.loads work, and how do Python types map to JSON?","**`json.dumps`** serializes a Python object **to** a JSON string; **`json.loads`**\nparses a JSON string **back** into Python objects. The `dump`\u002F`load` variants\n(no `s`) work with file objects instead. The conversion follows a fixed type\nmapping.\n\n```python\nimport json\n\ndata = {\"name\": \"Ada\", \"age\": 36, \"tags\": [\"math\"], \"active\": True}\ns = json.dumps(data)            # dict -> JSON string\nback = json.loads(s)            # JSON string -> dict\n\nwith open(\"out.json\", \"w\") as f:\n    json.dump(data, f)          # write to file\n```\n\nThe mapping: `dict`->object, `list`\u002F`tuple`->array, `str`->string, `int`\u002F`float`\n->number, `True`\u002F`False`->`true`\u002F`false`, `None`->`null`. Note **tuples become\narrays** (you get a list back), and **dict keys are coerced to strings**. JSON has\nno native date, set, or bytes type.\n",{"id":3702,"difficulty":162,"q":3703,"a":3704},"json-custom-encoding","How do you serialize an object JSON doesn't support by default?","Unsupported types raise `TypeError: ... is not JSON serializable`. The two standard\nfixes are the **`default=` callback** (a function called for any unserializable\nobject, returning a JSON-friendly substitute) or a custom **`JSONEncoder` subclass**\npassed via **`cls=`**.\n\n```python\nimport json\nfrom datetime import datetime\n\ndef encode(obj):\n    if isinstance(obj, datetime):\n        return obj.isoformat()        # turn it into a string\n    raise TypeError(f\"not serializable: {type(obj)}\")\n\njson.dumps({\"when\": datetime.now()}, default=encode)\n\nclass MyEncoder(json.JSONEncoder):    # the class-based alternative\n    def default(self, obj):\n        if isinstance(obj, set):\n            return list(obj)\n        return super().default(obj)\njson.dumps({1, 2, 3}, cls=MyEncoder)\n```\n\nUse `default=` for a quick one-off; subclass `JSONEncoder` when you want reusable\nencoding logic. On the way back, use `object_hook` in `loads` to reconstruct custom\ntypes.\n",{"id":3706,"difficulty":162,"q":3707,"a":3708},"csv-reader-dictreader","What is the difference between csv.reader and csv.DictReader?","Both read CSV files, but the row format differs. **`csv.reader`** yields each row as\na **list of strings** indexed by position. **`csv.DictReader`** treats the first row\nas **headers** and yields each row as a **dict** keyed by column name — far more\nreadable and robust to column reordering.\n\n```python\nimport csv\n\nwith open(\"people.csv\", newline=\"\") as f:\n    for row in csv.reader(f):\n        print(row[0], row[1])         # positional — brittle\n\nwith open(\"people.csv\", newline=\"\") as f:\n    for row in csv.DictReader(f):\n        print(row[\"name\"], row[\"age\"])  # by header — clear\n```\n\nAlways open CSV files with **`newline=\"\"`** to let the `csv` module handle line\nendings correctly. Prefer `DictReader`\u002F`DictWriter` for named-column access; use the\nplain `reader` for headerless or purely positional data.\n",{"id":3710,"difficulty":162,"q":3711,"a":3712},"pickle-vs-json","What is the difference between pickle and json, and what is the security warning?","**`json`** is a **text**, language-independent format for **simple data** (dicts,\nlists, numbers, strings). **`pickle`** is a **binary**, Python-specific format that\ncan serialize **almost any Python object** (custom classes, functions references,\nnested objects). The crucial caveat: **never unpickle untrusted data**.\n\n```python\nimport json, pickle\n\njson.dumps({\"x\": 1})              # \"{\\\"x\\\": 1}\" — readable, portable\npickle.dumps({\"x\": 1})            # b'\\x80\\x04...' — binary, Python-only\n\n# DANGER: unpickling runs arbitrary code embedded in the data\npickle.loads(untrusted_bytes)     # can execute malicious payloads!\n```\n\n`pickle.loads` can **execute arbitrary code** during deserialization, so it's a\nremote-code-execution risk on attacker-controlled input. Rule of thumb: use **json**\nfor config, APIs, and anything crossing a trust boundary; use **pickle** only for\ntrusted, internal Python-to-Python data (e.g. caches you wrote yourself).\n",{"id":3714,"difficulty":162,"q":3715,"a":3716},"serializing-datetimes","How do you serialize datetimes to JSON and back?","JSON has **no date type**, so a `datetime` must be converted to a **string** — the\nISO 8601 format via **`.isoformat()`** is the standard choice because it's\nunambiguous and parseable. On the way out use `default=`; on the way back parse the\nstring with `datetime.fromisoformat()`.\n\n```python\nimport json\nfrom datetime import datetime\n\nevent = {\"name\": \"launch\", \"at\": datetime(2026, 6, 18, 14, 30)}\n\ntext = json.dumps(event, default=lambda o: o.isoformat())\n# '{\"name\": \"launch\", \"at\": \"2026-06-18T14:30:00\"}'\n\nraw = json.loads(text)\nraw[\"at\"] = datetime.fromisoformat(raw[\"at\"])   # back to datetime\n```\n\nJSON can't tell a date-string from an ordinary string, so you must know which fields\nto re-parse (or use `object_hook`). Prefer ISO strings in **UTC** for portability,\nand convert to local time only at display.\n",{"id":3718,"difficulty":253,"q":3719,"a":3720},"json-formatting-options","What options control JSON output formatting?","`json.dumps` accepts **`indent`** (pretty-print), **`sort_keys`** (deterministic key\norder), **`separators`** (compact output), and **`ensure_ascii`** (escape non-ASCII or\nkeep Unicode). These shape readability and size.\n\n```python\nimport json\ndata = {\"b\": 1, \"a\": [1, 2]}\njson.dumps(data, indent=2)                    # pretty, multi-line\njson.dumps(data, sort_keys=True)              # '{\"a\": [1, 2], \"b\": 1}'\njson.dumps(data, separators=(\",\", \":\"))       # '{\"b\":1,\"a\":[1,2]}' — compact\njson.dumps({\"x\": \"café\"}, ensure_ascii=False) # keeps 'café' (not \\u...)\n```\n\nRule of thumb: `indent` for human-readable files\u002Flogs, compact `separators` for\nnetwork payloads, `sort_keys` for reproducible output (e.g. diffs\u002Fhashing).\n",{"id":3722,"difficulty":162,"q":3723,"a":3724},"object-hook-decoding","How does `object_hook` reconstruct custom types when loading JSON?","`json.loads(..., object_hook=fn)` calls `fn` on **every decoded object (dict)**,\nletting you transform it into a richer Python type — the inverse of `default=` for\nencoding.\n\n```python\nimport json\nfrom datetime import datetime\n\ndef decode(d):\n    if \"at\" in d:\n        d[\"at\"] = datetime.fromisoformat(d[\"at\"])\n    return d\n\njson.loads('{\"at\": \"2026-06-18T14:30:00\"}', object_hook=decode)\n# {'at': datetime(2026, 6, 18, 14, 30)}\n```\n\nRule of thumb: pair `default=` (encode) with `object_hook` (decode) to round-trip\ncustom types like datetimes, Decimals, or your own classes through JSON.\n",{"id":3726,"difficulty":162,"q":3727,"a":3728},"csv-writing","How do you write CSV files correctly?","Use `csv.writer` (lists) or `csv.DictWriter` (dicts, with `writeheader()`). Open with\n**`newline=\"\"`** to prevent blank rows on Windows, and let the module handle quoting\nof fields containing commas\u002Fquotes\u002Fnewlines.\n\n```python\nimport csv\nwith open(\"out.csv\", \"w\", newline=\"\") as f:\n    w = csv.DictWriter(f, fieldnames=[\"name\", \"age\"])\n    w.writeheader()\n    w.writerow({\"name\": \"Ada, the\", \"age\": 36})   # comma auto-quoted\n    w.writerows([{\"name\": \"Bob\", \"age\": 40}])\n```\n\nRule of thumb: always `newline=\"\"` when opening CSV files, and use `DictWriter` with\n`writeheader()` for named columns — never hand-build CSV with string joins (quoting!).\n",{"id":3730,"difficulty":150,"q":3731,"a":3732},"json-numeric-precision","What happens to special floats and big numbers in JSON?","Python's `json` emits `NaN`, `Infinity`, `-Infinity` by default (which is **invalid**\nJSON); pass `allow_nan=False` to raise instead. Large `int`s serialize fine but may\n**lose precision** when read by JavaScript (its numbers are 64-bit floats).\n\n```python\nimport json\njson.dumps(float(\"nan\"))                 # 'NaN' — not valid standard JSON\njson.dumps(float(\"inf\"), allow_nan=False)  # ValueError\n\njson.dumps(2**60)                        # fine in Python\n# but JS parsing loses precision beyond 2**53\n```\n\nRule of thumb: set `allow_nan=False` for strict JSON; serialize very large integers as\n**strings** if a JavaScript\u002Fother consumer must read them safely.\n",{"id":3734,"difficulty":162,"q":3735,"a":3736},"pickle-protocols","What are pickle protocols and what can't be pickled?","Pickle has versioned **protocols** (higher = more efficient\u002Fcompact; protocol 5 is\nlatest). Some objects **can't be pickled**: open file handles, sockets, lambdas, and\nmost local\u002Fnested functions. `__reduce__`\u002F`__getstate__` customize pickling.\n\n```python\nimport pickle\npickle.dumps(obj, protocol=pickle.HIGHEST_PROTOCOL)\n\npickle.dumps(lambda x: x)        # PicklingError — lambdas can't be pickled\npickle.dumps(open(\"f\"))          # TypeError — file handles can't be pickled\n```\n\nRule of thumb: use `HIGHEST_PROTOCOL` for internal pickling; for things pickle can't\nhandle (lambdas, connections), redesign or implement `__getstate__`\u002F`__setstate__`.\n",{"id":3738,"difficulty":162,"q":3739,"a":3740},"safe-alternatives-pickle","What are safer alternatives to pickle for persistence?","For untrusted or cross-language data, prefer **JSON** (simple data), **`json`\u002FCSV** for\ntabular, or schema-based formats like **Protocol Buffers**, **MessagePack**, or\n**TOML\u002FYAML** for config. These don't execute code on load.\n\n```python\nimport json\n# safe round-trip for plain data:\njson.dump(config, open(\"config.json\", \"w\"))\n\n# for binary efficiency without code execution: msgpack (third-party)\n# for typed cross-language schemas: protobuf, avro\n```\n\nRule of thumb: reach for pickle only for trusted internal Python state; use JSON\u002F\nmsgpack\u002Fprotobuf at trust boundaries and for interoperability.\n",{"id":3742,"difficulty":162,"q":3743,"a":3744},"json-key-coercion","What happens to non-string dict keys when serializing to JSON?","JSON object keys must be **strings**, so `json.dumps` **coerces** `int`\u002F`float`\u002F`bool`\u002F\n`None` keys to strings — and on `loads` they come back as **strings**, not their\noriginal type. Other key types (tuples) raise `TypeError`.\n\n```python\nimport json\ns = json.dumps({1: \"a\", True: \"b\"})\ns                       # '{\"1\": \"a\", \"true\": \"b\"}' — keys stringified\njson.loads(s)           # {'1': 'a', 'true': 'b'} — keys are now strings\n\njson.dumps({(1, 2): \"x\"})   # TypeError — tuple keys not allowed\n```\n\nRule of thumb: JSON keys round-trip as strings — if you need int keys back, convert\nthem yourself after loading, and avoid non-stringifiable key types.\n",{"id":3746,"difficulty":162,"q":3747,"a":3748},"csv-dialects-delimiters","How do you handle non-comma delimiters and quoting in CSV?","Pass **`delimiter=`**, **`quotechar=`**, and **`quoting=`** to `reader`\u002F`writer`, or\nuse a registered **dialect**. This handles TSV, semicolon-separated, or\nquote-everything formats.\n\n```python\nimport csv\n# tab-separated:\ncsv.reader(f, delimiter=\"\\t\")\n# semicolon with custom quote:\ncsv.writer(f, delimiter=\";\", quotechar=\"'\", quoting=csv.QUOTE_ALL)\n\ncsv.register_dialect(\"pipes\", delimiter=\"|\")\ncsv.reader(f, dialect=\"pipes\")\n```\n\nRule of thumb: configure `delimiter`\u002F`quoting` for non-CSV-comma formats; the `csv`\nmodule handles embedded delimiters\u002Fquotes correctly so you don't have to.\n",{"id":3750,"difficulty":150,"q":3751,"a":3752},"streaming-large-json","How do you handle JSON files too large to fit in memory?","`json.load` reads the whole document into memory. For huge files, use **line-delimited\nJSON** (one object per line) and process line by line, or a streaming\u002Fincremental\nparser like the third-party **`ijson`**.\n\n```python\nimport json\n# JSON Lines: one JSON object per line — stream it\nwith open(\"events.jsonl\") as f:\n    for line in f:\n        obj = json.loads(line)     # constant memory per record\n        process(obj)\n\n# for a single giant nested JSON, use ijson (incremental) instead of json.load\n```\n\nRule of thumb: prefer JSON Lines for large datasets so you can stream records; reach\nfor `ijson` when you must incrementally parse one massive JSON document.\n",{"id":3754,"difficulty":162,"q":3755,"a":3756},"dataclass-json-roundtrip","How do you serialize a dataclass to JSON and back?","Convert with **`dataclasses.asdict()`** to a plain dict for `json.dumps`, and\nreconstruct by unpacking the loaded dict into the dataclass (`Cls(**data)`). Nested\ndataclasses need recursive handling or a library like `pydantic`.\n\n```python\nimport json\nfrom dataclasses import dataclass, asdict\n\n@dataclass\nclass User:\n    name: str\n    age: int\n\nu = User(\"Ada\", 36)\ns = json.dumps(asdict(u))        # '{\"name\": \"Ada\", \"age\": 36}'\nUser(**json.loads(s))            # User(name='Ada', age=36)\n```\n\nRule of thumb: `asdict` out, `Cls(**d)` in for flat dataclasses; for nested or\nvalidated models, use pydantic\u002F`dataclasses-json` rather than hand-rolling.\n",{"description":148},"Python interview questions on json.dumps\u002Floads and type mapping, custom JSON encoding, csv reader vs DictReader, pickle vs json and pickle security, and serializing datetimes.","python\u002Fstdlib\u002Fserialization","JSON, CSV & pickle","nk7v1w9iQW5SWEPusH7zfSQhJOuFpfUV-xQkyGmwuTE",{"id":3763,"title":3764,"body":3765,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":3769,"navigation":153,"order":55,"path":3770,"questions":3771,"questionsCount":1346,"related":218,"seo":3836,"seoDescription":3837,"stem":3838,"subtopic":3839,"topic":28,"topicSlug":30,"updated":223,"__hash__":3840},"qa\u002Fpython\u002Fdata-structures\u002Fcollections-module.md","Collections Module",{"type":145,"value":3766,"toc":3767},[],{"title":148,"searchDepth":29,"depth":29,"links":3768},[],{},"\u002Fpython\u002Fdata-structures\u002Fcollections-module",[3772,3776,3780,3784,3788,3792,3796,3800,3804,3808,3812,3816,3820,3824,3828,3832],{"id":3773,"difficulty":253,"q":3774,"a":3775},"counter","What is collections.Counter and what is it good for?","**`Counter`** is a `dict` subclass for **counting hashable items**. You pass it\nany iterable and it tallies occurrences into a `{element: count}` mapping, with\nhandy extras like `most_common()`. Missing keys return **0** instead of raising.\n\n```python\nfrom collections import Counter\nc = Counter(\"mississippi\")\n# Counter({'i': 4, 's': 4, 'p': 2, 'm': 1})\n\nc.most_common(2)     # [('i', 4), ('s', 4)] — top N by count\nc[\"z\"]               # 0 — missing key, no KeyError\nc.update(\"pp\")       # add more counts -> p becomes 4\n\nCounter([1, 1, 2]) + Counter([1, 3])   # Counter({1: 3, 2: 1, 3: 1})\n```\n\nIt replaces the manual `d[x] = d.get(x, 0) + 1` pattern and supports arithmetic\nbetween counters. Reach for `Counter` whenever the task is \"how many of each?\" —\nword frequencies, tallies, or finding the most common elements.\n",{"id":3777,"difficulty":253,"q":3778,"a":3779},"defaultdict","How does collections.defaultdict work, and how is it different from dict.setdefault?","**`defaultdict(factory)`** is a `dict` that **auto-creates a missing key's value**\nby calling the zero-argument `factory` (like `list`, `int`, or `set`) the first\ntime you access it — so you can append or increment without initializing.\n\n```python\nfrom collections import defaultdict\n\ngroups = defaultdict(list)\ngroups[\"fruits\"].append(\"apple\")   # key auto-created as [] then appended\n# {'fruits': ['apple']}\n\ncounts = defaultdict(int)\nfor ch in \"aab\":\n    counts[ch] += 1                # missing keys start at 0\n# {'a': 2, 'b': 1}\n```\n\nThe difference from **`setdefault`**: `setdefault` evaluates its default\n**on every call** even when the key exists (wasteful), whereas `defaultdict`\nonly calls the factory **on a miss**. Use `defaultdict` for grouping and\ncounting loops; use plain `setdefault` for a one-off default insertion.\n",{"id":3781,"difficulty":162,"q":3782,"a":3783},"deque","What is a collections.deque and why use it over a list?","A **`deque`** (\"double-ended queue\") gives **O(1) appends and pops at *both*\nends**, whereas a `list` is O(n) for operations at the front (every element\nshifts). It also supports a **`maxlen`** for a fixed-size sliding window.\n\n```python\nfrom collections import deque\n\nd = deque([1, 2, 3])\nd.appendleft(0)    # O(1) — list.insert(0, x) would be O(n)\nd.append(4)        # O(1)\nd.popleft()        # O(1) — efficient FIFO queue\n\nwindow = deque(maxlen=3)\nfor x in [1, 2, 3, 4]:\n    window.append(x)   # auto-drops from the left\n# deque([2, 3, 4], maxlen=3)\n```\n\nUse a deque for **queues, BFS, and sliding windows**; with `maxlen` it\nauto-discards the oldest item when full (great for \"last N\" buffers). Stick with\na list when you only push\u002Fpop at the end or need fast random indexing.\n",{"id":3785,"difficulty":162,"q":3786,"a":3787},"ordereddict","Is OrderedDict still useful now that regular dicts keep insertion order (3.7+)?","Mostly not for ordering itself — since **Python 3.7** a plain `dict` already\npreserves insertion order, so `OrderedDict` is no longer needed just to remember\norder. But it still has a couple of **distinct features** a regular dict lacks.\n\n```python\nfrom collections import OrderedDict\n\n# 1) order-sensitive equality\nOrderedDict(a=1, b=2) == OrderedDict(b=2, a=1)   # False\ndict(a=1, b=2)        == dict(b=2, a=1)          # True (order ignored)\n\n# 2) move_to_end and a popitem(last=...) toggle\nod = OrderedDict(a=1, b=2, c=3)\nod.move_to_end(\"a\")        # OrderedDict([('b',2),('c',3),('a',1)])\nod.popitem(last=False)     # pop from the FRONT — FIFO\n```\n\nSo use `OrderedDict` when you need **order-sensitive `==`**, **`move_to_end`**,\nor **`popitem(last=False)`** — for example, building an LRU cache. For plain\n\"keep the order I inserted,\" a regular dict is now enough.\n",{"id":3789,"difficulty":162,"q":3790,"a":3791},"chainmap","What is collections.ChainMap?","**`ChainMap`** groups multiple dicts into a **single, layered view** without\ncopying them. Lookups search the underlying mappings **in order** and return the\nfirst match, so earlier maps **shadow** later ones.\n\n```python\nfrom collections import ChainMap\n\ndefaults = {\"color\": \"red\", \"size\": \"M\"}\noverrides = {\"color\": \"blue\"}\nsettings = ChainMap(overrides, defaults)\n\nsettings[\"color\"]    # 'blue' — first map wins\nsettings[\"size\"]     # 'M'    — falls through to defaults\n\nsettings[\"size\"] = \"L\"   # writes go to the FIRST map (overrides) only\n```\n\nIt's perfect for **layered configuration** (CLI args over env vars over\ndefaults) and scope-like lookups, because it stays live — changing a source dict\nshows up immediately. Note that **writes and deletes only affect the first\nmapping**. Use it to merge config layers without flattening them into one dict.\n",{"id":3793,"difficulty":253,"q":3794,"a":3795},"namedtuple-crossref","How does namedtuple fit into the collections module?","**`collections.namedtuple`** is a factory that creates an **immutable tuple\nsubclass with named fields** — lightweight, hashable records that read like\nobjects but behave like tuples (indexing, unpacking, comparison).\n\n```python\nfrom collections import namedtuple\n\nPoint = namedtuple(\"Point\", [\"x\", \"y\"])\np = Point(3, 4)\np.x, p[0]        # 3, 3  — named and positional access\np._asdict()      # {'x': 3, 'y': 4}\np._replace(x=9)  # Point(x=9, y=4) — returns a NEW tuple (immutable)\n```\n\nIt rounds out the module's record-like tools alongside `Counter`,\n`defaultdict`, and `deque`. For richer needs — type hints, defaults, methods —\n`typing.NamedTuple` or a `@dataclass` is the modern choice. Use `namedtuple` for\nsimple, immutable, self-documenting records.\n",{"id":3797,"difficulty":253,"q":3798,"a":3799},"counter-most-common","How do you get the N most frequent items with Counter?","Use **`Counter.most_common(n)`**, which returns a list of `(item, count)`\ntuples sorted by count descending. With no argument it returns **all** items\nordered by frequency. It's the idiomatic one-liner for \"top N\" problems.\n\n```python\nfrom collections import Counter\nwords = \"a b a c a b\".split()\nCounter(words).most_common(2)     # [('a', 3), ('b', 2)]\n```\n\nRule of thumb: reach for `Counter(iterable).most_common(n)` instead of\nhand-rolling a frequency dict plus a sort.\n",{"id":3801,"difficulty":162,"q":3802,"a":3803},"counter-arithmetic","How do Counters support arithmetic?","Counters support **`+`, `-`, `&` (min\u002Fintersection), and `|` (max\u002Funion)**,\ntreating counts like multiset multiplicities. Addition combines counts;\nsubtraction drops counts at or below zero. This makes combining or diffing\nfrequency tables trivial.\n\n```python\nfrom collections import Counter\na, b = Counter(\"aab\"), Counter(\"abc\")\na + b          # Counter({'a': 3, 'b': 2, 'c': 1})\na - b          # Counter({'a': 1})   negatives dropped\na & b          # Counter({'a': 1, 'b': 1})  per-key min\n```\n\nRule of thumb: use Counter arithmetic to merge or compare counts instead of\nlooping over keys manually.\n",{"id":3805,"difficulty":162,"q":3806,"a":3807},"counter-negative","Can a Counter hold zero or negative counts?","**Yes** — unlike a true multiset, a `Counter` lets you **store and update**\nzero or negative values directly (e.g. `c[\"x\"] -= 5`). Those keys stay in the\ndict. Only the **arithmetic operators** (`+`, `-`) and `most_common`\u002F\n`+Counter()` strip non-positive counts; `elements()` skips them too.\n\n```python\nfrom collections import Counter\nc = Counter(a=2, b=0, c=-1)\nlist(c.elements())     # ['a', 'a']   -> ignores b and c\n+c                     # Counter({'a': 2})  unary + drops \u003C=0\n```\n\nRule of thumb: direct assignment keeps any int; use `+c` or arithmetic when\nyou want only positive counts.\n",{"id":3809,"difficulty":162,"q":3810,"a":3811},"defaultdict-factory","What factory functions are common with defaultdict?","The **default_factory** is any zero-arg callable producing the default value:\n**`int`** for counters (`0`), **`list`** for grouping (`[]`), **`set`** for\nunique grouping, and even **`dict`** for nested maps. Accessing a missing key\ncalls the factory and stores the result.\n\n```python\nfrom collections import defaultdict\ngroups = defaultdict(list)\nfor name, dept in people:\n    groups[dept].append(name)        # no KeyError, list auto-created\n\ncounts = defaultdict(int)\nfor ch in text: counts[ch] += 1\n```\n\nRule of thumb: pick the factory by the value type you accumulate — `list`\u002F\n`set` to group, `int` to count, `dict` to nest.\n",{"id":3813,"difficulty":162,"q":3814,"a":3815},"defaultdict-keyerror-pitfall","What is a subtle pitfall of defaultdict?","**Reading** a missing key **creates and inserts it**, which can silently grow\nthe dict or change membership checks. A mere `d[k]` (or even `k in d` after a\nbracket access) is no longer side-effect-free. Use `.get()` or `k in d` when\nyou only want to peek.\n\n```python\nfrom collections import defaultdict\nd = defaultdict(list)\n_ = d[\"missing\"]        # inserts 'missing': [] !\nprint(dict(d))          # {'missing': []}\n```\n\nRule of thumb: read with `d.get(k)` when you don't intend to create the key;\nreserve bracket access for when auto-creation is what you want.\n",{"id":3817,"difficulty":162,"q":3818,"a":3819},"deque-maxlen","What does a deque's maxlen do?","**`maxlen`** caps the deque's size: appending to a full deque **discards the\nitem at the opposite end**. This gives you a fixed-size sliding window or a\n\"last N items\" buffer for free, with O(1) appends.\n\n```python\nfrom collections import deque\nlast3 = deque(maxlen=3)\nfor x in range(5):\n    last3.append(x)\nlast3                    # deque([2, 3, 4], maxlen=3)\n```\n\nRule of thumb: use a `maxlen` deque for rolling logs, moving windows, or\n\"keep only the most recent N\" without manual trimming.\n",{"id":3821,"difficulty":162,"q":3822,"a":3823},"deque-rotate","What does deque.rotate do and what are deque's complexity guarantees?","**`rotate(n)`** shifts elements right by `n` (left if negative) in O(k).\ndeque gives **O(1) appends and pops at both ends** (`append`\u002F`appendleft`\u002F\n`pop`\u002F`popleft`), versus a list's O(n) `insert(0)`\u002F`pop(0)`. Indexing the\nmiddle, though, is **O(n)** for a deque.\n\n```python\nfrom collections import deque\nd = deque([1, 2, 3, 4])\nd.rotate(1)              # deque([4, 1, 2, 3])\nd.rotate(-2)             # deque([2, 3, 4, 1])\n```\n\nRule of thumb: use deque for queue\u002Fstack workloads with end operations; use a\nlist when you need fast random indexing.\n",{"id":3825,"difficulty":162,"q":3826,"a":3827},"chainmap-updates","Where do writes go in a ChainMap?","A **ChainMap** searches its mappings left to right for **reads**, but **all\nwrites\u002Fdeletes affect only the first mapping**. This is perfect for layered\nconfigs (CLI > env > defaults) where you want a writable top layer over\nread-only fallbacks.\n\n```python\nfrom collections import ChainMap\ndefaults = {\"color\": \"red\", \"size\": \"M\"}\ncfg = ChainMap({}, defaults)\ncfg[\"color\"] = \"blue\"    # writes to the first (empty) map\ncfg[\"color\"], cfg[\"size\"]   # ('blue', 'M')\ndefaults                  # unchanged\n```\n\nRule of thumb: use ChainMap to overlay scopes without copying\u002Fmerging, with\nedits landing in the first layer only.\n",{"id":3829,"difficulty":162,"q":3830,"a":3831},"ordereddict-still-useful","Does OrderedDict have any methods regular dict lacks?","Yes — **`move_to_end(key, last=True)`** and **`popitem(last=...)`** give\nexplicit reordering, and OrderedDict equality is **order-sensitive** (a plain\ndict's is not). That makes it handy for LRU-cache-style structures.\n\n```python\nfrom collections import OrderedDict\nod = OrderedDict(a=1, b=2, c=3)\nod.move_to_end(\"a\")       # OrderedDict(b=2, c=3, a=1)\nod.popitem(last=False)    # ('b', 2) -> pops from the front\n```\n\nRule of thumb: use a plain dict for ordering; reach for OrderedDict when you\nneed `move_to_end`, front-popping, or order-sensitive equality.\n",{"id":3833,"difficulty":150,"q":3834,"a":3835},"userdict-userlist","When would you subclass UserDict or UserList instead of dict\u002Flist?","Subclassing built-in `dict`\u002F`list` directly is leaky — internal methods don't\nalways call your overrides (e.g. `dict.update` may bypass `__setitem__`).\n**`UserDict`\u002F`UserList`** wrap the data in a `.data` attribute and route\neverything through your overridden methods, so customization behaves\nconsistently.\n\n```python\nfrom collections import UserDict\nclass UpperDict(UserDict):\n    def __setitem__(self, k, v):\n        super().__setitem__(k.upper(), v)\n\nd = UpperDict(); d[\"a\"] = 1\nd.data                    # {'A': 1}  -> override honored everywhere\n```\n\nRule of thumb: subclass `UserDict`\u002F`UserList` when you need reliable method\noverrides; use plain subclasses only for trivial additions.\n",{"description":148},"Python interview questions on the collections module — Counter, defaultdict, deque, OrderedDict, ChainMap, and how they relate to namedtuple.","python\u002Fdata-structures\u002Fcollections-module","The collections Module","XMX5wE7lmE-zurIgGoYPBAUuxBNFVK3TjybuEfyqCm4",{"id":3842,"title":3843,"body":3844,"description":148,"difficulty":253,"extension":151,"framework":10,"frameworkSlug":8,"meta":3848,"navigation":153,"order":55,"path":3849,"questions":3850,"questionsCount":1346,"related":218,"seo":3915,"seoDescription":3916,"stem":3917,"subtopic":3918,"topic":20,"topicSlug":21,"updated":223,"__hash__":3919},"qa\u002Fpython\u002Ffundamentals\u002Ftruthiness-conversion.md","Truthiness Conversion",{"type":145,"value":3845,"toc":3846},[],{"title":148,"searchDepth":29,"depth":29,"links":3847},[],{},"\u002Fpython\u002Ffundamentals\u002Ftruthiness-conversion",[3851,3855,3859,3863,3867,3871,3875,3879,3883,3887,3891,3895,3899,3903,3907,3911],{"id":3852,"difficulty":253,"q":3853,"a":3854},"falsy-values","Which values are falsy in Python?","A handful of values are **falsy** (treated as `False` in a boolean context):\n**`None`**, **`False`**, **zero** of any numeric type (`0`, `0.0`, `0j`), and\n**empty containers\u002Fsequences** (`\"\"`, `[]`, `{}`, `()`, `set()`, `range(0)`).\nAlmost everything else is **truthy**.\n\n```python\nbool(0), bool(\"\"), bool([]), bool(None)   # all False\nbool(1), bool(\"x\"), bool([0]), bool(\" \")  # all True\nif not items:        # idiomatic empty check\n    print(\"empty\")\n```\n\nWhy it matters: idiomatic Python uses `if items:` rather than `if len(items) > 0:`.\nRule of thumb: \"empty or zero or None\" is falsy; everything else is truthy.\n",{"id":3856,"difficulty":162,"q":3857,"a":3858},"bool-len-protocol","How does Python decide whether a custom object is truthy?","Python calls **`__bool__`** first; if it's not defined, it falls back to\n**`__len__`** (zero length is falsy). If neither exists, the object is **always\ntruthy** — the default for plain instances.\n\n```python\nclass Box:\n    def __init__(self, items): self.items = items\n    def __len__(self): return len(self.items)   # used for truthiness\n\nbool(Box([]))     # False — __len__ is 0\nbool(Box([1]))    # True\n\nclass Always:\n    def __bool__(self): return False   # __bool__ wins over __len__\nbool(Always())    # False\n```\n\nRule of thumb: define `__bool__` (or `__len__`) so `if obj:` makes sense for your\ntype; otherwise every instance is truthy.\n",{"id":3860,"difficulty":253,"q":3861,"a":3862},"explicit-conversion","How do explicit type conversions like int(), str(), and list() work?","Python provides **constructor functions** that convert between types: `int()`,\n`float()`, `str()`, `bool()`, `list()`, `tuple()`, `set()`, `dict()`. They build a\n**new** object and raise `ValueError` if the input can't be converted.\n\n```python\nint(\"42\")        # 42\nint(\"3.9\")       # ValueError — not a valid int literal\nint(3.9)         # 3   — truncates toward zero\nfloat(\"3.14\")    # 3.14\nstr(255)         # \"255\"\nlist(\"abc\")      # ['a', 'b', 'c']\nlist({1: \"a\"})   # [1]  — iterates keys\n```\n\nRule of thumb: these are **explicit** conversions you call yourself — Python rarely\nconverts types implicitly, so reach for the constructor you need.\n",{"id":3864,"difficulty":253,"q":3865,"a":3866},"empty-container-truthiness","Are empty containers and None falsy?","Yes. Every **empty** built-in container is falsy — `[]`, `{}`, `()`, `set()`,\n`\"\"` — and so is **`None`**. A non-empty container is truthy regardless of what it\ncontains, even `[0]` or `[False]`.\n\n```python\nbool([]), bool({}), bool(set()), bool(\"\")   # all False\nbool([0]), bool([False]), bool({None})       # all True — non-empty!\nbool(None)                                   # False\n```\n\nWatch the trap: `[0]` is **truthy** because it has one element, even though that\nelement is falsy. Rule of thumb: container truthiness depends on **length**, not on\nthe elements' values.\n",{"id":3868,"difficulty":162,"q":3869,"a":3870},"and-or-short-circuit","What do `and` and `or` actually return?","`and`\u002F`or` **short-circuit** and return one of their **operands**, not a strict\n`True`\u002F`False`. `and` returns the **first falsy** operand (or the last if all are\ntruthy); `or` returns the **first truthy** operand (or the last if all are falsy).\n\n```python\n0 and 5         # 0   — first falsy, second never evaluated\n2 and 5         # 5   — both truthy, returns last\n0 or \"default\"  # \"default\"  — first truthy\nNone or 0 or [] # []  — all falsy, returns the last\nname = user_input or \"guest\"   # common default idiom\n```\n\nRule of thumb: `x or default` supplies a fallback, and short-circuiting means the\nright side is skipped when the result is already decided.\n",{"id":3872,"difficulty":253,"q":3873,"a":3874},"is-none-vs-eq-none","Why use `is None` instead of `== None`?","`None` is a **singleton** — there is exactly one `None` object — so `is None`\nchecks **identity**, which is fast and can't be fooled. `== None` calls `__eq__`,\nwhich a class can **override** to return a misleading result.\n\n```python\nif x is None:        # idiomatic, reliable\n    ...\n\nclass Weird:\n    def __eq__(self, other): return True\nWeird() == None      # True   — misleading!\nWeird() is None      # False  — correct\n```\n\nRule of thumb: always compare against `None`, `True`, and `False` with **`is`\u002F\n`is not`** — PEP 8 explicitly recommends it.\n",{"id":3876,"difficulty":162,"q":3877,"a":3878},"truthy-vs-explicit-empty-check","Why can `if x:` be a bug when you mean `if x is not None:`?","`if x:` is **falsy for many valid values** — `0`, `\"\"`, `[]`, `0.0` — not just\n`None`. If your real question is \"was an argument supplied?\", a bare truthiness check\nsilently rejects legitimate empty\u002Fzero inputs.\n\n```python\ndef f(count=None):\n    if not count:          # BUG: triggers for count=0 too\n        count = 10\n    return count\nf(0)                       # 10  — wanted 0!\n\ndef g(count=None):\n    if count is None:      # correct: only the unset case\n        count = 10\n    return count\ng(0)                       # 0\n```\n\nRule of thumb: distinguish \"missing\" from \"empty\u002Fzero\" — use `is None` for sentinels,\nreserve `if x:` for genuine emptiness checks.\n",{"id":3880,"difficulty":253,"q":3881,"a":3882},"bool-int-conversion","How does `bool()` convert numbers and strings?","`bool(n)` is `False` only for **zero**; any non-zero number is `True`. `bool(s)` is\n`False` only for the **empty string** — so `bool(\"0\")` and `bool(\"False\")` are both\n**`True`** (non-empty strings are truthy regardless of content).\n\n```python\nbool(0), bool(0.0), bool(-1)     # (False, False, True)\nbool(\"\"), bool(\"0\"), bool(\"False\")  # (False, True, True)  — trap!\nint(True), int(False)            # (1, 0)\n```\n\nRule of thumb: parsing input like `\"0\"`\u002F`\"false\"` needs explicit checks — never trust\n`bool(string)` to interpret the *content*, only its emptiness.\n",{"id":3884,"difficulty":162,"q":3885,"a":3886},"any-all-functions","How do `any()` and `all()` use truthiness?","`any(iterable)` returns `True` if **at least one** element is truthy; `all` returns\n`True` if **every** element is truthy. Both **short-circuit** and have edge cases on\nempty input: `all([])` is `True` (vacuous truth), `any([])` is `False`.\n\n```python\nany([0, \"\", 3])      # True  — 3 is truthy\nall([1, 2, \"\"])      # False — \"\" is falsy\nall([])              # True  — nothing fails\nany([])              # False — nothing succeeds\nall(x > 0 for x in nums)   # combine with a generator\n```\n\nRule of thumb: `all`\u002F`any` over a generator expression is the idiomatic \"do all\u002Fany\nitems satisfy this?\" — but remember `all([])` is `True`.\n",{"id":3888,"difficulty":162,"q":3889,"a":3890},"implicit-numeric-conversion","When does Python convert numeric types implicitly?","Python implicitly **widens** numbers in mixed arithmetic: `int` → `float` →\n`complex`, picking the more general type. It does **not** implicitly convert between\nstrings and numbers — that always raises `TypeError`.\n\n```python\n1 + 2.0        # 3.0   — int promoted to float\n3 + 4j         # (3+4j) — promoted to complex\nTrue + 1.5     # 2.5   — bool is an int\n\"3\" + 4        # TypeError — no str\u002Fint coercion\n```\n\nRule of thumb: numeric types auto-promote to the wider type; everything else needs an\nexplicit constructor (`int(s)`, `str(n)`).\n",{"id":3892,"difficulty":150,"q":3893,"a":3894},"comparison-chaining-truthiness","Does `if a == b == c` compare truthiness or values?","It compares **values** via chaining: `a == b == c` means `a == b and b == c`, with\n`b` evaluated once. It is **not** `(a == b) == c`, which would compare a bool to `c`.\n\n```python\n1 == 1 == 1        # True  — (1==1) and (1==1)\n(1 == 1) == 1      # True  — but for the wrong reason: True == 1\n(1 == 1) == 2      # False — True == 2\nTrue == 1 == 1.0   # True  — bool\u002Fint\u002Ffloat all equal here\n```\n\nRule of thumb: chained `==` checks all neighbors are equal; never parenthesize it as\n`(a == b) == c`, which collapses a comparison into a bool.\n",{"id":3896,"difficulty":253,"q":3897,"a":3898},"number-base-conversion","How do you convert between number bases?","`int(s, base)` parses a string in any base 2-36; `bin()`, `oct()`, `hex()` produce\nprefixed string representations. The reverse and forward directions are separate\ntools.\n\n```python\nint(\"ff\", 16)     # 255\nint(\"1010\", 2)    # 10\nint(\"0o17\", 0)    # 15  — base 0 auto-detects from prefix\nbin(10)           # '0b1010'\nhex(255)          # '0xff'\nformat(255, \"x\")  # 'ff'  — no prefix\n```\n\nRule of thumb: `int(s, base)` to parse, `bin`\u002F`oct`\u002F`hex` to produce; use `format`\nor f-strings when you want the digits without the `0b`\u002F`0x` prefix.\n",{"id":3900,"difficulty":162,"q":3901,"a":3902},"container-conversion-pitfalls","What surprises happen when converting between containers?","Converting to `set` or `dict` **drops duplicates and order info**, and converting a\ndict to a list\u002Ftuple yields its **keys**, not items. These lossy conversions catch\npeople off guard.\n\n```python\nlist({3, 1, 2})        # order not guaranteed by value\nset([1, 1, 2])         # {1, 2} — dups removed\nlist({\"a\": 1, \"b\": 2}) # ['a', 'b'] — keys only\ndict([(\"a\", 1), (\"b\", 2)])  # {'a': 1, 'b': 2} — from pairs\ntuple(\"ab\")            # ('a', 'b')\n```\n\nRule of thumb: `list(dict)` gives keys (use `.items()` for pairs), and `set()` is a\nquick dedupe but discards order and duplicate counts.\n",{"id":3904,"difficulty":162,"q":3905,"a":3906},"str-vs-repr-conversion","What is the difference between `str()` and `repr()`?","`str()` produces a **readable** form for end users (via `__str__`); `repr()` produces\nan **unambiguous** form for developers (via `__repr__`), ideally one you could paste\nback. If `__str__` is missing, `str()` falls back to `__repr__`.\n\n```python\nimport datetime\nd = datetime.date(2026, 6, 19)\nstr(d)        # '2026-06-19'  — friendly\nrepr(d)       # 'datetime.date(2026, 6, 19)'  — reconstructable\nstr(\"hi\")     # 'hi'\nrepr(\"hi\")    # \"'hi'\"  — shows the quotes\n```\n\nRule of thumb: implement `__repr__` for every class (debugging\u002Flogging); add\n`__str__` only when users need a prettier form.\n",{"id":3908,"difficulty":253,"q":3909,"a":3910},"ascii-ord-chr","How do you convert between characters and their code points?","**`ord(c)`** returns the integer Unicode code point of a single character;\n**`chr(n)`** returns the character for a code point. They are inverses and work\nacross the full Unicode range.\n\n```python\nord(\"A\")        # 65\nchr(65)         # \"A\"\nord(\"€\")        # 8364\nchr(8364)       # \"€\"\n[chr(ord(\"a\") + i) for i in range(3)]   # ['a', 'b', 'c']\n```\n\nRule of thumb: `ord`\u002F`chr` bridge characters and integers — useful for ciphers,\nalphabets, and ranges; they handle Unicode, not just ASCII.\n",{"id":3912,"difficulty":150,"q":3913,"a":3914},"nan-truthiness","Is `float('nan')` truthy or falsy?","`nan` is **truthy** — truthiness for floats depends only on being non-zero, and `nan`\nis not zero. This surprises people who expect \"not a number\" to behave like a falsy\nblank.\n\n```python\nbool(float(\"nan\"))   # True!\nbool(0.0)            # False\nimport math\nif math.isnan(x):    # the correct way to handle nan\n    ...\n```\n\nRule of thumb: never use truthiness to detect `nan` — it's truthy; test explicitly\nwith `math.isnan()`.\n",{"description":148},"Python interview questions on falsy values, __bool__\u002F__len__, explicit type conversion, truthiness of empty containers, short-circuiting and\u002For, and is None vs == None.","python\u002Ffundamentals\u002Ftruthiness-conversion","Truthiness & Type Conversion","GX1Vw00KmrPUtccDHubk0n9rS77TPFUXyZN2RsDwfmY",{"id":3921,"title":3922,"body":3923,"description":148,"difficulty":162,"extension":151,"framework":10,"frameworkSlug":8,"meta":3927,"navigation":153,"order":55,"path":3928,"questions":3929,"questionsCount":217,"related":218,"seo":3989,"seoDescription":3990,"stem":3991,"subtopic":3992,"topic":54,"topicSlug":56,"updated":223,"__hash__":3993},"qa\u002Fpython\u002Foop\u002Fdataclasses-slots.md","Dataclasses Slots",{"type":145,"value":3924,"toc":3925},[],{"title":148,"searchDepth":29,"depth":29,"links":3926},[],{},"\u002Fpython\u002Foop\u002Fdataclasses-slots",[3930,3934,3937,3941,3945,3949,3953,3957,3961,3965,3969,3973,3977,3981,3985],{"id":3931,"difficulty":253,"q":3932,"a":3933},"what-dataclass-generates","What does the @dataclass decorator generate for you?","`@dataclass` auto-generates the **boilerplate dunder methods** from the\nclass's annotated fields — primarily **`__init__`**, **`__repr__`**, and\n**`__eq__`**. You declare fields with type hints (and optional defaults) and\nskip writing the constructor by hand.\n\n```python\nfrom dataclasses import dataclass\n\n@dataclass\nclass Point:\n    x: int\n    y: int = 0           # field with a default\n\np = Point(1, 2)\np                        # Point(x=1, y=2)        — generated __repr__\np == Point(1, 2)         # True                   — generated __eq__\n# __init__(self, x, y=0) was generated automatically\n```\n\nYou can opt into more (`order=True` for comparison operators, `frozen=True` for\nimmutability). It removes the repetitive plumbing while leaving normal methods\nup to you.\n",{"id":596,"difficulty":162,"q":3935,"a":3936},"What does frozen=True do on a dataclass?","`@dataclass(frozen=True)` makes instances **immutable** — assigning to a field\nafter creation raises **`FrozenInstanceError`**. As a bonus, frozen dataclasses\nget a generated **`__hash__`**, so they're usable as **dict keys and set\nmembers**.\n\n```python\nfrom dataclasses import dataclass\n\n@dataclass(frozen=True)\nclass Point:\n    x: int\n    y: int\n\np = Point(1, 2)\np.x = 9            # FrozenInstanceError — immutable\n{p: \"origin\"}      # hashable — works as a dict key\n{Point(1, 2), Point(1, 2)}   # one element — equal and hashable\n```\n\nFrozen dataclasses are the concise, modern way to define **immutable value\nobjects**. Use them whenever an object represents a value that shouldn't change\nafter creation.\n",{"id":3938,"difficulty":150,"q":3939,"a":3940},"default-factory","Why must mutable dataclass defaults use field(default_factory=...)?","A bare mutable default (`tags: list = []`) would be **shared across all\ninstances** — the same default-argument trap as in regular functions — so\ndataclasses **forbid it and raise `ValueError`**. Instead, pass\n**`field(default_factory=list)`**, a zero-arg callable that creates a **fresh\nobject per instance**.\n\n```python\nfrom dataclasses import dataclass, field\n\n@dataclass\nclass Article:\n    title: str\n    tags: list = field(default_factory=list)   # new list each instance\n    # tags: list = []   # would raise ValueError at class-definition time\n\na, b = Article(\"A\"), Article(\"B\")\na.tags.append(\"python\")\nb.tags             # []  — independent, no shared state\n```\n\nUse `default_factory` for any mutable default (`list`, `dict`, `set`) or for a\nvalue that must be computed at construction time. Plain immutable defaults\n(numbers, strings, tuples) are fine inline.\n",{"id":3942,"difficulty":150,"q":3943,"a":3944},"slots","What is __slots__ and what does it buy you?","`__slots__` declares a **fixed set of allowed attributes**, so instances store\nthem in a compact array instead of a per-instance **`__dict__`**. This **saves\nsignificant memory** (no dict per object) and gives **faster attribute access**.\nThe trade-off: you **can't add new attributes** not listed in slots.\n\n```python\nclass Point:\n    __slots__ = (\"x\", \"y\")     # no per-instance __dict__\n    def __init__(self, x, y):\n        self.x, self.y = x, y\n\np = Point(1, 2)\np.x               # 2 — fast access\np.z = 3           # AttributeError — 'z' not in __slots__\n# p.__dict__      # also AttributeError — there is no dict\n\nfrom dataclasses import dataclass\n@dataclass(slots=True)        # dataclasses can generate slots (3.10+)\nclass Fast:\n    x: int\n    y: int\n```\n\nReach for `__slots__` when you create **huge numbers of small objects** and\nmemory matters. For ordinary classes the flexibility of `__dict__` is usually\nworth more than the savings.\n",{"id":3946,"difficulty":162,"q":3947,"a":3948},"dataclass-vs-namedtuple","When would you choose a dataclass over a namedtuple or NamedTuple?","A **`namedtuple`**\u002F**`typing.NamedTuple`** is an **immutable tuple** subclass —\nlightweight, hashable, iterable, and index-accessible, but values can't change.\nA **`@dataclass`** is a regular class — **mutable by default**, supports methods,\ninheritance, and `default_factory`, but isn't a tuple (no unpacking\u002Findexing\nunless you add it).\n\n```python\nfrom typing import NamedTuple\nfrom dataclasses import dataclass\n\nclass PointNT(NamedTuple):     # immutable, tuple-like\n    x: int\n    y: int\nx, y = PointNT(1, 2)           # unpacks like a tuple\n\n@dataclass\nclass PointDC:                 # mutable, full class\n    x: int\n    y: int\n    def shift(self, dx):       # can hold real methods\n        self.x += dx\n\nPointNT(1, 2)[0]   # 1 — index access (tuple)\nPointDC(1, 2).shift(5)         # mutate in place\n```\n\nChoose a **NamedTuple** for small immutable records and tuple semantics; choose a\n**dataclass** when you need mutability, methods, or richer field control. Use\n`frozen=True` dataclasses for immutable value objects that still want class\nfeatures.\n",{"id":3950,"difficulty":162,"q":3951,"a":3952},"post-init","What is __post_init__ used for?","`__post_init__` runs **automatically right after the generated `__init__`** —\nit's the hook for **validation and derived fields** that depend on the\nconstructor arguments. It pairs with `field(init=False)` to declare attributes\nthat aren't constructor parameters but are computed afterward.\n\n```python\nfrom dataclasses import dataclass, field\n\n@dataclass\nclass Rectangle:\n    width: float\n    height: float\n    area: float = field(init=False)     # not a constructor arg\n\n# filled in after __init__:\n    def __post_init__(self):\n        if self.width \u003C= 0 or self.height \u003C= 0:\n            raise ValueError(\"dimensions must be positive\")   # validation\n        self.area = self.width * self.height                  # derived field\n\nr = Rectangle(3, 4)\nr.area            # 12 — computed in __post_init__\nRectangle(-1, 4)  # ValueError\n```\n\nUse `__post_init__` whenever a dataclass needs logic the auto-generated\n`__init__` can't express — validation, normalization, or fields derived from\nothers.\n",{"id":3954,"difficulty":162,"q":3955,"a":3956},"field-options","What can the `field()` function configure besides defaults?","`field()` tunes per-attribute behavior: **`init=False`** (exclude from `__init__`),\n**`repr=False`** (hide from `__repr__`), **`compare=False`** (exclude from `__eq__`\u002F\nordering), **`default`\u002F`default_factory`**, and **`metadata`** (arbitrary dict for\ntools).\n\n```python\nfrom dataclasses import dataclass, field\n\n@dataclass\nclass User:\n    name: str\n    password: str = field(repr=False)        # keep out of repr\u002Flogs\n    id: int = field(default=0, compare=False) # ignored in equality\n    tags: list = field(default_factory=list)\n\nu = User(\"ada\", \"secret\")\nu                       # User(name='ada', tags=[]) — no password shown\n```\n\nRule of thumb: use `field()` to fine-tune which attributes participate in init,\nrepr, and comparison — handy for secrets, caches, and derived values.\n",{"id":3958,"difficulty":253,"q":3959,"a":3960},"asdict-astuple","How do you convert a dataclass to a dict or tuple?","Use **`dataclasses.asdict()`** and **`astuple()`** — they **recurse** into nested\ndataclasses, lists, and dicts, producing plain data structures (handy for JSON).\n`replace()` makes a modified copy.\n\n```python\nfrom dataclasses import dataclass, asdict, astuple, replace\n\n@dataclass\nclass Point:\n    x: int\n    y: int\n\np = Point(1, 2)\nasdict(p)           # {'x': 1, 'y': 2}\nastuple(p)          # (1, 2)\nreplace(p, y=9)     # Point(x=1, y=9) — new object, p unchanged\n```\n\nRule of thumb: `asdict`\u002F`astuple` for serialization (they deep-copy nested data),\n`replace` for immutable-style \"copy with changes.\"\n",{"id":3962,"difficulty":150,"q":3963,"a":3964},"dataclass-inheritance","What is the field-ordering rule when inheriting dataclasses?","Fields combine in **MRO order, base fields first**. Because Python won't allow a\nnon-default parameter after a default one, a subclass **can't add a required field\nafter the base supplied defaults** — it raises `TypeError` at class creation.\n\n```python\nfrom dataclasses import dataclass\n\n@dataclass\nclass Base:\n    a: int\n    b: int = 0\n\n@dataclass\nclass Sub(Base):\n    c: int          # TypeError: non-default 'c' follows default 'b'\n\n# fix: give c a default, or make b required\n```\n\nRule of thumb: once a base dataclass introduces a defaulted field, every later field\n(including in subclasses) must also have a default.\n",{"id":3966,"difficulty":162,"q":3967,"a":3968},"kw-only-fields","How does `kw_only=True` help with dataclass inheritance?","`@dataclass(kw_only=True)` (3.10+) makes fields **keyword-only** in `__init__`, which\n**sidesteps the default-ordering problem** — keyword-only params have no positional\nordering constraint. You can also mark individual fields with `field(kw_only=True)`.\n\n```python\nfrom dataclasses import dataclass\n\n@dataclass(kw_only=True)\nclass Config:\n    a: int = 0\n    b: int             # required, but keyword-only — order is fine\n\nConfig(b=5)            # OK — must pass by keyword\nConfig(5)              # TypeError — positional not allowed\n```\n\nRule of thumb: use `kw_only=True` to mix required and defaulted fields freely\n(especially across inheritance) and to force explicit, self-documenting calls.\n",{"id":3970,"difficulty":150,"q":3971,"a":3972},"slots-and-defaults-conflict","What's a common pitfall combining `__slots__` with class-level defaults?","A name can't be **both a slot and a class attribute** — listing `x` in `__slots__`\nand also assigning `x = 0` in the body raises `ValueError` (the class attribute\nwould shadow the slot descriptor). With `@dataclass(slots=True)` this is handled, but\nhand-written slots classes hit it.\n\n```python\nclass Bad:\n    __slots__ = (\"x\",)\n    x = 0              # ValueError: 'x' in __slots__ conflicts with class variable\n\nclass Ok:\n    __slots__ = (\"x\",)\n    def __init__(self, x=0):\n        self.x = x     # set defaults in __init__, not the class body\n```\n\nRule of thumb: with manual `__slots__`, supply defaults in `__init__` rather than as\nclass-body assignments to the slotted names.\n",{"id":3974,"difficulty":150,"q":3975,"a":3976},"slots-inheritance","Why might `__slots__` not save memory in a subclass?","If **any class in the hierarchy lacks `__slots__`**, instances get a `__dict__`\nanyway, erasing the savings. Every class in the chain (including the base) must\ndefine `__slots__` — and the subclass should only list its **new** attributes.\n\n```python\nclass Base:                 # no __slots__ -> instances get __dict__\n    pass\nclass Sub(Base):\n    __slots__ = (\"x\",)      # useless: __dict__ still present via Base\n\nSub().__dict__              # exists! no memory win\n\nclass Base2:\n    __slots__ = ()\nclass Sub2(Base2):\n    __slots__ = (\"x\",)      # now truly dict-free\n```\n\nRule of thumb: for slots to pay off, give every class in the MRO `__slots__` (use\n`__slots__ = ()` on otherwise-empty bases) and don't re-list inherited slots.\n",{"id":3978,"difficulty":162,"q":3979,"a":3980},"order-true","What does `order=True` add to a dataclass?","`@dataclass(order=True)` generates the comparison dunders (`__lt__`, `__le__`,\n`__gt__`, `__ge__`) that compare instances **field-by-field as a tuple**, in\ndeclaration order. This makes dataclasses sortable.\n\n```python\nfrom dataclasses import dataclass\n\n@dataclass(order=True)\nclass Version:\n    major: int\n    minor: int\n\nsorted([Version(1, 2), Version(1, 0), Version(0, 9)])\n# [Version(0, 9), Version(1, 0), Version(1, 2)]\nVersion(1, 0) \u003C Version(1, 2)   # True\n```\n\nRule of thumb: add `order=True` to make value objects sortable; control the sort key\nby field order, and use `field(compare=False)` to exclude a field.\n",{"id":3982,"difficulty":150,"q":3983,"a":3984},"sort-index-pattern","How do you sort a dataclass by a computed key with `order=True`?","Add a dedicated **`sort_index`** field with `field(init=False, repr=False)`, set it\nin `__post_init__`, and exclude the real data fields from comparison. Ordering then\nuses only your computed key.\n\n```python\nfrom dataclasses import dataclass, field\n\n@dataclass(order=True)\nclass Item:\n    sort_index: float = field(init=False, repr=False)\n    name: str = field(compare=False)\n    priority: int = field(compare=False)\n    def __post_init__(self):\n        self.sort_index = self.priority\n\nsorted([Item(\"a\", 3), Item(\"b\", 1)])   # ordered by priority\n```\n\nRule of thumb: a `sort_index` field plus `compare=False` on data fields is the\ncanonical recipe for custom-keyed ordering (e.g. priority queues).\n",{"id":3986,"difficulty":162,"q":3987,"a":3988},"namedtuple-vs-dict-memory","How do dataclass, slotted dataclass, and NamedTuple compare on memory?","Roughly: a **plain dataclass** carries a per-instance `__dict__` (largest);\n**`slots=True`** drops the dict (much smaller, fixed attributes); a **NamedTuple** is\na tuple subclass (smallest, immutable, but no per-instance methods state).\n\n```python\nfrom dataclasses import dataclass\nfrom typing import NamedTuple\n\n@dataclass\nclass A: x: int; y: int          # has __dict__\n\n@dataclass(slots=True)\nclass B: x: int; y: int          # no __dict__ — leaner\n\nclass C(NamedTuple): x: int; y: int   # tuple-backed, immutable, leanest\n```\n\nRule of thumb: many objects + mutability → `slots=True` dataclass; immutable records\n→ NamedTuple; convenience over footprint → plain dataclass.\n",{"description":148},"Python interview questions on @dataclass — generated methods, frozen=True, field(default_factory=...) for mutable defaults, __slots__ for memory and speed, dataclass vs namedtuple vs NamedTuple, and __post_init__.","python\u002Foop\u002Fdataclasses-slots","Dataclasses & __slots__","BSUKMNmr3g6LSd3hFOXcblaKMFUWr43yx1KhdFHG1Wo",{"id":3995,"title":3996,"body":3997,"description":148,"difficulty":150,"extension":151,"framework":10,"frameworkSlug":8,"meta":4001,"navigation":153,"order":64,"path":4002,"questions":4003,"questionsCount":217,"related":218,"seo":4063,"seoDescription":4064,"stem":4065,"subtopic":4066,"topic":54,"topicSlug":56,"updated":223,"__hash__":4067},"qa\u002Fpython\u002Foop\u002Fabc-protocols.md","Abc Protocols",{"type":145,"value":3998,"toc":3999},[],{"title":148,"searchDepth":29,"depth":29,"links":4000},[],{},"\u002Fpython\u002Foop\u002Fabc-protocols",[4004,4008,4012,4016,4020,4024,4028,4032,4036,4040,4044,4048,4052,4055,4059],{"id":4005,"difficulty":162,"q":4006,"a":4007},"what-is-abc","What is an abstract base class and what does @abstractmethod do?","An **abstract base class (ABC)** is a class that **can't be instantiated\ndirectly** and defines a set of methods subclasses **must implement**. You build\none by subclassing `abc.ABC` and decorating required methods with\n**`@abstractmethod`**. Python refuses to instantiate any subclass that leaves an\nabstract method unimplemented.\n\n```python\nfrom abc import ABC, abstractmethod\n\nclass Shape(ABC):\n    @abstractmethod\n    def area(self):            # subclasses MUST implement this\n        ...\n\nShape()                        # TypeError — can't instantiate abstract class\n\nclass Circle(Shape):\n    def __init__(self, r):\n        self.r = r\n    def area(self):            # concrete implementation\n        return 3.14159 * self.r ** 2\n\nCircle(2).area()               # 12.566... — works\n```\n\n`@abstractmethod` turns the \"must implement\" contract into an **enforced,\nload-time check** rather than a runtime `NotImplementedError` later. It defines\nan interface that subclasses are required to fulfill.\n",{"id":4009,"difficulty":162,"q":4010,"a":4011},"why-use-abcs","Why use ABCs instead of just regular classes?","ABCs **enforce an interface** — they guarantee at instantiation time that\nsubclasses provide the required methods, catching mistakes **early** instead of\nblowing up deep in your code. They also document intent clearly and integrate\nwith `isinstance` checks, so callers can verify capabilities.\n\n```python\nfrom abc import ABC, abstractmethod\n\nclass Storage(ABC):\n    @abstractmethod\n    def save(self, key, value): ...\n    @abstractmethod\n    def load(self, key): ...\n\nclass FileStorage(Storage):\n    def save(self, key, value): ...\n    # forgot load() ...\n\nFileStorage()      # TypeError — flags the missing method immediately\nisinstance(FileStorage, type) and issubclass(FileStorage, Storage)  # True\n```\n\nUse an ABC when you have a **family of classes that must share a contract** and\nyou want that contract enforced. For looser, optional interfaces, plain duck\ntyping or a `Protocol` may fit better.\n",{"id":4013,"difficulty":150,"q":4014,"a":4015},"collections-abc","What is collections.abc and how is it useful?","`collections.abc` provides the **standard ABCs for Python's container\nprotocols** — `Iterable`, `Iterator`, `Sequence`, `Mapping`, `Hashable`, and\nmore. You use them two ways: as a **base class** to inherit mixin methods, and\nin **`isinstance` checks** to test whether an object supports a protocol.\n\n```python\nfrom collections.abc import Iterable, Sequence, Mapping\n\nisinstance([1, 2], Iterable)   # True\nisinstance(\"abc\", Sequence)    # True\nisinstance({}, Mapping)        # True\n\nclass MyList(Sequence):        # inherit the protocol's mixin methods\n    def __init__(self, data):\n        self._data = data\n    def __getitem__(self, i):\n        return self._data[i]\n    def __len__(self):\n        return len(self._data)\n    # get __contains__, __iter__, __reversed__, index, count for FREE\n```\n\nSubclassing `Sequence` and implementing just `__getitem__` + `__len__` gives you\na fully functional sequence. Prefer these standard ABCs over inventing your own\nfor container-like types.\n",{"id":4017,"difficulty":162,"q":4018,"a":4019},"duck-typing-vs-abcs","What is the difference between duck typing and ABCs?","**Duck typing** is \"if it walks like a duck and quacks like a duck, it's a\nduck\" — Python doesn't check the type, it just **tries the operation** and works\nif the needed methods exist. **ABCs add an explicit, enforced contract** on top,\nletting you assert membership and catch missing methods up front.\n\n```python\n# Duck typing — no declared interface, just call and hope:\ndef make_it_quack(thing):\n    thing.quack()          # works for ANYTHING with a quack() method\n\nclass Dog:\n    def quack(self): return \"woof-quack\"\nmake_it_quack(Dog())       # works — Dog \"is\" a duck here\n\n# ABC — explicit, checkable contract:\nfrom abc import ABC, abstractmethod\nclass Duck(ABC):\n    @abstractmethod\n    def quack(self): ...\n```\n\nDuck typing is flexible and Pythonic but defers errors to runtime; ABCs trade\nsome flexibility for **early enforcement and clear interfaces**. Use duck typing\nfor loose coupling, ABCs when you want guarantees.\n",{"id":4021,"difficulty":150,"q":4022,"a":4023},"typing-protocol","What is typing.Protocol and how does it relate to duck typing?","`typing.Protocol` enables **structural typing** (\"static duck typing\"): a class\nsatisfies a protocol simply by **having the right methods\u002Fattributes** — no\nexplicit inheritance needed. Type checkers verify the structure **statically**,\ngiving you duck typing's flexibility **with** static safety. Add\n**`@runtime_checkable`** to also allow `isinstance` checks at runtime.\n\n```python\nfrom typing import Protocol, runtime_checkable\n\n@runtime_checkable\nclass Drawable(Protocol):\n    def draw(self) -> str: ...     # required shape, not inheritance\n\nclass Button:                      # does NOT inherit Drawable\n    def draw(self) -> str:\n        return \"[Button]\"\n\ndef render(item: Drawable) -> str: # type checker accepts Button\n    return item.draw()\n\nrender(Button())                   # works — structural match\nisinstance(Button(), Drawable)     # True — thanks to @runtime_checkable\n```\n\nUnlike ABCs, classes don't register or subclass anything — matching the\n**structure is enough**. Note `@runtime_checkable` only checks method\n**names\u002Fexistence**, not signatures. Use Protocols for flexible, statically\nverified interfaces; use ABCs when you need shared implementation or explicit\nregistration.\n",{"id":4025,"difficulty":150,"q":4026,"a":4027},"abstract-property","How do you declare an abstract property or abstract classmethod?","Stack the decorators with **`@abstractmethod` innermost** (closest to the\nfunction). It composes with `@property`, `@classmethod`, and `@staticmethod` to\nrequire subclasses to provide those specific member kinds.\n\n```python\nfrom abc import ABC, abstractmethod\n\nclass Config(ABC):\n    @property\n    @abstractmethod\n    def name(self) -> str: ...        # subclasses must define a `name` property\n\n    @classmethod\n    @abstractmethod\n    def from_file(cls, path): ...     # required classmethod\n```\n\nRule of thumb: put `@abstractmethod` directly above the `def`, with `@property`\u002F\n`@classmethod` above it — order matters, and getting it wrong silently drops the\nabstractness.\n",{"id":4029,"difficulty":150,"q":4030,"a":4031},"register-virtual-subclass","What does ABC `.register()` do?","`MyABC.register(SomeClass)` makes `SomeClass` a **virtual subclass**: `isinstance`\u002F\n`issubclass` report `True` **without** inheritance — but the ABC does **not** enforce\nthat abstract methods exist and provides **no** mixin methods. It's a pure\n\"I promise this conforms\" assertion.\n\n```python\nfrom abc import ABC, abstractmethod\n\nclass Quacker(ABC):\n    @abstractmethod\n    def quack(self): ...\n\nclass Duck:                      # no inheritance\n    def quack(self): return \"quack\"\n\nQuacker.register(Duck)\nissubclass(Duck, Quacker)        # True — virtual subclass\nisinstance(Duck(), Quacker)      # True\n# but Python never checks Duck actually has quack()\n```\n\nRule of thumb: `register` retrofits third-party classes into an ABC hierarchy for\nisinstance checks, but you lose abstractness enforcement and inherited mixins.\n",{"id":4033,"difficulty":150,"q":4034,"a":4035},"subclasshook","How does `__subclasshook__` enable structural isinstance checks?","Overriding **`__subclasshook__`** lets an ABC decide `issubclass`\u002F`isinstance` by\n**inspecting structure** (e.g. \"has a `__len__`?\") rather than the inheritance tree.\nThis is how `collections.abc` classes recognize unrelated types.\n\n```python\nfrom abc import ABC, abstractmethod\n\nclass Sized(ABC):\n    @abstractmethod\n    def __len__(self): ...\n    @classmethod\n    def __subclasshook__(cls, C):\n        if cls is Sized:\n            return any(\"__len__\" in B.__dict__ for B in C.__mro__)\n        return NotImplemented\n\nisinstance([1, 2], Sized)     # True — list has __len__, no registration\n```\n\nRule of thumb: `__subclasshook__` powers duck-typed `isinstance` (like `Iterable`\u002F\n`Hashable`); return `NotImplemented` to fall back to normal subclass logic.\n",{"id":4037,"difficulty":162,"q":4038,"a":4039},"protocol-vs-abc-when","When should you choose a Protocol over an ABC?","Choose a **Protocol** when you only need a **structural contract** and don't control\n(or don't want to couple) the implementing classes — great for typing third-party or\nbuilt-in objects. Choose an **ABC** when you want **shared mixin code**, explicit\nregistration, or instantiation-time enforcement.\n\n```python\nfrom typing import Protocol\n\nclass SupportsClose(Protocol):    # any object with close() fits, no inheritance\n    def close(self) -> None: ...\n\ndef cleanup(r: SupportsClose) -> None:\n    r.close()                     # files, sockets, custom — all match\n```\n\nRule of thumb: Protocol = \"fits by shape, no coupling, static checks\"; ABC =\n\"shared base, enforced at runtime, explicit hierarchy.\"\n",{"id":4041,"difficulty":162,"q":4042,"a":4043},"notimplemented-vs-error","What is the difference between `NotImplemented` and `NotImplementedError`?","**`NotImplementedError`** is an **exception** you `raise` to signal an unfinished\nmethod. **`NotImplemented`** is a **singleton value** you `return` from binary\nspecial methods (`__eq__`, `__add__`) to tell Python \"try the reflected operation.\"\nThey are not interchangeable.\n\n```python\nclass Base:\n    def render(self):\n        raise NotImplementedError   # subclass must override\n\nclass Money:\n    def __eq__(self, other):\n        if not isinstance(other, Money):\n            return NotImplemented    # let Python try other.__eq__\n        return self.amount == other.amount\n```\n\nRule of thumb: `raise NotImplementedError` for abstract-ish stubs; `return\nNotImplemented` from operator dunders to allow fallback\u002Freflection.\n",{"id":4045,"difficulty":150,"q":4046,"a":4047},"abc-metaclass","Why does `abc.ABC` exist when there's already `ABCMeta`?","ABCs work via the **`ABCMeta` metaclass**. `abc.ABC` is just a convenience base class\nthat already uses that metaclass, so you can write `class X(ABC)` instead of the\nverbose `class X(metaclass=ABCMeta)`. You need the metaclass form when combining with\nanother metaclass.\n\n```python\nfrom abc import ABC, ABCMeta, abstractmethod\n\nclass A(ABC):                       # the easy, common way\n    @abstractmethod\n    def f(self): ...\n\nclass B(metaclass=ABCMeta):         # equivalent, explicit metaclass\n    @abstractmethod\n    def f(self): ...\n```\n\nRule of thumb: use `ABC` for everyday abstract classes; drop to\n`metaclass=ABCMeta` only when you must merge it with a custom metaclass.\n",{"id":4049,"difficulty":162,"q":4050,"a":4051},"abstractmethod-with-body","Can an abstract method have an implementation, and why would it?","Yes. `@abstractmethod` still **requires the subclass to override** it, but the body\ncan hold shared logic the subclass calls via `super()`. This gives a default while\nkeeping the override mandatory.\n\n```python\nfrom abc import ABC, abstractmethod\n\nclass Validator(ABC):\n    @abstractmethod\n    def validate(self, x):\n        if x is None:                 # shared precondition\n            raise ValueError(\"None not allowed\")\n\n    # subclass:\nclass Positive(Validator):\n    def validate(self, x):\n        super().validate(x)           # reuse base checks\n        return x > 0\n```\n\nRule of thumb: an abstract method with a body provides reusable scaffolding that\nsubclasses extend via `super()` — but the override is still required.\n",{"id":2303,"difficulty":162,"q":4053,"a":4054},"Can a Protocol require attributes, not just methods?","Yes — declare them as **annotated class variables** in the Protocol body. A class\nsatisfies the protocol if it has matching attributes (commonly set in `__init__`),\nchecked structurally by the type checker.\n\n```python\nfrom typing import Protocol\n\nclass Named(Protocol):\n    name: str            # required attribute\n    def greet(self) -> str: ...\n\nclass User:\n    def __init__(self, name: str):\n        self.name = name          # satisfies `name: str`\n    def greet(self) -> str:\n        return f\"hi {self.name}\"\n```\n\nRule of thumb: Protocols can specify both attributes and methods — annotate the\nattribute in the body; no value\u002Fassignment needed.\n",{"id":4056,"difficulty":162,"q":4057,"a":4058},"enforcing-abstract-at-runtime","At what moment does an unimplemented abstract method raise?","The check fires at **instantiation**, not definition. Defining an incomplete\nsubclass is fine; the `TypeError` only happens when you try to **create an\ninstance**. This lets you build deep abstract hierarchies.\n\n```python\nfrom abc import ABC, abstractmethod\n\nclass A(ABC):\n    @abstractmethod\n    def f(self): ...\n\nclass B(A):       # OK — still abstract, no error yet\n    pass\n\nclass C(B):\n    def f(self): return 1\n\nB()               # TypeError here — at instantiation\nC()               # fine\n```\n\nRule of thumb: abstractness is enforced when you instantiate, so intermediate\nabstract subclasses are legal and common.\n",{"id":4060,"difficulty":150,"q":4061,"a":4062},"collections-abc-mixins","Which methods must you implement to get free mixins from `collections.abc`?","Each container ABC lists **abstract methods** (you implement) and **mixin methods**\n(you get free). E.g. `Mapping` needs `__getitem__`, `__len__`, `__iter__` and then\ngives you `keys`, `items`, `values`, `get`, `__contains__`, `__eq__`.\n\n```python\nfrom collections.abc import Mapping\n\nclass FrozenDict(Mapping):\n    def __init__(self, d): self._d = dict(d)\n    def __getitem__(self, k): return self._d[k]   # required\n    def __iter__(self): return iter(self._d)       # required\n    def __len__(self): return len(self._d)         # required\n\nfd = FrozenDict({\"a\": 1})\nfd.get(\"a\"), \"a\" in fd, list(fd.keys())   # all work for free\n```\n\nRule of thumb: implement only the few abstract methods an `abc` lists and inherit the\nrest — far less code than building a container by hand.\n",{"description":148},"Python interview questions on abstract base classes and @abstractmethod, why use ABCs, collections.abc, duck typing vs ABCs, and typing.Protocol for structural\u002Fstatic duck typing with runtime_checkable.","python\u002Foop\u002Fabc-protocols","Abstract Base Classes & Protocols","f1wPt2Bhk7VL7JAaEEtUrhsStq6oorZaN3m7w5-YwDU",1782244099476]