[{"data":1,"prerenderedAt":221},["ShallowReactive",2],{"topic-python-data-structures":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-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",[25,68,103,141,181],{"id":26,"title":27,"body":28,"description":32,"difficulty":34,"extension":35,"framework":10,"frameworkSlug":8,"meta":36,"navigation":37,"order":13,"path":38,"questions":39,"related":61,"seo":62,"seoDescription":63,"stem":64,"subtopic":65,"topic":19,"topicSlug":21,"updated":66,"__hash__":67},"qa\u002Fpython\u002Fdata-structures\u002Flists.md","Lists",{"type":29,"value":30,"toc":31},"minimark",[],{"title":32,"searchDepth":20,"depth":20,"links":33},"",[],"medium","md",{},true,"\u002Fpython\u002Fdata-structures\u002Flists",[40,44,48,52,56],{"id":41,"difficulty":34,"q":42,"a":43},"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":45,"difficulty":34,"q":46,"a":47},"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":49,"difficulty":34,"q":50,"a":51},"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":53,"difficulty":34,"q":54,"a":55},"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":57,"difficulty":58,"q":59,"a":60},"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",null,{"description":32},"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","2026-06-18","UKHUEr1MtYNcjg937qLbMk2iR2kGGF2n3zhJyYXTljY",{"id":69,"title":70,"body":71,"description":32,"difficulty":34,"extension":35,"framework":10,"frameworkSlug":8,"meta":75,"navigation":37,"order":20,"path":76,"questions":77,"related":61,"seo":98,"seoDescription":99,"stem":100,"subtopic":101,"topic":19,"topicSlug":21,"updated":66,"__hash__":102},"qa\u002Fpython\u002Fdata-structures\u002Ftuples.md","Tuples",{"type":29,"value":72,"toc":73},[],{"title":32,"searchDepth":20,"depth":20,"links":74},[],{},"\u002Fpython\u002Fdata-structures\u002Ftuples",[78,82,86,90,94],{"id":79,"difficulty":58,"q":80,"a":81},"tuple-vs-list","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":83,"difficulty":58,"q":84,"a":85},"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":87,"difficulty":34,"q":88,"a":89},"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":91,"difficulty":34,"q":92,"a":93},"namedtuple","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":95,"difficulty":34,"q":96,"a":97},"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",{"description":32},"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","k8gJCQHd3O516LEW4hNwzbc-dMsxspsIWpQ8XWKiCig",{"id":104,"title":105,"body":106,"description":32,"difficulty":34,"extension":35,"framework":10,"frameworkSlug":8,"meta":110,"navigation":37,"order":11,"path":111,"questions":112,"related":61,"seo":137,"seoDescription":138,"stem":139,"subtopic":105,"topic":19,"topicSlug":21,"updated":66,"__hash__":140},"qa\u002Fpython\u002Fdata-structures\u002Fdictionaries.md","Dictionaries",{"type":29,"value":107,"toc":108},[],{"title":32,"searchDepth":20,"depth":20,"links":109},[],{},"\u002Fpython\u002Fdata-structures\u002Fdictionaries",[113,117,121,125,129,133],{"id":114,"difficulty":58,"q":115,"a":116},"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":118,"difficulty":58,"q":119,"a":120},"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":122,"difficulty":34,"q":123,"a":124},"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":126,"difficulty":34,"q":127,"a":128},"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":130,"difficulty":34,"q":131,"a":132},"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":134,"difficulty":58,"q":135,"a":136},"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",{"description":32},"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","LzhuhDxYYShbZNpJ1GNHVfGL3cDVdrMl9q-LhVisbXs",{"id":142,"title":143,"body":144,"description":32,"difficulty":34,"extension":35,"framework":10,"frameworkSlug":8,"meta":148,"navigation":37,"order":149,"path":150,"questions":151,"related":61,"seo":176,"seoDescription":177,"stem":178,"subtopic":179,"topic":19,"topicSlug":21,"updated":66,"__hash__":180},"qa\u002Fpython\u002Fdata-structures\u002Fsets.md","Sets",{"type":29,"value":145,"toc":146},[],{"title":32,"searchDepth":20,"depth":20,"links":147},[],{},4,"\u002Fpython\u002Fdata-structures\u002Fsets",[152,156,160,164,168,172],{"id":153,"difficulty":58,"q":154,"a":155},"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":157,"difficulty":34,"q":158,"a":159},"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":161,"difficulty":58,"q":162,"a":163},"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":165,"difficulty":58,"q":166,"a":167},"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":169,"difficulty":34,"q":170,"a":171},"frozenset","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":173,"difficulty":58,"q":174,"a":175},"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",{"description":32},"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","cTcBrrIA7tgVyW4p3hQf_V6QjLhZcf7eBJuL-k_iHL0",{"id":182,"title":183,"body":184,"description":32,"difficulty":34,"extension":35,"framework":10,"frameworkSlug":8,"meta":188,"navigation":37,"order":189,"path":190,"questions":191,"related":61,"seo":216,"seoDescription":217,"stem":218,"subtopic":219,"topic":19,"topicSlug":21,"updated":66,"__hash__":220},"qa\u002Fpython\u002Fdata-structures\u002Fcollections-module.md","Collections Module",{"type":29,"value":185,"toc":186},[],{"title":32,"searchDepth":20,"depth":20,"links":187},[],{},5,"\u002Fpython\u002Fdata-structures\u002Fcollections-module",[192,196,200,204,208,212],{"id":193,"difficulty":58,"q":194,"a":195},"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":197,"difficulty":58,"q":198,"a":199},"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":201,"difficulty":34,"q":202,"a":203},"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":205,"difficulty":34,"q":206,"a":207},"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":209,"difficulty":34,"q":210,"a":211},"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":213,"difficulty":58,"q":214,"a":215},"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",{"description":32},"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","LZW7rGcpTd9BsXIZIOFd-X_5qK8sKAY3AZmE-BjNbyY",1781808674656]