Python - Create Dictionary with Examples
• Python dictionaries are mutable, unordered collections that store data as key-value pairs, offering O(1) average time complexity for lookups, insertions, and deletions
Key Insights
• Python dictionaries are mutable, unordered collections that store data as key-value pairs, offering O(1) average time complexity for lookups, insertions, and deletions • Multiple creation methods exist including literal syntax, dict() constructor, dict comprehensions, and fromkeys() method, each suited for different scenarios • Dictionary keys must be immutable and hashable (strings, numbers, tuples), while values can be any Python object including nested dictionaries and lists
Creating Dictionaries with Literal Syntax
The most straightforward way to create a dictionary is using curly braces with key-value pairs separated by colons:
# Empty dictionary
empty_dict = {}
# Simple dictionary
user = {
"name": "Alice",
"age": 30,
"email": "alice@example.com"
}
# Dictionary with mixed data types
config = {
"host": "localhost",
"port": 8080,
"debug": True,
"max_connections": 100,
"allowed_ips": ["192.168.1.1", "10.0.0.1"]
}
print(user["name"]) # Output: Alice
print(config["port"]) # Output: 8080
Literal syntax is ideal for small to medium dictionaries where you know all key-value pairs at creation time. It’s readable and performs well.
Using the dict() Constructor
The dict() constructor offers flexibility for creating dictionaries from various sources:
# From keyword arguments
person = dict(name="Bob", age=25, city="New York")
# From tuple pairs
coordinates = dict([("x", 10), ("y", 20), ("z", 30)])
# From another dictionary (shallow copy)
original = {"a": 1, "b": 2}
copy = dict(original)
# From zip() combining two lists
keys = ["username", "password", "role"]
values = ["admin", "secret123", "superuser"]
credentials = dict(zip(keys, values))
print(credentials)
# Output: {'username': 'admin', 'password': 'secret123', 'role': 'superuser'}
The constructor is particularly useful when converting other data structures or when working with dynamically generated key-value pairs.
Dictionary Comprehensions
Dictionary comprehensions provide a concise way to create dictionaries based on iterables or transformations:
# Square numbers
squares = {x: x**2 for x in range(1, 6)}
print(squares) # Output: {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
# Filter and transform
numbers = [1, 2, 3, 4, 5, 6]
even_squares = {x: x**2 for x in numbers if x % 2 == 0}
print(even_squares) # Output: {2: 4, 4: 16, 6: 36}
# From existing dictionary
prices = {"apple": 0.5, "banana": 0.3, "orange": 0.7}
discounted = {item: price * 0.9 for item, price in prices.items()}
print(discounted)
# Output: {'apple': 0.45, 'banana': 0.27, 'orange': 0.63}
# Conditional logic
grades = {"Alice": 85, "Bob": 72, "Charlie": 91, "Diana": 68}
pass_fail = {name: "Pass" if score >= 70 else "Fail"
for name, score in grades.items()}
print(pass_fail)
# Output: {'Alice': 'Pass', 'Bob': 'Pass', 'Charlie': 'Pass', 'Diana': 'Fail'}
Comprehensions excel when you need to generate dictionaries programmatically with transformations or filters applied.
Using fromkeys() Method
The fromkeys() method creates a dictionary with specified keys and a default value:
# All values set to None
keys = ["name", "age", "email"]
template = dict.fromkeys(keys)
print(template) # Output: {'name': None, 'age': None, 'email': None}
# All values set to specific default
counters = dict.fromkeys(["a", "b", "c"], 0)
print(counters) # Output: {'a': 0, 'b': 0, 'c': 0}
# Initialize with empty lists (CAUTION: shallow copy issue)
# WRONG approach - all keys share same list object
wrong = dict.fromkeys(["x", "y", "z"], [])
wrong["x"].append(1)
print(wrong) # Output: {'x': [1], 'y': [1], 'z': [1]}
# CORRECT approach - use comprehension for mutable defaults
correct = {key: [] for key in ["x", "y", "z"]}
correct["x"].append(1)
print(correct) # Output: {'x': [1], 'y': [], 'z': []}
Be cautious with fromkeys() when using mutable default values, as all keys will reference the same object.
Nested Dictionaries
Dictionaries can contain other dictionaries, enabling hierarchical data structures:
# Multi-level nested structure
organization = {
"engineering": {
"backend": {
"team_lead": "Alice",
"members": ["Bob", "Charlie"],
"tech_stack": ["Python", "PostgreSQL"]
},
"frontend": {
"team_lead": "Diana",
"members": ["Eve", "Frank"],
"tech_stack": ["React", "TypeScript"]
}
},
"sales": {
"team_lead": "Grace",
"members": ["Henry", "Iris"]
}
}
# Accessing nested values
print(organization["engineering"]["backend"]["team_lead"]) # Output: Alice
print(organization["engineering"]["frontend"]["tech_stack"][0]) # Output: React
# Creating nested dictionaries dynamically
employees = ["emp1", "emp2", "emp3"]
employee_data = {
emp_id: {
"status": "active",
"projects": [],
"metadata": {}
} for emp_id in employees
}
print(employee_data["emp1"])
# Output: {'status': 'active', 'projects': [], 'metadata': {}}
Using defaultdict for Auto-Initialization
The collections.defaultdict automatically creates default values for missing keys:
from collections import defaultdict
# Group items by category
items = [("fruit", "apple"), ("vegetable", "carrot"),
("fruit", "banana"), ("vegetable", "spinach")]
grouped = defaultdict(list)
for category, item in items:
grouped[category].append(item)
print(dict(grouped))
# Output: {'fruit': ['apple', 'banana'], 'vegetable': ['carrot', 'spinach']}
# Count occurrences
text = "hello world"
char_count = defaultdict(int)
for char in text:
char_count[char] += 1
print(dict(char_count))
# Output: {'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1}
# Nested defaultdict
nested = defaultdict(lambda: defaultdict(int))
nested["user1"]["login_count"] += 1
nested["user1"]["login_count"] += 1
nested["user2"]["login_count"] += 1
print(dict(nested))
# Output: {'user1': {'login_count': 2}, 'user2': {'login_count': 1}}
Merging Dictionaries
Python 3.9+ introduced the merge operator, alongside older methods:
# Using | operator (Python 3.9+)
dict1 = {"a": 1, "b": 2}
dict2 = {"c": 3, "d": 4}
merged = dict1 | dict2
print(merged) # Output: {'a': 1, 'b': 2, 'c': 3, 'd': 4}
# Handling conflicts - rightmost wins
base = {"name": "Alice", "age": 30}
updates = {"age": 31, "city": "Boston"}
result = base | updates
print(result) # Output: {'name': 'Alice', 'age': 31, 'city': 'Boston'}
# Using unpacking (Python 3.5+)
merged_unpack = {**dict1, **dict2}
# Using update() - modifies in place
config = {"host": "localhost", "port": 8080}
config.update({"port": 9090, "debug": True})
print(config) # Output: {'host': 'localhost', 'port': 9090, 'debug': True}
# Merging multiple dictionaries
dicts = [{"a": 1}, {"b": 2}, {"c": 3}]
combined = {}
for d in dicts:
combined.update(d)
print(combined) # Output: {'a': 1, 'b': 2, 'c': 3}
Performance Considerations
Dictionary creation method choice impacts performance:
import timeit
# Benchmark different creation methods
def literal():
return {"a": 1, "b": 2, "c": 3}
def constructor():
return dict(a=1, b=2, c=3)
def comprehension():
return {k: v for k, v in [("a", 1), ("b", 2), ("c", 3)]}
# Literal syntax is fastest
print(timeit.timeit(literal, number=1000000)) # ~0.05s
print(timeit.timeit(constructor, number=1000000)) # ~0.15s
print(timeit.timeit(comprehension, number=1000000)) # ~0.25s
Use literal syntax for static dictionaries, comprehensions for transformations, and constructors for dynamic key-value generation. Choose defaultdict when you need automatic initialization to avoid KeyError exceptions and reduce boilerplate code.