Check if a Dictionary Is Empty in Python (and Why None Passes)

To check if a dictionary is empty in Python, put it straight into an if:

if not my_dict:
    print("the dictionary is empty")

An empty dict is falsy, so nothing else is needed. The catch is that None is falsy too, which means that test quietly passes for a dictionary that was never built at all.

Below are the four ways people write this check, which one to prefer, and the three cases where the obvious answer is wrong. Output comes from a run on Python 3.12.5.

Diagram showing that the Python not operator returns True for an empty dictionary and also for None, but False for a dictionary holding falsy values
not d answers True for an empty dict and for None alike.

Four ways to check if a dictionary is empty

All four agree on a plain dictionary:

settings = {}

print("four ways to ask the same question:")
print("  not settings      ->", not settings)
print("  len(settings) == 0 ->", len(settings) == 0)
print("  settings == {}     ->", settings == {})
print("  not bool(settings) ->", not bool(settings))
print()

settings = {"theme": "dark"}
print("and once it has something in it:")
print("  not settings      ->", not settings)
print("  len(settings) == 0 ->", len(settings) == 0)
print("  settings == {}     ->", settings == {})
print("  not bool(settings) ->", not bool(settings))

Output:

four ways to ask the same question:
  not settings      -> True
  len(settings) == 0 -> True
  settings == {}     -> True
  not bool(settings) -> True

and once it has something in it:
  not settings      -> False
  len(settings) == 0 -> False
  settings == {}     -> False
  not bool(settings) -> False
Command Prompt comparing the not operator len bool and equality checks on an empty Python dictionary
The four checks agree on a plain dictionary, empty or not.
CheckReads asVerdict
if not d:“if there is nothing in d”Preferred. Shortest and idiomatic.
if len(d) == 0:“if d has zero keys”Fine, and explicit. Useful in code reviewed by beginners.
if d == {}:“if d equals an empty dict”Works, but builds a throwaway dict to compare against.
if not bool(d):“if d is falsy”Redundant. if already calls bool.

Why not my_dict is the Pythonic choice

Python treats empty containers as false, so the conversion is automatic:

config = {}

# the idiomatic form
if not config:
    print("no config loaded")

# the same test, spelled out
if len(config) == 0:
    print("still no config loaded")

# an empty dict is falsy, so this works too
if config:
    print("this line never runs")
else:
    print("empty dicts are falsy")

print()
print("bool of an empty dict  ->", bool({}))
print("bool of a full dict    ->", bool({"a": 1}))
print()
print("that falsiness is why `if not config:` is enough on its own.")

Output:

no config loaded
still no config loaded
empty dicts are falsy

bool of an empty dict  -> False
bool of a full dict    -> True

that falsiness is why `if not config:` is enough on its own.

if d: already runs bool(d) behind the scenes, which is why wrapping it in bool() adds nothing. The same rule covers empty lists, tuples, sets and strings, so the habit carries across.

Style guides favour the short form for exactly this reason. It behaves the same for dictionaries you have just looked a key up in and for ones built from scratch.

Why the check passes when the dictionary is None

This is where the short check goes wrong:

empty = {}
missing = None

print("both of these pass the same test:")
print("  not empty   ->", not empty)
print("  not missing ->", not missing, "  <- None is falsy too")
print()
print("so `if not d:` cannot tell 'the dict is empty'")
print("apart from 'the dict was never built'.")
print()

try:
    len(missing)
except TypeError as err:
    print("len(missing) -> TypeError:", err)

print()
print("these do tell them apart:")
print("  missing == {}                 ->", missing == {})
print("  missing is None               ->", missing is None)
print("  isinstance(missing, dict)     ->", isinstance(missing, dict))
print()
print("if a function can return None, check for that first:")
for value in ({}, None, {"a": 1}):
    if value is None:
        print("  None       -> never loaded")
    elif not value:
        print("  {}         -> loaded but empty")
    else:
        print(f"  {value} -> has data")

Output:

both of these pass the same test:
  not empty   -> True
  not missing -> True   <- None is falsy too

so `if not d:` cannot tell 'the dict is empty'
apart from 'the dict was never built'.

len(missing) -> TypeError: object of type 'NoneType' has no len()

these do tell them apart:
  missing == {}                 -> False
  missing is None               -> True
  isinstance(missing, dict)     -> False

if a function can return None, check for that first:
  {}         -> loaded but empty
  None       -> never loaded
  {'a': 1} -> has data
Command Prompt showing that the Python not operator returns True for None as well as for an empty dictionary
not None is True as well, so the short check cannot tell the two apart.

An empty dictionary and a missing dictionary mean different things. One says the data loaded and there was none; the other says the data never arrived. If a function can return None, test for that first.

len(None) raises TypeError: object of type 'NoneType' has no len(), which at least fails loudly. d == {} and isinstance(d, dict) both return False for None, so either one separates the cases cleanly.

A dictionary full of falsy values is not empty

Emptiness is about keys, not about what those keys hold:

record = {"name": None, "count": 0, "note": ""}

print("record        ->", record)
print("not record    ->", not record, "  it is NOT empty")
print("len(record)   ->", len(record))
print()
print("every VALUE is falsy, but the dict still has three keys.")
print("emptiness is about keys, not about what the keys hold.")
print()

print("to ask whether every value is falsy, say so explicitly:")
print("  all(not v for v in record.values()) ->", all(not v for v in record.values()))
print()
print("and to drop the empty entries:")
print("  {k: v for k, v in record.items() if v} ->", {k: v for k, v in record.items() if v})

Output:

record        -> {'name': None, 'count': 0, 'note': ''}
not record    -> False   it is NOT empty
len(record)   -> 3

every VALUE is falsy, but the dict still has three keys.
emptiness is about keys, not about what the keys hold.

to ask whether every value is falsy, say so explicitly:
  all(not v for v in record.values()) -> True

and to drop the empty entries:
  {k: v for k, v in record.items() if v} -> {}

Three keys means three keys, even when every value is None, 0 or an empty string. If what you actually care about is whether anything useful is stored, say that with all(not v for v in d.values()).

Stripping the blank entries is a one-line comprehension, which is often what people were reaching for. There is more on walking a dictionary in iterating through a dictionary.

Checking a defaultdict can fill it

This one bites quietly:

from collections import defaultdict

counts = defaultdict(list)

print("a fresh defaultdict is empty:")
print("  not counts ->", not counts, "| len:", len(counts))
print()

print("now inspect one key to see what is there:")
print("  counts['users'] ->", counts["users"])
print()

print("and check again:")
print("  not counts ->", not counts, "| len:", len(counts))
print("  the lookup CREATED the key, so the check changed the answer")
print()

print("use .get() or `in` to look without creating:")
other = defaultdict(list)
print("  other.get('users')   ->", other.get("users"))
print("  'users' in other     ->", "users" in other)
print("  not other            ->", not other, "| len:", len(other))

Output:

a fresh defaultdict is empty:
  not counts -> True | len: 0

now inspect one key to see what is there:
  counts['users'] -> []

and check again:
  not counts -> False | len: 1
  the lookup CREATED the key, so the check changed the answer

use .get() or `in` to look without creating:
  other.get('users')   -> None
  'users' in other     -> False
  not other            -> True | len: 0
Command Prompt showing that reading a key from a Python defaultdict creates that key and makes the dictionary non-empty
Looking a key up in a defaultdict creates it, so the dictionary is no longer empty.

A defaultdict manufactures a value the moment you read a missing key. Printing one to see what is in there therefore adds a key, and the emptiness check that follows returns the wrong answer.

.get() and the in operator both look without writing, so use those while inspecting. Counting keys afterwards is covered in counting the keys in a dictionary.

Is not d faster than len(d) == 0?

Not in a way that matters:

import timeit

setup_empty = "d = {}"
setup_big = "d = {i: i for i in range(1000000)}"

print("2,000,000 checks on an EMPTY dict:")
for label, snippet in [("not d       ", "not d"),
                       ("bool(d)     ", "bool(d)"),
                       ("len(d) == 0 ", "len(d) == 0"),
                       ("d == {}     ", "d == {}")]:
    print(f"  {label} {timeit.timeit(snippet, setup_empty, number=2000000):.3f}s")

print()
print("2,000,000 checks on a dict holding 1,000,000 keys:")
print(f"  len(d) == 0  {timeit.timeit('len(d) == 0', setup_big, number=2000000):.3f}s")
print()
print("len() reads a stored count, so the size of the dict makes no difference.")
print("All four are constant time; pick the one that reads best.")

Output:

2,000,000 checks on an EMPTY dict:
  not d        0.034s
  bool(d)      0.049s
  len(d) == 0  0.064s
  d == {}      0.066s

2,000,000 checks on a dict holding 1,000,000 keys:
  len(d) == 0  0.129s

len() reads a stored count, so the size of the dict makes no difference.
All four are constant time; pick the one that reads best.