Python Dictionary Literal: Syntax and Usage
python dictionary literal: Understand Python dictionary literals: syntax, when to use them, common mistakes, and performance considerations for cleaner code.
The python dictionary literal is the most direct way to create a dictionary in Python. It uses curly braces with key-value pairs separated by commas, and it is the syntax you will see in most production code. This article covers the literal syntax, how it differs from the dict() constructor, and the practical decisions that come up when you use it.
What Is a Dictionary Literal in Python?
A dictionary literal is a fixed, readable way to construct a dictionary directly in source code. The syntax is simple: a pair of curly braces {} containing comma-separated key: value pairs. For example:
config = { "host": "localhost", "port": 5432, "debug": True }
This creates a dictionary with three entries. The keys are strings, but they can be any immutable type: integers, tuples, or even frozenset. Values can be any Python object, including lists, other dictionaries, or callables.
The literal is evaluated at runtime each time the line executes. That means the dictionary is freshly created on every call, which matters when you use a literal as a default argument or inside a loop.
Basic Syntax and Common Variations
You can write a dictionary literal on one line or span multiple lines. Python accepts both, as long as the braces are balanced. The trailing comma is optional but recommended for multi-line literals because it makes adding new entries later less error-prone.
# Single line user = {"name": "Ada", "role": "admin"} # Multi-line with trailing comma user = { "name": "Ada", "role": "admin", }
Keys do not need to be quoted if they are valid Python identifiers, but they will be treated as strings only when you use the dict() keyword form. In a literal, unquoted keys are evaluated as expressions. For example:
# This is a syntax error: `name` is treated as a variable # d = {name: "Ada"} # This works: the string key is explicit name = "user_name" d = {name: "Ada"} # key is the value of the variable `name`
If you need a key that is not a string, you write it directly without quotes. The literal syntax gives you full control over the key type.
When to Use a Literal Instead of dict()
The dict() constructor is useful when you need to build a dictionary from keyword arguments, from a sequence of pairs, or from another mapping. However, for a fixed, known set of key-value pairs, a literal is clearer and more concise.
# Using dict() with keyword arguments settings = dict(host="localhost", port=5432) # Equivalent literal settings = {"host": "localhost", "port": 5432}
The literal version is preferred because it makes the data structure visible at a glance. It also avoids a subtle limitation: dict() with keyword arguments only works when keys are valid Python identifiers. If you need a key like "first-name" or "123", the literal is the only readable option.
For dynamic construction, such as building a dictionary from a list of pairs, dict() is the right tool:
pairs = [("a", 1), ("b", 2)] d = dict(pairs)
Use a literal when the structure is static and known at development time. Use dict() when the keys or values come from runtime data.
Nested Literals and Dynamic Construction
Dictionaries often contain other dictionaries. A nested literal keeps the hierarchy visible and reduces the need for separate assignment statements.
user = { "name": "Ada", "address": { "city": "London", "postcode": "E1 6AN" }, "roles": ["admin", "editor"] }
You can also mix literals with computed values. The right-hand side of a key: value pair is an expression, so you can call functions or reference variables.
import datetime def build_event(name): return { "name": name, "created_at": datetime.datetime.now().isoformat(), "retries": 0 }
This is a common pattern for creating small data objects without defining a full class. The literal gives you a clear schema while still allowing dynamic values.
Common Mistakes with Dictionary Literals
One frequent error is using a list as a key. Lists are mutable, so they cannot be hashed. Python raises TypeError: unhashable type: 'list' if you try. Use a tuple instead if you need a compound key.
# This fails # bad = {["a", "b"]: 1} # This works good = {("a", "b"): 1}
Another mistake is forgetting that duplicate keys are not allowed. If you repeat a key in a literal, the last value wins, but this is usually a bug. Python does not warn you.
d = {"a": 1, "a": 2} # d is {'a': 2}
Also, be careful with boolean keys. True and 1 are considered equal as dictionary keys because they hash to the same value. This can lead to surprising overwrites:
d = {True: "yes", 1: "no"} # d is {True: 'no'}
These issues are not specific to literals, but they appear more often when you write a literal by hand because the structure is fixed and easy to inspect.
Performance and Memory Considerations
Creating a dictionary literal is a single operation that allocates the dictionary and inserts the entries in one step. In practice, the performance difference between a literal and dict() with keyword arguments is negligible for small dictionaries. The real cost comes from repeated creation in hot loops.
If you need the same dictionary many times, consider defining it once as a module-level constant. This avoids re-allocating the object on every call. For example:
DEFAULT_CONFIG = {"host": "localhost", "port": 5432} def connect(config=DEFAULT_CONFIG): # use config pass
Using a mutable default argument like a dictionary literal is a well-known pitfall. The default is evaluated once at function definition time, and every call that omits the argument shares the same dictionary object. If you mutate it, the change persists across calls. Use None as the default and create a fresh literal inside the function instead.
def add_user(name, roles=None): if roles is None: roles = {"read": True} roles[name] = True return roles
This is a maintainability issue as much as a performance one. The literal is fine when you create it fresh each time, but not when it is accidentally reused.
Maintaining Readability with Large Literals
Large dictionary literals can become hard to read. When a literal spans dozens of lines, consider whether a class or a dataclass would better represent the data. However, if you need a plain dictionary, keep the formatting consistent: one key-value pair per line, aligned colons, and a trailing comma.
You can also use comments to explain non-obvious keys or values. The literal syntax supports comments inside the braces, which is useful for configuration data.
settings = { "host": "localhost", # service host "port": 5432, # default port "timeout": 30, # seconds }
If the literal is built from several sources, you can merge them with the ** unpacking operator inside a literal. This is a Python 3.5+ feature that keeps the result readable while combining dictionaries.
base = {"host": "localhost", "port": 5432} overrides = {"port": 8080} final = {**base, **overrides}
The result has the overrides values taking precedence. This pattern is useful for configuration layering without mutating the original dictionaries.
Compatibility and Edge Cases
Dictionary literals are supported in all Python 3 versions. The ** unpacking inside a literal is available from Python 3.5 onward, so if you support older versions, avoid that syntax. The order of keys is preserved as insertion order from Python 3.7 onward, which is a language guarantee. Before 3.7, it was an implementation detail of CPython, but most code relied on it anyway.
When you write a literal with a computed key, the key expression is evaluated once per entry. This matters if the expression has side effects, such as calling a function that increments a counter. The evaluation order is left to right, and the value expression is evaluated after the key for each pair.
One edge case is using an empty literal {} to create an empty dictionary. This is the only way to get a dictionary literal; set() is used for an empty set. The empty dictionary literal is common and harmless.
Another edge case is using a dictionary literal inside a comprehension. You can create a dictionary with a comprehension, but the syntax uses {} with a key: value expression, not a plain literal. This is a separate feature, but it is often confused with a literal because of the braces.
squares = {x: x**2 for x in range(5)}
That is a dictionary comprehension, not a literal. It is evaluated lazily in the sense that the loop runs at execution time, but the result is a dictionary. Use a literal when the keys and values are known statically; use a comprehension when they must be computed from an iterable.
Understanding these distinctions helps you choose the right construction method and avoid subtle bugs. The python dictionary literal remains the clearest way to express a fixed mapping in code, and knowing its boundaries keeps your code predictable and maintainable.