Python Empty List: Creation, Checks, and Common Pitfalls
python empty list: Learn how to create empty lists in Python, check emptiness with truthiness, and avoid pitfalls like mutable default arguments and None confusion.
In Python, an empty list is a list with zero elements, and it appears constantly in real code: as an accumulator, a default collection, or a result that has nothing to return. The syntax [] creates one, and so does list(), but the two are not identical in every way. The phrase python empty list usually points at three practical questions: how to create one, how to check whether a list is empty, and how to avoid the bugs that empty lists expose.
Creating an Empty List: [] vs list()
The literal [] is the most common way to create an empty list:
items = []
Calling the built-in list() with no arguments also produces an empty list:
items = list()
Both produce a list object with length zero. The difference is mainly style and context. The literal [] is faster because it is a bytecode constant rather than a function call, and it is the conventional choice in most codebases. list() is useful when the code already uses a factory-style pattern, or when a function parameter expects a callable that produces a list.
There is no meaningful difference in the resulting object. Both are mutable, both support the same methods, and both compare equal to each other:
[] == list() # True
Checking Whether a List Is Empty
The idiomatic way to check whether a list is empty is to rely on truthiness:
if not items: print("items is empty")
An empty list is falsy in Python, so not items evaluates to True when items has no elements. This is the standard check used across Python codebases.
The alternative is to compare the length:
if len(items) == 0: print("items is empty")
Both work, but the truthiness check is more readable and slightly faster because it does not call len(). The len() version is not wrong; it is simply more verbose. Some developers prefer it because it makes the condition explicit, especially for readers who are not familiar with Python truthiness rules.
Comparing directly to an empty list also works:
if items == []: print("items is empty")
This is discouraged. It creates a new list object for every comparison, and it is less idiomatic than the truthiness check. It also fails to handle the case where items is None, which the truthiness check handles naturally in many code paths.
Why Truthiness Is the Standard Check
Python defines truthiness for every object. A list is falsy when it is empty and truthy otherwise. That single rule makes if items: and if not items: reliable for lists without any explicit comparison.
def process(items): if not items: return [] return [item.upper() for item in items]
The truthiness check covers the empty case and returns early. It does not raise an error when items is an empty list, and it reads naturally: "if there are no items, return an empty list."
The same rule applies when a function returns a list that may be empty. A caller can check the result directly:
result = find_matches(query) if not result: print("no matches found")
This pattern is consistent across the standard library and most third-party libraries, so it is the check that other developers expect to see.
Empty List vs None
A common source of bugs is confusing an empty list with None. They are different values with different truthiness: None is falsy, and an empty list is also falsy. That means if not result: cannot distinguish between "no result" and "an empty result."
def find_matches(query): if not query: return None return []
In this example, find_matches("") returns None, while find_matches("x") returns an empty list when there are no matches. A caller that checks if not result: treats both the same way, which may hide a real difference between "invalid input" and "valid input with no matches."
The fix is to decide what the function contract is. If an empty list means "no matches," return [] and never return None. If None means "invalid input," the caller must check for None explicitly before using the result:
result = find_matches(query) if result is None: print("invalid query") elif not result: print("no matches")
Keeping the two distinct avoids silent bugs where an empty list is mistaken for a missing value.
The Mutable Default Argument Trap
An empty list used as a default argument is a classic Python pitfall. Default arguments are evaluated once at function definition time, so the same list object is reused for every call that does not pass an explicit value.
def add_item(item, items=[]): items.append(item) return items
Every call to add_item without an items argument appends to the same list. The second call returns a list that already contains the first item. This is almost never what the developer intended.
The standard fix is to use None as the default and create a fresh list inside the function:
def add_item(item, items=None): if items is None: items = [] items.append(item) return items
Now each call without an explicit items argument gets its own empty list. This is the canonical pattern for avoiding shared mutable state in default arguments.
Performance and Memory Considerations
The truthiness check if not items: is the fastest way to test for emptiness because it inspects the object's truthiness directly. The len() call adds a small function-call overhead, and the comparison items == [] allocates a new list on every evaluation. In practice, the difference matters only in hot loops where the check runs millions of times.
Creating an empty list is cheap. The literal [] is a constant operation, while list() involves a function call. Neither has a meaningful memory footprint for a single empty list. The memory cost appears when empty lists are created in large numbers, such as inside a loop that builds many small lists:
rows = [[] for _ in range(10000)]
This creates ten thousand distinct empty lists. If the code only needs to know whether a row exists, a list of booleans or a single list with sentinel values would use less memory. For most applications, the overhead is negligible, but it is worth keeping in mind when building large collections of small lists.
Empty Lists in Loops and Comprehensions
Empty lists interact with loops and comprehensions in ways that are easy to overlook. Iterating over an empty list simply does nothing, which is usually the desired behavior:
for item in items: process(item)
A list comprehension over an empty list produces another empty list:
doubled = [x * 2 for x in items] # [] when items is empty
This is convenient because it means the result type is preserved even when the input is empty. The same is not true for generator expressions, which produce a generator object that must be consumed. If a function returns a list and the caller relies on the result being a list, a comprehension over an empty input still returns a list, so no special handling is required.
One edge case is using a list as an accumulator in a loop that may never execute:
accumulator = [] for item in items: accumulator.append(item.upper())
When items is empty, accumulator remains an empty list. That is the correct result, and it avoids the need for a separate branch that returns None or raises an error.
Another edge case is checking emptiness before consuming a list with pop() or remove(). Calling pop() on an empty list raises IndexError, and remove() raises ValueError when the value is absent. Guarding with a truthiness check prevents those exceptions:
if items: last = items.pop()
This is a practical pattern for stack-like usage where the list may be empty at the end of processing.