Advanced Python Idioms and Optimization
Creation Performance and Bytecode
Literals vs Constructors
In performance-critical Python, the choice between [] and list() or {} and dict() is not merely stylistic. Literals consistently outperform their constructor counterparts due to fundamental differences in the underlying bytecode generated by the interpreter. This distinction is crucial for writing high-throughput code where micro-optimizations compound.
The performance penalty of constructors stems from two main sources: the overhead of a name lookup and the cost of a function call. A literal is a direct instruction to the interpreter to build a data structure. A constructor call, however, is a multi-step process. First, the interpreter must find the list or dict name in the (a relatively slow dictionary lookup). Then, it must execute a function call, which involves pushing onto the call stack and other overhead.
Disassembling the Operations
The dis module provides a clear view of this difference. Let's inspect the opcodes for creating an empty list.
import dis
def create_list_literal():
return []
def create_list_constructor():
return list()
print("--- List Literal ---")
dis.dis(create_list_literal)
print("\n--- List Constructor ---")
dis.dis(create_list_constructor)
The output reveals the literal version uses a single BUILD_LIST opcode. The constructor, however, requires LOAD_GLOBAL to find the list built-in, followed by CALL_FUNCTION to invoke it.
List Literal: 2 0 BUILD_LIST 0 2 RETURN_VALUE
List Constructor: 2 0 LOAD_GLOBAL 0 (list) 2 CALL_FUNCTION 0 4 RETURN_VALUE
The same principle applies to dictionaries. The literal {} compiles to BUILD_MAP, while dict() follows the LOAD_GLOBAL and CALL_FUNCTION path. These extra opcodes, especially the name lookup, are the source of the performance gap.
Quantifying the Impact
While the difference per operation is minuscule, it becomes significant when creating millions of objects. We can use the timeit module to benchmark this at scale. The results consistently show that literals are faster.
import timeit
# Number of creations
n = 10_000_000
# Benchmark list creation
t_literal_list = timeit.timeit('[]', number=n)
t_constructor_list = timeit.timeit('list()', number=n)
# Benchmark dict creation
t_literal_dict = timeit.timeit('{}', number=n)
t_constructor_dict = timeit.timeit('dict()', number=n)
print(f"List Literal: {t_literal_list:.4f}s")
print(f"List Constructor: {t_constructor_list:.4f}s")
print(f"Dict Literal: {t_literal_dict:.4f}s")
print(f"Dict Constructor: {t_constructor_dict:.4f}s")
Typical results show literals being anywhere from 30% to 50% faster for this specific task. For any performance-sensitive code path involving repeated creation of basic collections, preferring literals is a clear-cut micro-optimization backed by how the interpreter works.
This principle of mechanical sympathy, understanding the machinery your code runs on, is key to moving from proficient to high-performance Python development.
What is the primary reason that creating a collection with a literal (e.g., []) is faster than using a constructor (e.g., list()) in CPython?
When inspecting the bytecode for creating an empty dictionary using {}, which opcode would you expect to see?