[{"data":1,"prerenderedAt":151},["ShallowReactive",2],{"topic-python-functional":3},{"framework":4,"topic":15,"subtopics":24},{"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",{"id":16,"description":17,"extension":7,"frameworkSlug":8,"meta":18,"name":19,"order":20,"slug":21,"stem":22,"__hash__":23},"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",[25,69,108],{"id":26,"title":27,"body":28,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":37,"navigation":38,"order":13,"path":39,"questions":40,"related":62,"seo":63,"seoDescription":64,"stem":65,"subtopic":66,"topic":19,"topicSlug":21,"updated":67,"__hash__":68},"qa\u002Fpython\u002Ffunctional\u002Ffunctools.md","Functools",{"type":29,"value":30,"toc":31},"minimark",[],{"title":32,"searchDepth":33,"depth":33,"links":34},"",2,[],"medium","md",{},true,"\u002Fpython\u002Ffunctional\u002Ffunctools",[41,45,49,53,57],{"id":42,"difficulty":35,"q":43,"a":44},"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":46,"difficulty":35,"q":47,"a":48},"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":50,"difficulty":35,"q":51,"a":52},"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":54,"difficulty":35,"q":55,"a":56},"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":58,"difficulty":59,"q":60,"a":61},"cached-property-singledispatch","hard","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",null,{"description":32},"Python interview questions on functools — lru_cache and cache, partial, wraps, reduce, cached_property, and singledispatch.","python\u002Ffunctional\u002Ffunctools","functools","2026-06-18","O3Gm_dZN9ODH7b85aBNZuE1Zy-RWDP_xQIzayQMjmcM",{"id":70,"title":71,"body":72,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":76,"navigation":38,"order":33,"path":77,"questions":78,"related":62,"seo":103,"seoDescription":104,"stem":105,"subtopic":106,"topic":19,"topicSlug":21,"updated":67,"__hash__":107},"qa\u002Fpython\u002Ffunctional\u002Fmap-filter-reduce.md","Map Filter Reduce",{"type":29,"value":73,"toc":74},[],{"title":32,"searchDepth":33,"depth":33,"links":75},[],{},"\u002Fpython\u002Ffunctional\u002Fmap-filter-reduce",[79,84,88,92,95,99],{"id":80,"difficulty":81,"q":82,"a":83},"map-basics","easy","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":85,"difficulty":35,"q":86,"a":87},"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":89,"difficulty":81,"q":90,"a":91},"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":54,"difficulty":35,"q":93,"a":94},"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":96,"difficulty":35,"q":97,"a":98},"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":100,"difficulty":35,"q":101,"a":102},"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",{"description":32},"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","y6sFK7mPcsPaQc2XKJdXy2VVJ6uRfXj-LWrXBILxPQc",{"id":109,"title":110,"body":111,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":115,"navigation":38,"order":11,"path":116,"questions":117,"related":62,"seo":146,"seoDescription":147,"stem":148,"subtopic":149,"topic":19,"topicSlug":21,"updated":67,"__hash__":150},"qa\u002Fpython\u002Ffunctional\u002Fitertools.md","Itertools",{"type":29,"value":112,"toc":113},[],{"title":32,"searchDepth":33,"depth":33,"links":114},[],{},"\u002Fpython\u002Ffunctional\u002Fitertools",[118,122,126,130,134,138,142],{"id":119,"difficulty":35,"q":120,"a":121},"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":123,"difficulty":81,"q":124,"a":125},"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":127,"difficulty":35,"q":128,"a":129},"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":131,"difficulty":35,"q":132,"a":133},"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":135,"difficulty":59,"q":136,"a":137},"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":139,"difficulty":35,"q":140,"a":141},"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":143,"difficulty":35,"q":144,"a":145},"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",{"description":32},"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","1Gxse2j1ry-O6nBlZJIbeCn668VKgt8dM8TEa8_-mMU",1781808675193]