← Reference · Nestor G Pestelos Jr · Print this page
Programming Languages · Python
Python Default Initialization Idioms
Reference entry · last updated September 20, 2026
Python default initialization idioms are the patterns Python programmers use to supply a value when an existing value needs a default or a dictionary key is absent. Python has no operator equivalent to Ruby's ||=. For an already bound name, x = x or y replaces any falsy value, including 0, "", and empty containers. Use if x is None when only None means unset. For dictionaries, the standard library supplies dict.get, dict.setdefault, and collections.defaultdict.[1][2]
First principles and definitions
Truth value and the object model
A Python name is a binding to an object, not a reserved storage cell with a type. Any object can be tested for truth value, for use in an if or while condition or as an operand of the Boolean operations. By default an object is considered true, and a class can override that by defining __bool__() to return false or __len__() to return zero.[1]
The false values are the constants None and False, numeric zeros, empty strings, and empty containers such as [] and {}. This set is broader than the Ruby pair of nil and false, and the difference drives every idiom on this page.
The Boolean operators and and or return one of their operands. The operator not returns a Boolean. The expression x or y first evaluates x; if x is true, its value is returned, and otherwise y is evaluated and its value is returned.[1][3]
Why there is no or-assign operator
Ruby defines ||= as conditional assignment: assign the right side only when the left side is nil or false. Python provides assignment statements, augmented assignment for arithmetic and sequence operators, and the assignment expression := introduced in Python 3.8. It defines no operator that assigns only when the current value is falsy. The idiom x = x or y uses or to choose the value, then assigns it.[1][5]
Dictionary lookup and KeyError
A dictionary maps unique keys to values, and a subscription lookup for an absent key raises KeyError. The read-with-default methods avoid the exception. dict.get(key, default) returns the value or the default and leaves the dictionary unchanged, while dict.setdefault(key, default) returns the value, or inserts the default when the key is missing and returns it.[1]
Conditional assignment without an operator
The idiom x = x or y evaluates x, keeps it if it is truthy, and otherwise takes y. Both this form and if x is None require an existing binding. An unbound name raises NameError, or UnboundLocalError for a local variable read before assignment. Initialize the name before testing it.[4]
x = None x = x or "default" # "default" x = 0 x = x or 1 # 1, because 0 is falsy
The falsy-value caveat
Because the test is truth value, a legitimate 0, False, "", or empty container is replaced. Use or only when every falsy value should trigger the default. If zero is a valid counter value, x = x or 1 discards it.[1]
Ruby's operator tests a narrower condition. x ||= y assigns when x is nil or false and leaves a legitimate 0 or empty string in place. Code ported from Ruby to Python by mechanical substitution changes behavior at every falsy value that is not None or False.
Testing for a sentinel instead
When a value can legitimately be zero, an empty string, or an empty container, test for the sentinel explicitly rather than for truth value.
x = None
if x is None:
x = 0
Use is None rather than a bare if not x when the sentinel is None, because if not x also fires on every other falsy value.
Dictionary defaults
get: read without inserting
d.get(k, default) returns the value for k, or default when k is absent, and does not modify the dictionary.[1] It suits read paths that must not create entries, such as configuration lookup or a count read where creating a key would change later output.
counts = {"billing": 2}
counts.get("email", 0) # 0, and "email" is still absent
setdefault: insert on first use
d.setdefault(k, default) returns the value for k. When k is missing it inserts k with default and returns default. The default argument itself defaults to None.[1] It tests key presence and preserves existing None and False values. Ruby's h[k] ||= v replaces nil and false.[5]
Python evaluates the default argument before calling setdefault, even when the key exists: d.setdefault(k, make_value()) always calls make_value(). The same applies to get.[3]
groups = {}
for row in rows:
groups.setdefault(row["dept"], []).append(row["name"])
The call inserts an empty list on the first row for a department and returns the existing list on later rows, so the append accumulates names per department and no row is dropped.
defaultdict: a factory on the dict
collections.defaultdict is a dict subclass that calls a factory function to supply missing values.[2] A subscription such as d[k] for a missing key invokes __missing__: when the default_factory attribute is None the lookup raises KeyError, and when it is set the factory is called with no arguments to provide the default.[2]
from collections import defaultdict
groups = defaultdict(list)
for row in rows:
groups[row["dept"]].append(row["name"])
setdefault takes a per-call default and works on an ordinary dictionary. defaultdict holds one factory and calls it for each missing-key subscription, then stores the result. With list, each new key gets a distinct list. Methods such as get do not call the factory.[2]
Comparison with Ruby
| Behavior | Ruby | Python |
|---|---|---|
| Conditional assignment | x ||= y | no operator; x = x or y requires a bound name |
| Default trigger for the forms above | nil or false | any falsy value |
| Insert a dict default | h[k] ||= v: replaces nil or false | d.setdefault(k, v): inserts only if the key is absent; evaluates v even if present |
| Grouping accumulator | (h[k] ||= []) << v | d.setdefault(k, []).append(v) |
| Auto-default dictionary | Hash.new { |h, k| h[k] = [] } | defaultdict(list) |
Choosing the idiom
- Read a default without inserting:
d.get(k, default). - Insert a default on first use:
d.setdefault(k, default). - Call a factory for each missing-key subscription:
defaultdict(factory). - An already bound variable where only
Nonemeans unset: testif x is None. - An already bound variable where every falsy value should trigger the default: use
x = x or y.
See also
References
- ^ Python Documentation, "Built-in Types." Free full text: docs.python.org/3/library/stdtypes.html
- ^ Python Documentation, "collections: Container datatypes." Free full text: docs.python.org/3/library/collections.html
- ^ Python Documentation, "Expressions." Free full text: docs.python.org/3/reference/expressions.html
- ^ Python Documentation, "Execution model: Resolution of names." docs.python.org/3/reference/executionmodel.html
- ^ Ruby Documentation, "Assignment: Abbreviated Assignment." docs.ruby-lang.org/en/3.4/syntax/assignment_rdoc.html