In this article
  1. Before you start
  2. Why interviewers ask this
  3. defaultdict from first principles
  4. Worked example
  5. Trade-offs and edge cases
  6. How to answer it in an interview
  7. Common mistakes
  8. Follow-up questions
  9. Sources

Before you start

Why interviewers ask this

The question separates candidates who write the same four-line counting loop every time from candidates who know the tool that removes it, and it tests whether they know the tool's one sharp edge. Interviewers watch for it in problems that group, count or build a graph, such as topological sort, where the adjacency list is the data structure everything else rests on.

In a coding round the question rarely arrives as "what is a defaultdict". It arrives as a task, "group these words by their letters", and the defaultdict is how a fluent candidate solves it in a few lines while saying why it is safe.

A weak answer uses it without saying what it does. A strong answer names the factory, says what a read does to the dictionary, and knows two alternatives.

defaultdict from first principles

In plain words

defaultdict
A defaultdict is a dictionary subclass in Python that supplies a value for a missing key by calling a stored function, and that is otherwise a normal dictionary. It lives in the standard library module collections.
Default factory
A default factory is the function, passed as the first argument to the defaultdict constructor, that is called with no arguments to build a value for a missing key. Typical choices are int, list and set.
KeyError
A KeyError is the error Python raises when a dictionary is asked for a key it does not hold. It is what a defaultdict avoids for missing keys.

Picture a filing cabinet. In a plain cabinet, asking for a folder that is not there gets you an error from the clerk. In a defaultdict cabinet, the clerk creates an empty folder of the kind you chose, files it under that name, and hands it to you.

The analogy breaks in one place. A real clerk would let you ask whether a folder exists without making one, and a defaultdict does the same only if you ask the right way. Asking for a missing name with square brackets always files a new folder.

In one precise sentence, a defaultdict is a dictionary whose missing-key lookup calls a factory and stores what it returns.

Step 1: A plain dictionary raises KeyError on a missing key

A plain dictionary refuses a lookup of a key it does not hold. The Python tutorial says that extracting a value for a non-existent key by subscripting raises a KeyError, and that get() returns None, or a value you give, instead.

That is why counting by hand needs a guard. A counting loop on the word mississippi, written with get, reads counts[ch] = counts.get(ch, 0) + 1. It works, and it gives the same counts as the other methods below: i 4, m 1, p 2, s 41. The cost is that every update spells out the default, and forgetting it in one place is a bug.

The same pattern for grouping needs an if key not in d check before each append, which is the clutter a defaultdict removes. The input is a key that is not there yet. The output is an error, or a manual default.

Step 2: A missing key runs the factory and the result is stored

A defaultdict handles the missing key itself. The collections documentation says that when the default factory is not None, it is called without arguments to provide a default value for the key, this value is inserted in the dictionary for the key, and it is returned.

The word inserted is the point. A defaultdict created with list and read once for the key x holds one key afterwards, where a get for the same key leaves it empty: the length was 0 after get and in, and 1 after the bracket read1. The read is a write.

Counting then collapses to two lines: d = defaultdict(int) and d[ch] += 1. The factory int() returns 0 for a new letter, the increment makes it 1, and the next time that letter is found. The input is a missing key. The output is a stored default the code can use at once.

Step 3: Pick the factory by the job

The default factory fixes what each new key starts as, so the choice is really the choice of job. With int, each new key starts at 0, which suits counting. With list, each starts as an empty list, which suits grouping items under a key. With set, each starts as an empty set, which suits collecting unique items per key. A lambda suits constants or a nested structure.

The nested form defaultdict(lambda: defaultdict(int)) gave per-user item counts in the run: user u1 held pen 2 and ink 1, and u2 held pen 11. A lambda: "unknown" returned "unknown" and stored it, leaving one key behind1.

One trap is a factory that returns the same object every time. With lambda: shared, where shared is one list, keys a and b ended up holding the same list, [1, 2] each, and a is b was true1. With list, each key got its own list. The factory must build a fresh value.

Step 4: Reads add keys, so guard the ones that should not

Only the bracket read adds a key. The collections documentation says the missing-key hook is not called for any operation besides subscripting, so get() returns None as in a normal dictionary, and the in test does not add anything either.

The cost is memory and surprise. In the run, 1,000 bracket reads of distinct missing keys left 1,000 keys in a defaultdict, while 1,000 get reads left a plain dictionary at zero1. A typo in a key name does not raise an error; it creates a new entry, and a later len or loop over the dictionary then includes it.

After the building phase, dict(d) makes a plain copy, and a missing key raises a KeyError again1. Build with the default, then freeze if lookups should be strict about spelling.

A defaultdict builds and stores a value when a key is missingFlow of a lookup: d[key] on a missing key raises KeyError in a plain dict, but a defaultdict calls its default factory, stores the new value under the key, and later reads find it.Read d[key]key is missingplain dictraises KeyErrordefaultdictcalls factory()Store valueunder the keydefaultdict(int)factory set at buildNext d[key]key is foundDiagram: Interview Clozo, CC BY 4.0 ·interview.clozo.ai/blog/defaultdict.html

A defaultdict turns a missing-key error into a stored default, so the next read finds it.CC BY 4.0 — source: What is defaultdict in Python?
Step-by-step description
  1. A missing key raises KeyError.
  2. The defaultdict calls its factory and stores the value instead.
  3. The factory is fixed when the defaultdict is built.
  4. The key now exists; get() and in never call the factory.
  • A plain dict raises KeyError when the key is missing.
  • A defaultdict calls the default factory and stores the result under the key.
  • The factory is chosen by the job: int to count, list to group, set to deduplicate.

Knowing it is half the job. Saying it is the other half.

Interview Clozo is an AI interview copilot for real interview rounds: it hears the question and puts a strong answer on your screen, and you say it in your own words. See the features or how it works.

Start Free Trial →

Worked example

Grouping nine words by their sorted letters with a list factory produces four groups in five lines of code, and no key was ever initialized by hand.
Sorted letters (key)Words stored under itGroup size
actcat, act, tac3
eilnstlisten, silent, enlist3
eggloogoogle, gogole2
dgodog1

The code is a loop over the words with groups["".join(sorted(w))].append(w) on a defaultdict(list). The group sizes, largest first, were 3, 3, 2 and 11. The first time each key appears, the factory makes an empty list, the word is appended to it, and later words with the same letters find it.

Counting with a defaultdict, with get and with Counter all give identical results, so the choice is about clarity and not correctness.
MethodResult for mississippi
defaultdict(int)i 4, m 1, p 2, s 4
dict with geti 4, m 1, p 2, s 4
Counterthe two most common are i and s, with 4 each

Those three rows come from the same run1. A Counter, as the documentation describes it, is a dictionary subclass for counting hashable objects that returns a zero count for a missing item without raising a KeyError. When counting is the whole job, it is the shortest tool.

A set factory shows deduplication. The five edges (a,b), (a,c), (a,b), (b,c) and (c,a) fed into a defaultdict(set) produced the neighbors a to b and c, b to c, and c to a: four unique edges from five rows1. A graph problem such as vertical order traversal groups nodes by column the same way, with a list per column.

Reading a missing key with brackets adds the key to a defaultdictBar chart of how many keys each container holds after 1,000 reads of distinct missing keys: 1,000 for a defaultdict read with brackets and 0 for a plain dict read with get.Keys stored afterwards (keys)02505007501,000defaultdict []dict get()Container and read style1,0000Diagram: Interview Clozo, CC BY 4.0 ·interview.clozo.ai/blog/defaultdict.html
Bracket reads grow a defaultdict, while get reads leave a dictionary unchanged.CC BY 4.0 — source: What is defaultdict in Python?
Text description of the diagram
  • After 1,000 bracket reads of distinct missing keys a defaultdict holds 1,000 keys.
  • After 1,000 get reads of distinct missing keys a plain dict holds none.
The default factory you pass decides what the dictionary is forTable of four default factories, int, list, set and a lambda, with the empty value each creates and the job it fits: counting, grouping, deduplicating and nesting.FactoryStarts asTypical jobint0Count itemslist[ ]Group items bykeysetempty setUnique items perkeylambdaany valueNested orconstantDiagram: Interview Clozo, CC BY 4.0 ·interview.clozo.ai/blog/defaultdict.html
Pick the factory from the job: int counts, list groups, set deduplicates.CC BY 4.0 — source: What is defaultdict in Python?
Text description of the diagram
  • The int factory starts a count at zero.
  • The list factory starts an empty list for each new key.

Trade-offs and edge cases

A defaultdict trades explicitness for brevity, and the trade is good only while the reader knows which dictionary is a defaultdict.

  • Versus setdefault. d.setdefault(k, make()) builds its default argument on every call, even when the key exists. In the run, five reads over two keys called the builder five times with setdefault and twice with a defaultdict factory1. For cheap defaults the cost is too small to notice; for expensive ones it is not.
  • Versus Counter. A Counter is purpose-built for counts and adds most_common; a defaultdict is more general. Use the Counter when the answer is only counts.
  • Silent typos. Misspelling a key creates a new entry instead of an error, so keep the factory-built dictionary local to the function that builds it.
  • Pickling. A defaultdict with a lambda factory raised a PicklingError when serialized, and one with int worked1. When data crosses a process boundary, as in parallel processing, use a named factory such as int.
  • Not a free lunch. The first read of a key allocates a value, so a million distinct misses allocate a million values.

How to answer it in an interview

Interviewer asks

What is a defaultdict, and when would you use one?

A strong answer

A defaultdict is a dictionary that takes a factory function. When I read a key that is missing, it calls the factory with no arguments, stores the result under that key and returns it. So with int I count with d[x] += 1, with list I group with d[k].append(v), and with set I deduplicate. The catch is that a bracket read inserts the key, so a typo creates an entry and a membership check should use in. When the work is only counting, I would use Counter, and once the build is done I can convert to a plain dict.

The spoken structure is: define it with the factory, give the three uses, state the insertion catch, then name the alternative.

Common mistakes

  • "A defaultdict only returns a default and leaves the dictionary alone" (wrong). The documentation says the value is inserted for the key (right: say it stores the default).
  • "d.get(k) on a defaultdict gives the default" (wrong). get returns None for a missing key, exactly as in a plain dictionary (right: say bracket reads call the factory, get does not).
  • "defaultdict([]) makes empty lists" (wrong). The first argument must be callable, and the run raised "first argument must be callable or None" (right: pass list, not [])1.
  • "lambda: shared_list is the same as list" (wrong). It hands every key the same object (right: build a fresh value each call).
  • "Checking k in d creates the key" (wrong). The in test leaves the dictionary unchanged, as the length stayed 0 (right: use in or get to look without adding).

Follow-up questions

What is the difference between defaultdict and Counter?

A Counter is a dictionary subclass specialised for counting, returning zero for a missing item and offering helpers such as most_common. A defaultdict is general-purpose: its factory can build counts, lists, sets or nested dictionaries, at the price of one more line of setup.

What is the difference between defaultdict and setdefault?

setdefault is a dictionary method that returns the stored value or inserts a given default, while a defaultdict is a class that does the insertion on every bracket read of a missing key. The method evaluates its default on every call, so an expensive default is built even when it is not needed.

Does reading a key with get or in add it to a defaultdict?

No. Only the bracket read calls the factory; get and in leave the dictionary unchanged, which is how you look a key up without creating it.

Can the default factory take arguments?

No. The documentation says it is called without arguments, so a factory that needs a parameter has to be wrapped in a lambda or a small function that supplies it.

How do you stop a defaultdict from adding keys by accident?

Build with it, then convert with dict(d), after which a missing key raises a KeyError again. Keep the defaultdict inside the function that builds it, so its insert-on-read behavior does not leak into code that expects a plain dictionary. Related habits come up in a monotonic stack problem and in a k closest points problem, where the right container choice shortens the solution.

Key takeaways
  • A defaultdict calls its factory for a missing key, stores the result under that key and returns it.
  • Use int to count, list to group and set to deduplicate; use Counter when the job is only counting.
  • A bracket read inserts the key, while get and in do not, so typos create entries.
  • The factory takes no arguments and must build a fresh value each call.
  • Convert to a plain dict after building if lookups should raise errors for missing keys.

Sources

  1. collections: container datatypes, defaultdict objects (Python 3.15.1 documentation) — Python Software Foundation (page footer: last updated 2026-10-24), 24 Oct 2026
  2. collections: Counter objects (Python 3.15.1 documentation) — Python Software Foundation (read 2026-10-24), 24 Oct 2026
  3. Data Structures, Dictionaries (Python 3.15.1 tutorial) — Python Software Foundation (read 2026-10-24), 24 Oct 2026