[{"data":1,"prerenderedAt":823},["ShallowReactive",2],{"blog-\u002Fblog\u002Fpython-collections-module-explained":3},{"id":4,"title":5,"body":6,"description":809,"difficulty":810,"extension":811,"framework":812,"frameworkSlug":60,"meta":813,"navigation":88,"order":121,"path":814,"qaPath":815,"seo":816,"stem":817,"subtopic":818,"topic":819,"topicSlug":820,"updated":821,"__hash__":822},"blog\u002Fblog\u002Fpython-collections-module-explained.md","Python collections Module Explained — Counter, defaultdict, and deque",{"type":7,"value":8,"toc":799},"minimark",[9,14,46,50,55,163,170,174,184,274,285,289,299,369,388,392,410,516,525,529,540,639,642,646,652,757,764,768,795],[10,11,13],"h2",{"id":12},"the-collections-module-explained","The collections module, explained",[15,16,17,18,22,23,22,26,29,30,33,34,37,38,41,42,45],"p",{},"The built-in ",[19,20,21],"code",{},"list",", ",[19,24,25],{},"dict",[19,27,28],{},"set",", and ",[19,31,32],{},"tuple"," cover most needs, but the ",[19,35,36],{},"collections","\nmodule adds specialised containers that make common tasks shorter and faster. Knowing them\nis a quick way to look fluent — ",[19,39,40],{},"Counter"," and ",[19,43,44],{},"defaultdict"," in particular show up\nconstantly.",[10,47,49],{"id":48},"counter-tallying-made-trivial","Counter — tallying made trivial",[15,51,52,54],{},[19,53,40],{}," is a dict subclass that counts hashable items. It turns a manual tally loop into\none line:",[56,57,62],"pre",{"className":58,"code":59,"language":60,"meta":61,"style":61},"language-python shiki shiki-themes github-light github-dark","from collections import Counter\n\nc = Counter(\"mississippi\")\nc                       # Counter({'s': 4, 'i': 4, 'p': 2, 'm': 1})\nc[\"s\"]                  # 4\nc[\"z\"]                  # 0 — missing keys return 0, not KeyError\nc.most_common(2)        # [('s', 4), ('i', 4)]\n","python","",[19,63,64,83,90,109,119,134,147],{"__ignoreMap":61},[65,66,69,73,77,80],"span",{"class":67,"line":68},"line",1,[65,70,72],{"class":71},"szBVR","from",[65,74,76],{"class":75},"sVt8B"," collections ",[65,78,79],{"class":71},"import",[65,81,82],{"class":75}," Counter\n",[65,84,86],{"class":67,"line":85},2,[65,87,89],{"emptyLinePlaceholder":88},true,"\n",[65,91,93,96,99,102,106],{"class":67,"line":92},3,[65,94,95],{"class":75},"c ",[65,97,98],{"class":71},"=",[65,100,101],{"class":75}," Counter(",[65,103,105],{"class":104},"sZZnC","\"mississippi\"",[65,107,108],{"class":75},")\n",[65,110,112,115],{"class":67,"line":111},4,[65,113,114],{"class":75},"c                       ",[65,116,118],{"class":117},"sJ8bj","# Counter({'s': 4, 'i': 4, 'p': 2, 'm': 1})\n",[65,120,122,125,128,131],{"class":67,"line":121},5,[65,123,124],{"class":75},"c[",[65,126,127],{"class":104},"\"s\"",[65,129,130],{"class":75},"]                  ",[65,132,133],{"class":117},"# 4\n",[65,135,137,139,142,144],{"class":67,"line":136},6,[65,138,124],{"class":75},[65,140,141],{"class":104},"\"z\"",[65,143,130],{"class":75},[65,145,146],{"class":117},"# 0 — missing keys return 0, not KeyError\n",[65,148,150,153,157,160],{"class":67,"line":149},7,[65,151,152],{"class":75},"c.most_common(",[65,154,156],{"class":155},"sj4cs","2",[65,158,159],{"class":75},")        ",[65,161,162],{"class":117},"# [('s', 4), ('i', 4)]\n",[15,164,165,166,169],{},"It also does arithmetic — ",[19,167,168],{},"Counter(a) + Counter(b)"," merges counts — which is great for\ncombining tallies.",[10,171,173],{"id":172},"defaultdict-no-more-key-not-found","defaultdict — no more \"key not found\"",[15,175,176,178,179,183],{},[19,177,44],{}," calls a ",[180,181,182],"strong",{},"factory function"," to supply a default the first time you touch a\nmissing key. The classic use is grouping:",[56,185,187],{"className":58,"code":186,"language":60,"meta":61,"style":61},"from collections import defaultdict\n\ngroups = defaultdict(list)\nfor name in [\"Ada\", \"Alan\", \"Brian\"]:\n    groups[name[0]].append(name)   # no need to check if the key exists first\n\ngroups   # {'A': ['Ada', 'Alan'], 'B': ['Brian']}\n",[19,188,189,200,204,218,248,262,266],{"__ignoreMap":61},[65,190,191,193,195,197],{"class":67,"line":68},[65,192,72],{"class":71},[65,194,76],{"class":75},[65,196,79],{"class":71},[65,198,199],{"class":75}," defaultdict\n",[65,201,202],{"class":67,"line":85},[65,203,89],{"emptyLinePlaceholder":88},[65,205,206,209,211,214,216],{"class":67,"line":92},[65,207,208],{"class":75},"groups ",[65,210,98],{"class":71},[65,212,213],{"class":75}," defaultdict(",[65,215,21],{"class":155},[65,217,108],{"class":75},[65,219,220,223,226,229,232,235,237,240,242,245],{"class":67,"line":111},[65,221,222],{"class":71},"for",[65,224,225],{"class":75}," name ",[65,227,228],{"class":71},"in",[65,230,231],{"class":75}," [",[65,233,234],{"class":104},"\"Ada\"",[65,236,22],{"class":75},[65,238,239],{"class":104},"\"Alan\"",[65,241,22],{"class":75},[65,243,244],{"class":104},"\"Brian\"",[65,246,247],{"class":75},"]:\n",[65,249,250,253,256,259],{"class":67,"line":121},[65,251,252],{"class":75},"    groups[name[",[65,254,255],{"class":155},"0",[65,257,258],{"class":75},"]].append(name)   ",[65,260,261],{"class":117},"# no need to check if the key exists first\n",[65,263,264],{"class":67,"line":136},[65,265,89],{"emptyLinePlaceholder":88},[65,267,268,271],{"class":67,"line":149},[65,269,270],{"class":75},"groups   ",[65,272,273],{"class":117},"# {'A': ['Ada', 'Alan'], 'B': ['Brian']}\n",[15,275,276,277,22,279,22,281,284],{},"The factory can be ",[19,278,21],{},[19,280,28],{},[19,282,283],{},"int"," (for counting), or any zero-argument callable.",[10,286,288],{"id":287},"defaultdict-vs-dictsetdefault","defaultdict vs dict.setdefault",[15,290,291,292,295,296,298],{},"Both handle missing keys, but differently. ",[19,293,294],{},"setdefault"," works on a plain dict and is\nevaluated every call; ",[19,297,44],{}," only invokes the factory on a miss:",[56,300,302],{"className":58,"code":301,"language":60,"meta":61,"style":61},"d = {}\nd.setdefault(\"a\", []).append(1)     # works, but builds a [] every call\n\ndd = defaultdict(list)\ndd[\"a\"].append(1)                   # cleaner; factory called only on miss\n",[19,303,304,314,334,338,351],{"__ignoreMap":61},[65,305,306,309,311],{"class":67,"line":68},[65,307,308],{"class":75},"d ",[65,310,98],{"class":71},[65,312,313],{"class":75}," {}\n",[65,315,316,319,322,325,328,331],{"class":67,"line":85},[65,317,318],{"class":75},"d.setdefault(",[65,320,321],{"class":104},"\"a\"",[65,323,324],{"class":75},", []).append(",[65,326,327],{"class":155},"1",[65,329,330],{"class":75},")     ",[65,332,333],{"class":117},"# works, but builds a [] every call\n",[65,335,336],{"class":67,"line":92},[65,337,89],{"emptyLinePlaceholder":88},[65,339,340,343,345,347,349],{"class":67,"line":111},[65,341,342],{"class":75},"dd ",[65,344,98],{"class":71},[65,346,213],{"class":75},[65,348,21],{"class":155},[65,350,108],{"class":75},[65,352,353,356,358,361,363,366],{"class":67,"line":121},[65,354,355],{"class":75},"dd[",[65,357,321],{"class":104},[65,359,360],{"class":75},"].append(",[65,362,327],{"class":155},[65,364,365],{"class":75},")                   ",[65,367,368],{"class":117},"# cleaner; factory called only on miss\n",[15,370,371,372,376,377,376,380,383,384,387],{},"One caveat: simply ",[373,374,375],"em",{},"reading"," ",[19,378,379],{},"dd[missing]",[180,381,382],{},"creates"," the key in a defaultdict. Use\n",[19,385,386],{},"dd.get(k)"," if you want to look without inserting.",[10,389,391],{"id":390},"deque-fast-operations-at-both-ends","deque — fast operations at both ends",[15,393,394,395,398,399,402,403,41,406,409],{},"A ",[19,396,397],{},"deque"," (double-ended queue) gives O(1) appends and pops at ",[180,400,401],{},"both"," ends, unlike a list\nwhere ",[19,404,405],{},"insert(0, x)",[19,407,408],{},"pop(0)"," are O(n):",[56,411,413],{"className":58,"code":412,"language":60,"meta":61,"style":61},"from collections import deque\n\nq = deque([1, 2, 3])\nq.appendleft(0)    # deque([0, 1, 2, 3])\nq.popleft()        # 0  — O(1), great for queues\nq.append(4)        # deque([1, 2, 3, 4])\n\ndq = deque(maxlen=3)    # bounded — old items drop off automatically\n",[19,414,415,426,430,454,467,475,488,492],{"__ignoreMap":61},[65,416,417,419,421,423],{"class":67,"line":68},[65,418,72],{"class":71},[65,420,76],{"class":75},[65,422,79],{"class":71},[65,424,425],{"class":75}," deque\n",[65,427,428],{"class":67,"line":85},[65,429,89],{"emptyLinePlaceholder":88},[65,431,432,435,437,440,442,444,446,448,451],{"class":67,"line":92},[65,433,434],{"class":75},"q ",[65,436,98],{"class":71},[65,438,439],{"class":75}," deque([",[65,441,327],{"class":155},[65,443,22],{"class":75},[65,445,156],{"class":155},[65,447,22],{"class":75},[65,449,450],{"class":155},"3",[65,452,453],{"class":75},"])\n",[65,455,456,459,461,464],{"class":67,"line":111},[65,457,458],{"class":75},"q.appendleft(",[65,460,255],{"class":155},[65,462,463],{"class":75},")    ",[65,465,466],{"class":117},"# deque([0, 1, 2, 3])\n",[65,468,469,472],{"class":67,"line":121},[65,470,471],{"class":75},"q.popleft()        ",[65,473,474],{"class":117},"# 0  — O(1), great for queues\n",[65,476,477,480,483,485],{"class":67,"line":136},[65,478,479],{"class":75},"q.append(",[65,481,482],{"class":155},"4",[65,484,159],{"class":75},[65,486,487],{"class":117},"# deque([1, 2, 3, 4])\n",[65,489,490],{"class":67,"line":149},[65,491,89],{"emptyLinePlaceholder":88},[65,493,495,498,500,503,507,509,511,513],{"class":67,"line":494},8,[65,496,497],{"class":75},"dq ",[65,499,98],{"class":71},[65,501,502],{"class":75}," deque(",[65,504,506],{"class":505},"s4XuR","maxlen",[65,508,98],{"class":71},[65,510,450],{"class":155},[65,512,463],{"class":75},[65,514,515],{"class":117},"# bounded — old items drop off automatically\n",[15,517,518,519,521,522,524],{},"Use ",[19,520,397],{}," for queues, BFS frontiers, and sliding windows (",[19,523,506],{},").",[10,526,528],{"id":527},"ordereddict-still-useful","OrderedDict — still useful?",[15,530,531,532,535,536,539],{},"Since Python 3.7, regular dicts ",[180,533,534],{},"guarantee insertion order",", so you rarely need\n",[19,537,538],{},"OrderedDict"," just for ordering. It still offers a couple of unique features:",[56,541,543],{"className":58,"code":542,"language":60,"meta":61,"style":61},"from collections import OrderedDict\n\nod = OrderedDict()\nod.move_to_end(\"key\")        # reorder explicitly\nod.popitem(last=False)       # pop from the front (LRU-cache pattern)\n\nOrderedDict(a=1) == OrderedDict(a=1)   # equality is order-sensitive\n",[19,544,545,556,560,570,583,602,606],{"__ignoreMap":61},[65,546,547,549,551,553],{"class":67,"line":68},[65,548,72],{"class":71},[65,550,76],{"class":75},[65,552,79],{"class":71},[65,554,555],{"class":75}," OrderedDict\n",[65,557,558],{"class":67,"line":85},[65,559,89],{"emptyLinePlaceholder":88},[65,561,562,565,567],{"class":67,"line":92},[65,563,564],{"class":75},"od ",[65,566,98],{"class":71},[65,568,569],{"class":75}," OrderedDict()\n",[65,571,572,575,578,580],{"class":67,"line":111},[65,573,574],{"class":75},"od.move_to_end(",[65,576,577],{"class":104},"\"key\"",[65,579,159],{"class":75},[65,581,582],{"class":117},"# reorder explicitly\n",[65,584,585,588,591,593,596,599],{"class":67,"line":121},[65,586,587],{"class":75},"od.popitem(",[65,589,590],{"class":505},"last",[65,592,98],{"class":71},[65,594,595],{"class":155},"False",[65,597,598],{"class":75},")       ",[65,600,601],{"class":117},"# pop from the front (LRU-cache pattern)\n",[65,603,604],{"class":67,"line":136},[65,605,89],{"emptyLinePlaceholder":88},[65,607,608,611,614,616,618,621,624,627,629,631,633,636],{"class":67,"line":149},[65,609,610],{"class":75},"OrderedDict(",[65,612,613],{"class":505},"a",[65,615,98],{"class":71},[65,617,327],{"class":155},[65,619,620],{"class":75},") ",[65,622,623],{"class":71},"==",[65,625,626],{"class":75}," OrderedDict(",[65,628,613],{"class":505},[65,630,98],{"class":71},[65,632,327],{"class":155},[65,634,635],{"class":75},")   ",[65,637,638],{"class":117},"# equality is order-sensitive\n",[15,640,641],{},"Plain dict equality ignores order; OrderedDict equality respects it.",[10,643,645],{"id":644},"chainmap-layered-lookups","ChainMap — layered lookups",[15,647,648,651],{},[19,649,650],{},"ChainMap"," groups several dicts into one view, searching them in order — perfect for layered\nconfiguration (CLI args over env over defaults):",[56,653,655],{"className":58,"code":654,"language":60,"meta":61,"style":61},"from collections import ChainMap\n\ndefaults = {\"color\": \"red\", \"size\": \"M\"}\noverrides = {\"color\": \"blue\"}\nconfig = ChainMap(overrides, defaults)\nconfig[\"color\"]    # 'blue'  (found in overrides first)\nconfig[\"size\"]     # 'M'     (falls through to defaults)\n",[19,656,657,668,672,704,722,732,745],{"__ignoreMap":61},[65,658,659,661,663,665],{"class":67,"line":68},[65,660,72],{"class":71},[65,662,76],{"class":75},[65,664,79],{"class":71},[65,666,667],{"class":75}," ChainMap\n",[65,669,670],{"class":67,"line":85},[65,671,89],{"emptyLinePlaceholder":88},[65,673,674,677,679,682,685,688,691,693,696,698,701],{"class":67,"line":92},[65,675,676],{"class":75},"defaults ",[65,678,98],{"class":71},[65,680,681],{"class":75}," {",[65,683,684],{"class":104},"\"color\"",[65,686,687],{"class":75},": ",[65,689,690],{"class":104},"\"red\"",[65,692,22],{"class":75},[65,694,695],{"class":104},"\"size\"",[65,697,687],{"class":75},[65,699,700],{"class":104},"\"M\"",[65,702,703],{"class":75},"}\n",[65,705,706,709,711,713,715,717,720],{"class":67,"line":111},[65,707,708],{"class":75},"overrides ",[65,710,98],{"class":71},[65,712,681],{"class":75},[65,714,684],{"class":104},[65,716,687],{"class":75},[65,718,719],{"class":104},"\"blue\"",[65,721,703],{"class":75},[65,723,724,727,729],{"class":67,"line":121},[65,725,726],{"class":75},"config ",[65,728,98],{"class":71},[65,730,731],{"class":75}," ChainMap(overrides, defaults)\n",[65,733,734,737,739,742],{"class":67,"line":136},[65,735,736],{"class":75},"config[",[65,738,684],{"class":104},[65,740,741],{"class":75},"]    ",[65,743,744],{"class":117},"# 'blue'  (found in overrides first)\n",[65,746,747,749,751,754],{"class":67,"line":149},[65,748,736],{"class":75},[65,750,695],{"class":104},[65,752,753],{"class":75},"]     ",[65,755,756],{"class":117},"# 'M'     (falls through to defaults)\n",[15,758,759,760,763],{},"Writes go to the ",[180,761,762],{},"first"," mapping only; the underlying dicts stay separate.",[10,765,767],{"id":766},"recap","Recap",[15,769,770,772,773,775,776,778,779,782,783,785,786,788,789,791,792,794],{},[19,771,36],{}," gives you sharper tools than the built-ins for common jobs: ",[180,774,40],{}," for\ntallying (missing keys are ",[19,777,255],{},", plus ",[19,780,781],{},"most_common","), ",[180,784,44],{}," for grouping without\nexistence checks (factory runs only on a miss — but reads create keys), ",[180,787,397],{}," for O(1)\noperations at both ends and bounded sliding windows, ",[180,790,538],{}," for the few\norder-sensitive operations dicts lack, and ",[180,793,650],{}," for layered lookups across multiple\ndictionaries.",[796,797,798],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":61,"searchDepth":85,"depth":85,"links":800},[801,802,803,804,805,806,807,808],{"id":12,"depth":85,"text":13},{"id":48,"depth":85,"text":49},{"id":172,"depth":85,"text":173},{"id":287,"depth":85,"text":288},{"id":390,"depth":85,"text":391},{"id":527,"depth":85,"text":528},{"id":644,"depth":85,"text":645},{"id":766,"depth":85,"text":767},"The specialised containers in Python's collections module — Counter for tallying, defaultdict for grouping, deque for fast ends, ChainMap, and where OrderedDict still fits.","medium","md","Python",{},"\u002Fblog\u002Fpython-collections-module-explained","\u002Fpython\u002Fdata-structures\u002Fcollections-module",{"title":5,"description":809},"blog\u002Fpython-collections-module-explained","The collections Module","Data Structures","data-structures","2026-06-19","Bzu9i_5NCq3vMOfaDcGT8ME6jHhedBCFY-FUxKEkidA",1782244094137]