Python Programming Dictionary
20+ Methods Comprehension

Python Dictionary Complete Guide

Learn all Python dictionary operations - creation, manipulation, built-in methods, comprehension, and advanced operations with practical examples.

Key-Value

Pairs storage

Fast Lookup

O(1) access

Mutable

Can be modified

Unordered

Python 3.7+ ordered

1. Introduction to Dictionaries

Dictionaries are unordered (Python 3.7+ maintains insertion order), mutable collections of key-value pairs. They are optimized for retrieving values by key.

Key Properties

  • Keys must be unique and immutable (strings, numbers, tuples)
  • Values can be any data type (mutable or immutable)
  • Mutable — can be changed after creation
  • Fast lookups by key — O(1) average time

2. Creating Dictionaries

Creating dictionaries
# Empty dictionary
empty_dict = {}
empty_dict2 = dict()

# Using curly braces
person = {
    "name": "Alice",
    "age": 30,
    "city": "New York"
}

# Using dict() constructor
person2 = dict(name="Bob", age=25, city="Los Angeles")

# From list of tuples
items = [("name", "Charlie"), ("age", 35), ("city", "Chicago")]
person3 = dict(items)

# Using fromkeys() - creates dict with default values
keys = ["name", "age", "city"]
default_dict = dict.fromkeys(keys, "unknown")
print(default_dict)  # {'name': 'unknown', 'age': 'unknown', 'city': 'unknown'}

# Dictionary with mixed key types
mixed = {
    "name": "David",
    1: "one",
    (1, 2): "tuple key",
    True: "boolean key"
}

# Values can be any type
complex_dict = {
    "numbers": [1, 2, 3],
    "metadata": {"created": "2024", "version": 1},
    "active": True
}

3. Accessing Dictionary Elements

Basic Access

[], get(), setdefault()
person = {"name": "Alice", "age": 30, "city": "New York"}

# Using square brackets (KeyError if key doesn't exist)
print(person["name"])    # Alice
print(person["age"])     # 30

# KeyError for missing key
# print(person["country"])  # KeyError!

# Using get() (returns None or default if key doesn't exist)
print(person.get("country"))        # None
print(person.get("country", "USA")) # USA
print(person.get("city", "Unknown")) # New York

# Using setdefault() - get value or set default if key missing
print(person.setdefault("phone", "123-456-7890"))  # 123-456-7890
print(person)  # phone is now added to dict

Accessing Nested Elements

Nested dicts
nested_dict = {
    "user1": {
        "name": "Alice",
        "address": {
            "city": "New York",
            "zip": "10001"
        }
    },
    "user2": {
        "name": "Bob",
        "address": {
            "city": "Los Angeles",
            "zip": "90210"
        }
    }
}

print(nested_dict["user1"]["name"])              # Alice
print(nested_dict["user2"]["address"]["city"])   # Los Angeles

4. Modifying Dictionaries

Adding and Updating Elements

Assignment and update()
person = {"name": "Alice", "age": 30}

# Adding new key-value pair
person["city"] = "New York"
print(person)  # {'name': 'Alice', 'age': 30, 'city': 'New York'}

# Updating existing key
person["age"] = 31
print(person)  # {'name': 'Alice', 'age': 31, 'city': 'New York'}

# Using update() - merges another dictionary
person.update({"country": "USA", "phone": "123-456-7890"})
print(person)

# Update with keyword arguments
person.update(age=32, city="Boston")
print(person)

# Update with list of tuples
person.update([("zip", "02108"), ("state", "MA")])
print(person)

Removing Elements

pop, popitem, del, clear
person = {"name": "Alice", "age": 30, "city": "New York", "phone": "123-4567"}

# pop() - remove and return value
age = person.pop("age")
print(age)          # 30
print(person)       # {'name': 'Alice', 'city': 'New York', 'phone': '123-4567'}

# pop() with default (no KeyError)
country = person.pop("country", "Not found")
print(country)      # Not found

# popitem() - remove and return (key, value) pair (LIFO in Python 3.7+)
key, value = person.popitem()
print(f"Removed: {key}={value}")  # Removed: phone=123-4567
print(person)  # {'name': 'Alice', 'city': 'New York'}

# del statement
del person["city"]
print(person)  # {'name': 'Alice'}

# clear() - remove all items
person.clear()
print(person)  # {}

5. Dictionary Methods

Information Methods

len, in, keys, values, items
person = {"name": "Alice", "age": 30, "city": "New York", "age": 31}  # Duplicate keys overwrite

# Length (number of key-value pairs)
print(len(person))  # 3

# Check if key exists
print("name" in person)     # True
print("country" in person)  # False
print("name" not in person) # False

# Check if value exists
print("Alice" in person.values())   # True
print(40 in person.values())        # False

# Get all keys
keys = person.keys()
print(keys)  # dict_keys(['name', 'age', 'city'])

# Get all values
values = person.values()
print(values)  # dict_values(['Alice', 31, 'New York'])

# Get all key-value pairs
items = person.items()
print(items)  # dict_items([('name', 'Alice'), ('age', 31), ('city', 'New York')])

Dictionary Views as Dynamic

Live views
person = {"name": "Alice", "age": 30}
keys = person.keys()
values = person.values()

print(keys)    # dict_keys(['name', 'age'])
print(values)  # dict_values(['Alice', 30])

# Views are dynamic - they reflect changes to the dictionary
person["city"] = "New York"
print(keys)    # dict_keys(['name', 'age', 'city'])
print(values)  # dict_values(['Alice', 30, 'New York'])

# Convert views to lists
key_list = list(person.keys())
value_list = list(person.values())
item_list = list(person.items())
Next: See the complete dictionary methods reference table below for every method at a glance.

Python Dictionary Methods Complete Reference

Python dictionaries provide built-in methods for access, updates, views, and copying. Here is a concise reference with examples.

Complete Dictionary Methods Reference Table

Category Method Description Syntax Example Result
Access get() Returns value for key, or default if missing d.get("a", 0) Value or default
Access setdefault() Gets value; if key missing, sets it to default and returns it d.setdefault("k", []) Value (possibly newly set)
Views keys() Dynamic view of dict keys d.keys() dict_keys(...)
Views values() Dynamic view of dict values d.values() dict_values(...)
Views items() Dynamic view of (key, value) pairs d.items() dict_items(...)
Modification clear() Removes all key-value pairs d.clear() None; dict is {}
Modification pop() Removes key and returns value; optional default if missing d.pop("a", None) Value removed or default
Modification popitem() Removes and returns last inserted pair (LIFO, 3.7+) d.popitem() (key, value)
Modification update() Merges from mapping or iterable of pairs / kwargs d.update({"b": 2}) None; dict updated in place
Copy copy() Shallow copy of the dictionary d.copy() New dict (shallow)
Class fromkeys() New dict with keys from iterable, all values equal (default None) dict.fromkeys(["a","b"], 0) {"a": 0, "b": 0}
Quick tips:
  • Use get() instead of [] when the key might be missing.
  • keys(), values(), and items() reflect live changes to the dict.
  • popitem() removes items in LIFO order (insertion order) in Python 3.7+.
  • Avoid dict.fromkeys(keys, [])—the same list is shared; use a comprehension instead.
  • For nested dicts, copy() is shallow; use copy.deepcopy() for full independence.

6. Looping Through Dictionaries

Basic Looping

keys, values, items
person = {"name": "Alice", "age": 30, "city": "New York"}

# Loop through keys
for key in person:
    print(f"{key}: {person[key]}")

# Loop through keys explicitly
for key in person.keys():
    print(key)

# Loop through values
for value in person.values():
    print(value)

# Loop through key-value pairs
for key, value in person.items():
    print(f"{key} = {value}")

Advanced Looping Techniques

sorted, reversed, conditions
# Sorted iteration
fruits = {"apple": 5, "banana": 3, "orange": 7, "grape": 2}

for key in sorted(fruits):
    print(f"{key}: {fruits[key]}")

# Iterate in reverse
for key in reversed(fruits):
    print(f"{key}: {fruits[key]}")

# Iterate with conditional
for key, value in fruits.items():
    if value > 4:
        print(f"{key} has {value} items")

# Using enumerate with dict
for i, (key, value) in enumerate(fruits.items()):
    print(f"{i}. {key}: {value}")

# Using zip with dict
keys = list(fruits.keys())
values = list(fruits.values())
for k, v in zip(keys, values):
    print(f"{k}: {v}")

7. Dictionary Comprehension

Dictionary comprehension provides a concise way to create dictionaries.

Basic Syntax

Basics
# Create dictionary of squares
squares = {x: x**2 for x in range(1, 6)}
print(squares)  # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# Create from two lists
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]
people = {name: age for name, age in zip(names, ages)}
print(people)  # {'Alice': 25, 'Bob': 30, 'Charlie': 35}

With Conditions

Filtering and transforms
# Filtering
numbers = range(1, 11)
even_squares = {x: x**2 for x in numbers if x % 2 == 0}
print(even_squares)  # {2: 4, 4: 16, 6: 36, 8: 64, 10: 100}

# Conditional values
parity = {x: "even" if x % 2 == 0 else "odd" for x in range(1, 6)}
print(parity)  # {1: 'odd', 2: 'even', 3: 'odd', 4: 'even', 5: 'odd'}

# Transforming a dictionary
original = {"apple": 5, "banana": 3, "orange": 7}
upscaled = {k: v * 10 for k, v in original.items()}
print(upscaled)  # {'apple': 50, 'banana': 30, 'orange': 70}

8. Advanced Dictionary Operations

Merging Dictionaries

**, |, update
# Python 3.5+ using ** operator
dict1 = {"a": 1, "b": 2}
dict2 = {"c": 3, "d": 4}
merged = {**dict1, **dict2}
print(merged)  # {'a': 1, 'b': 2, 'c': 3, 'd': 4}

# With overlapping keys (later overwrites earlier)
dict1 = {"a": 1, "b": 2}
dict2 = {"b": 99, "c": 3}
merged = {**dict1, **dict2}
print(merged)  # {'a': 1, 'b': 99, 'c': 3}

# Python 3.9+ using | operator
merged = dict1 | dict2
print(merged)  # {'a': 1, 'b': 99, 'c': 3}

# Update in-place
dict1.update(dict2)
print(dict1)  # {'a': 1, 'b': 99, 'c': 3}

Dictionary Union (Python 3.9+)

| and |=
# | operator for union
d1 = {"a": 1, "b": 2}
d2 = {"c": 3, "d": 4}
union = d1 | d2
print(union)  # {'a': 1, 'b': 2, 'c': 3, 'd': 4}

# |= for in-place update
d1 |= d2
print(d1)  # {'a': 1, 'b': 2, 'c': 3, 'd': 4}

Deep Copy for Nested Dictionaries

copy.deepcopy
import copy

original = {
    "user": {
        "name": "Alice",
        "preferences": {"theme": "dark", "language": "en"}
    }
}

# Shallow copy
shallow = original.copy()
original["user"]["preferences"]["theme"] = "light"
print(shallow["user"]["preferences"]["theme"])  # light (changed!)

# Deep copy
deep = copy.deepcopy(original)
original["user"]["preferences"]["language"] = "es"
print(deep["user"]["preferences"]["language"])  # en (unchanged!)

9. OrderedDict

While regular dictionaries maintain insertion order from Python 3.7+, OrderedDict provides additional functionality.

collections.OrderedDict
from collections import OrderedDict

# Creating OrderedDict
ordered = OrderedDict()
ordered["a"] = 1
ordered["b"] = 2
ordered["c"] = 3
print(ordered)  # OrderedDict([('a', 1), ('b', 2), ('c', 3)])

# Move to end/beginning
ordered.move_to_end("b")
print(ordered)  # OrderedDict([('a', 1), ('c', 3), ('b', 2)])

ordered.move_to_end("a", last=False)
print(ordered)  # OrderedDict([('a', 1), ('c', 3), ('b', 2)])

# Pop from beginning
first = ordered.popitem(last=False)
print(first)      # ('a', 1)
print(ordered)    # OrderedDict([('c', 3), ('b', 2)])

# Equality considers order in OrderedDict
od1 = OrderedDict([("a", 1), ("b", 2)])
od2 = OrderedDict([("b", 2), ("a", 1)])
print(od1 == od2)  # False (different order)

# Regular dict equality ignores order
d1 = {"a": 1, "b": 2}
d2 = {"b": 2, "a": 1}
print(d1 == d2)    # True

10. DefaultDict

defaultdict provides a default value for missing keys.

collections.defaultdict
from collections import defaultdict

# Default value as int (0)
counter = defaultdict(int)
counter["apple"] += 1
counter["banana"] += 2
print(counter)  # defaultdict(<class 'int'>, {'apple': 1, 'banana': 2})

# Default value as list
grouped = defaultdict(list)
grouped["fruits"].append("apple")
grouped["fruits"].append("banana")
grouped["vegetables"].append("carrot")
print(grouped)  # defaultdict(<class 'list'>, {'fruits': ['apple', 'banana'], 'vegetables': ['carrot']})

# Default value as set
unique = defaultdict(set)
unique["colors"].add("red")
unique["colors"].add("blue")
unique["colors"].add("red")  # Duplicate ignored
print(unique)  # defaultdict(<class 'set'>, {'colors': {'blue', 'red'}})

# Custom default factory
def default_value():
    return "N/A"

default_dict = defaultdict(default_value)
print(default_dict["missing"])  # N/A

11. Counter

Counter is a dictionary subclass for counting hashable objects.

collections.Counter
from collections import Counter

# Creating counters
word_counter = Counter("hello world")
print(word_counter)  # Counter({'l': 3, 'o': 2, ...})

list_counter = Counter(['a', 'b', 'c', 'a', 'b', 'a'])
print(list_counter)  # Counter({'a': 3, 'b': 2, 'c': 1})

# Creating from dictionary
c = Counter({'a': 3, 'b': 2, 'c': 1})

# Counting elements in a sentence
sentence = "the quick brown fox jumps over the lazy dog"
word_counts = Counter(sentence.split())
print(word_counts)

# Most common elements
print(word_counter.most_common(3))  # [('l', 3), ('o', 2), ('h', 1)]

# Arithmetic operations
c1 = Counter(a=3, b=2, c=1)
c2 = Counter(a=1, b=2, c=3)
print(c1 + c2)  # Counter({'a': 4, 'c': 4, 'b': 4})
print(c1 - c2)  # Counter({'a': 2})
print(c1 & c2)  # Counter({'b': 2, 'c': 1, 'a': 1})  # Intersection (min)
print(c1 | c2)  # Counter({'a': 3, 'c': 3, 'b': 2})  # Union (max)

# Elements (returns iterator)
print(list(c1.elements()))  # ['a', 'a', 'a', 'b', 'b', 'c']

# Subtract
c1.subtract(c2)
print(c1)  # Counter({'a': 2})

12. Common Pitfalls

Common mistakes
# Pitfall 1: Using mutable objects as keys
# invalid = {[1, 2]: "list key"}  # TypeError: unhashable type: 'list'
valid = {(1, 2): "tuple key"}     # Works - tuples are hashable

# Pitfall 2: Modifying dict while iterating
my_dict = {"a": 1, "b": 2, "c": 3}
# This will raise RuntimeError
# for key in my_dict:
#     if key == "b":
#         del my_dict[key]

# Instead, iterate over copy
for key in list(my_dict.keys()):
    if key == "b":
        del my_dict[key]
print(my_dict)  # {'a': 1, 'c': 3}

# Pitfall 3: Shallow copy with nested dicts
original = {"user": {"name": "Alice"}}
shallow = original.copy()
original["user"]["name"] = "Bob"
print(shallow["user"]["name"])  # Bob (changed!)

# Use deepcopy for nested structures
import copy
deep = copy.deepcopy(original)
original["user"]["name"] = "Charlie"
print(deep["user"]["name"])  # Bob (unchanged)

# Pitfall 4: Using fromkeys with mutable defaults
# All keys share the same list object!
d = dict.fromkeys(["a", "b", "c"], [])
d["a"].append(1)
print(d)  # {'a': [1], 'b': [1], 'c': [1]}

# Correct way
d = {key: [] for key in ["a", "b", "c"]}
d["a"].append(1)
print(d)  # {'a': [1], 'b': [], 'c': []}

# Pitfall 5: KeyError vs None
person = {"name": "Alice"}
# print(person["age"])  # KeyError!
print(person.get("age"))  # None (safer)

# Pitfall 6: Assumed order (pre-Python 3.7)
# In older Python versions, dict order was not guaranteed
# Use OrderedDict if order matters across all versions

Key Takeaways

  • Dictionaries map unique, immutable keys to values; lookups are fast on average
  • Use get(), setdefault(), and membership checks to avoid surprises
  • keys(), values(), and items() return dynamic views
  • Merge with update(), {**a, **b}, or | / |= (3.9+)
  • Use copy.deepcopy when cloning nested structures independently
  • OrderedDict, defaultdict, and Counter extend dict behavior for ordering, defaults, and counting
  • Never mutate a dict while iterating over it—iterate over a snapshot or build a new dict