1. Introduction
A dictionary is one of Python’s most powerful and most-used built-in data structures. It stores data as key-value pairs — each unique key maps to a value, just like a real-world dictionary maps a word to its definition. Dictionaries are the backbone of Python itself: function keyword arguments, object attributes, JSON data, environment configurations, and much more are all represented as dictionaries under the hood.
Introduced as a hash map, Python dictionaries offer O(1) average-time complexity for lookups, insertions, and deletions. Since Python 3.7, they also preserve insertion order, making them even more versatile.
This blog covers everything from creating your first dictionary to advanced patterns like nested dicts, defaultdict, Counter, and dictionary comprehensions — with output tables after every example so you can see exactly what each operation produces.
2. Creating Dictionaries
Dictionaries can be created in four different ways depending on context and preference.
2.1 Literal Syntax { }
# Curly-brace literal — most common waystudent = { 'name': 'Priya Sharma', 'age': 22, 'grade': 'A', 'score': 91.5}print(student)
OUTPUT: print(student)
| Output |
| {‘name’: ‘Priya Sharma’, ‘age’: 22, ‘grade’: ‘A’, ‘score’: 91.5} |
2.2 dict() Constructor
# Using the dict() built-in with keyword argumentsproduct = dict(name='Laptop', brand='Dell', price=75000, in_stock=True)print(product)# From a list of (key, value) tuplespairs = [('city', 'Delhi'), ('country', 'India'), ('pin', 110001)]location = dict(pairs)print(location)
OUTPUT: dict() from keyword args and from tuple pairs
| Variable | Output |
| product | {‘name’: ‘Laptop’, ‘brand’: ‘Dell’, ‘price’: 75000, ‘in_stock’: True} |
| location | {‘city’: ‘Delhi’, ‘country’: ‘India’, ‘pin’: 110001} |
2.3 dict.fromkeys() — Initialise with Default Values
# Create a dict with predefined keys, all sharing the same default valuesubjects = ['Math', 'Science', 'English', 'History']scores = dict.fromkeys(subjects, 0) # all start at 0print(scores)# Default value None when omittedprofile = dict.fromkeys(['name', 'email', 'phone'])print(profile)
OUTPUT: fromkeys() with default values
| Variable | Output |
| scores | {‘Math’: 0, ‘Science’: 0, ‘English’: 0, ‘History’: 0} |
| profile | {‘name’: None, ’email’: None, ‘phone’: None} |
2.4 Empty Dictionary
empty1 = {} # literal empty dictempty2 = dict() # constructor empty dictprint(type(empty1), len(empty1)) # <class 'dict'> 0
OUTPUT: Empty dictionary type and length
| Output |
| <class ‘dict’> 0 |
3. Accessing Values
Dictionary values are accessed by their key. Python offers two ways: direct bracket notation and the safer .get() method.
3.1 Bracket Notation [ ]
employee = {'id': 101, 'name': 'Arjun', 'dept': 'Engineering', 'salary': 85000}print(employee['name']) # direct accessprint(employee['salary'])# KeyError if key doesn't exist# print(employee['age']) # KeyError: 'age'
OUTPUT: Accessing name and salary
| Expression | Output |
| employee[‘name’] | Arjun |
| employee[‘salary’] | 85000 |
3.2 .get() — Safe Access with Default
employee = {'id': 101, 'name': 'Arjun', 'dept': 'Engineering', 'salary': 85000}print(employee.get('dept')) # returns value if key existsprint(employee.get('age')) # returns None (key missing, no error)print(employee.get('age', 'N/A')) # returns 'N/A' as defaultprint(employee.get('salary', 0)) # key exists, so returns 85000
OUTPUT: .get() — safe key access
| Expression | Output |
| employee.get(‘dept’) | Engineering |
| employee.get(‘age’) | None |
| employee.get(‘age’, ‘N/A’) | N/A |
| employee.get(‘salary’, 0) | 85000 |
Best practice: always use .get() when you are not certain a key exists. It avoids KeyError and lets you specify a sensible default.
4. Adding, Updating & Deleting Keys
Dictionaries are mutable — you can add new keys, change existing values, and remove entries at any time.
4.1 Adding New Key-Value Pairs
inventory = {'apples': 50, 'bananas': 30}print('Before:', inventory)inventory['oranges'] = 45 # add a new keyinventory['grapes'] = 20print('After:', inventory)
OUTPUT: Adding new keys to a dictionary
| State | Dictionary |
| Before | {‘apples’: 50, ‘bananas’: 30} |
| After | {‘apples’: 50, ‘bananas’: 30, ‘oranges’: 45, ‘grapes’: 20} |
4.2 Updating Existing Values
prices = {'rice': 60, 'wheat': 45, 'sugar': 55}print('Before:', prices)prices['rice'] = 65 # update existing keyprices['sugar'] = 58print('After:', prices)
OUTPUT: Updating existing values
| Key | Before | After |
| rice | 60 | 65 |
| wheat | 45 | 45 (unchanged) |
| sugar | 55 | 58 |
4.3 Deleting Keys — del, pop(), popitem(), clear()
config = {'host': 'localhost', 'port': 5432, 'db': 'mydb', 'debug': True}# del — remove key (KeyError if missing)del config['debug']print('After del:', config)# pop() — remove and return the valueport = config.pop('port')print('Popped port:', port)print('After pop:', config)# pop() with default (no error if key missing)val = config.pop('timeout', 30)print('Default pop:', val)# popitem() — remove and return last inserted (key, value) pairitem = config.popitem()print('Popped item:', item)# clear() — remove ALL entriesconfig.clear()print('After clear:', config)
OUTPUT: Step-by-step deletion operations
| Operation | Return Value | Dictionary After |
| del config[‘debug’] | – | {‘host’: ‘localhost’, ‘port’: 5432, ‘db’: ‘mydb’} |
| config.pop(‘port’) | 5432 | {‘host’: ‘localhost’, ‘db’: ‘mydb’} |
| config.pop(‘timeout’, 30) | 30 | {‘host’: ‘localhost’, ‘db’: ‘mydb’} (unchanged) |
| config.popitem() | (‘db’,’mydb’) | {‘host’: ‘localhost’} |
| config.clear() | – | {} |
5. Dictionary Methods — Complete Reference
| Method | Description | Returns |
| d.keys() | All keys | dict_keys view |
| d.values() | All values | dict_values view |
| d.items() | All key-value pairs as tuples | dict_items view |
| d.get(k, def) | Value for key k, or default | value or default |
| d.update(other) | Merge another dict / iterable into d | None (in-place) |
| d.setdefault(k,v) | Return value if key exists, else set it | value |
| d.pop(k, def) | Remove key and return value | value or default |
| d.popitem() | Remove and return last (k, v) pair | (key, value) |
| d.copy() | Shallow copy of the dictionary | new dict |
| d.clear() | Remove all items | None |
| d.fromkeys(k, v) | New dict from keys with optional value | new dict |
| len(d) | Number of key-value pairs | int |
| k in d | True if key k exists | bool |
| k not in d | True if key k does not exist | bool |
5.1 .keys(), .values(), .items()
person = {'name': 'Rohan', 'city': 'Mumbai', 'age': 28, 'job': 'Developer'}print(person.keys()) # dict_keys(['name', 'city', 'age', 'job'])print(person.values()) # dict_values(['Rohan', 'Mumbai', 28, 'Developer'])print(person.items()) # dict_items([('name','Rohan'),('city','Mumbai'),...])# Convert to list if you need indexingkeys_list = list(person.keys())print(keys_list) # ['name', 'city', 'age', 'job']
OUTPUT: keys(), values(), and items()
| Method | Output |
| person.keys() | dict_keys([‘name’, ‘city’, ‘age’, ‘job’]) |
| person.values() | dict_values([‘Rohan’, ‘Mumbai’, 28, ‘Developer’]) |
| person.items() | dict_items([(‘name’,’Rohan’), (‘city’,’Mumbai’), (‘age’,28), (‘job’,’Developer’)]) |
| list(person.keys()) | [‘name’, ‘city’, ‘age’, ‘job’] |
5.2 .update() — Merging Dictionaries
base = {'name': 'Sita', 'age': 25, 'city': 'Pune'}extra = {'age': 26, 'email': 'sita@example.com', 'phone': '9876543210'}base.update(extra) # merges extra into base; overlapping keys are overwrittenprint(base)# Python 3.9+ merge operator |merged = base | {'tier': 'Gold'}print(merged)
OUTPUT: update() merges and overwrites — age changed to 26
| Variable | Output |
| base (after update) | {‘name’: ‘Sita’, ‘age’: 26, ‘city’: ‘Pune’, ’email’: ‘sita@example.com’, ‘phone’: ‘9876543210’} |
| merged | {‘name’: ‘Sita’, ‘age’: 26, ‘city’: ‘Pune’, ’email’: ‘sita@example.com’, ‘phone’: ‘9876543210’, ‘tier’: ‘Gold’} |
5.3 .setdefault() — Insert If Missing
user = {'name': 'Amit', 'email': 'amit@mail.com'}# Key exists — returns existing value, does NOT overwritev1 = user.setdefault('name', 'Unknown')print(v1) # Amit (existing value returned)# Key missing — inserts with default and returns defaultv2 = user.setdefault('role', 'viewer')print(v2) # viewerprint(user) # role has been added
OUTPUT: setdefault() — only inserts if key is absent
| Expression | Output |
| v1 = user.setdefault(‘name’,’Unknown’) | Amit (key existed, no change) |
| v2 = user.setdefault(‘role’,’viewer’) | viewer (key added) |
| print(user) | {‘name’: ‘Amit’, ’email’: ‘amit@mail.com’, ‘role’: ‘viewer’} |
6. Iterating Over a Dictionary
You can iterate over a dictionary in three ways: over keys (default), over values, or over key-value pairs simultaneously.
6.1 Iterate Over Keys (Default)
marks = {'Maths': 88, 'Science': 92, 'English': 79, 'History': 85}for subject in marks: # iterates keys by default print(subject)
OUTPUT: Iterating keys
| Output (one per line) |
| Maths |
| Science |
| English |
| History |
6.2 Iterate Over Values
for score in marks.values(): print(score)
OUTPUT: Iterating values
| Output (one per line) |
| 88 |
| 92 |
| 79 |
| 85 |
6.3 Iterate Over Key-Value Pairs with .items()
for subject, score in marks.items(): print(f'{subject}: {score}')
OUTPUT: Iterating key-value pairs
| Output |
| Maths: 88 |
| Science: 92 |
| English: 79 |
| History: 85 |
6.4 Enumerate with Index
for idx, (subject, score) in enumerate(marks.items(), start=1): print(f'{idx}. {subject} -> {score}')
OUTPUT: Enumerated key-value pairs
| Output |
| 1. Maths -> 88 |
| 2. Science -> 92 |
| 3. English -> 79 |
| 4. History -> 85 |
7. Dictionary Comprehensions
Dictionary comprehensions provide a concise, readable way to create or transform dictionaries in a single expression. They follow the pattern: {key_expr: value_expr for item in iterable if condition}.
7.1 Basic Comprehension — Squares
squares = {x: x**2 for x in range(1, 7)}print(squares)
OUTPUT: Squares dictionary
| Output |
| { 1:1, 2:4, 3:9, 4:16, 5:25, 6:36 } |
7.2 Comprehension with Condition — Filter
all_scores = {'Alice': 72, 'Bob': 88, 'Carol': 55, 'David': 93, 'Eve': 61}# Keep only students who passed (score >= 70)passed = {name: score for name, score in all_scores.items() if score >= 70}print(passed)# Grade mappinggrades = {name: 'Pass' if s >= 70 else 'Fail' for name, s in all_scores.items()}print(grades)
OUTPUT: Filtered and transformed comprehensions
| Variable | Output |
| passed | {‘Alice’: 72, ‘Bob’: 88, ‘David’: 93} |
| grades | {‘Alice’: ‘Pass’, ‘Bob’: ‘Pass’, ‘Carol’: ‘Fail’, ‘David’: ‘Pass’, ‘Eve’: ‘Fail’} |
7.3 Swap Keys and Values
country_code = {'India': 'IN', 'Germany': 'DE', 'Japan': 'JP', 'Brazil': 'BR'}# Invert: code -> countrycode_country = {code: country for country, code in country_code.items()}print(code_country)
OUTPUT: Inverted dictionary
| Output |
| {‘IN’: ‘India’, ‘DE’: ‘Germany’, ‘JP’: ‘Japan’, ‘BR’: ‘Brazil’} |
7.4 Comprehension from Two Lists (zip)
subjects = ['Maths', 'Physics', 'Chemistry', 'Biology']scores = [88, 76, 91, 83]result = {sub: sc for sub, sc in zip(subjects, scores)}print(result)
OUTPUT: Dictionary built from two lists with zip
| Output |
| {‘Maths’: 88, ‘Physics’: 76, ‘Chemistry’: 91, ‘Biology’: 83} |
8. Nested Dictionaries
A dictionary’s values can themselves be dictionaries. This is ideal for representing hierarchical data like student records, JSON-like structures, or organisational trees.
8.1 Creating and Accessing Nested Dicts
school = { 'Class_A': { 'Priya': {'math': 90, 'science': 85}, 'Rohan': {'math': 78, 'science': 92}, }, 'Class_B': { 'Sara': {'math': 88, 'science': 79}, 'Amit': {'math': 95, 'science': 91}, }}# Access nested valueprint(school['Class_A']['Priya']['math']) # 90# Safe nested access with get()score = school.get('Class_A', {}).get('Rohan', {}).get('science', 0)print(score) # 92
OUTPUT: Nested dictionary access
| Expression | Output |
| school[‘Class_A’][‘Priya’][‘math’] | 90 |
| school.get(‘Class_A’,{}).get(‘Rohan’,{}).get(‘science’,0) | 92 |
8.2 Iterating Over Nested Dicts
for class_name, students in school.items(): print(f'--- {class_name} ---') for student, marks in students.items(): avg = sum(marks.values()) / len(marks) print(f' {student}: avg = {avg:.1f}')
OUTPUT: Nested iteration output
| Output |
| — Class_A — |
| Priya: avg = 87.5 |
| Rohan: avg = 85.0 |
| — Class_B — |
| Sara: avg = 83.5 |
| Amit: avg = 93.0 |
9. Special Dictionary Types from collections
Python’s collections module extends the built-in dict with three specialised variants, each solving a common pattern more elegantly.
9.1 defaultdict — No More KeyError on Missing Keys
from collections import defaultdict# defaultdict(list) — missing key automatically gets an empty listgroups = defaultdict(list)students = [('A', 'Priya'), ('B', 'Rohan'), ('A', 'Sara'), ('B', 'Amit'), ('A', 'Kiran')]for cls, name in students: groups[cls].append(name) # no KeyError even on first accessprint(dict(groups))# defaultdict(int) for countingword_count = defaultdict(int)sentence = 'the cat sat on the mat the cat'.split()for word in sentence: word_count[word] += 1print(dict(word_count))
OUTPUT: defaultdict — auto-initialised keys
| Variable | Output |
| groups | {‘A’: [‘Priya’, ‘Sara’, ‘Kiran’], ‘B’: [‘Rohan’, ‘Amit’]} |
| word_count | {‘the’: 3, ‘cat’: 2, ‘sat’: 1, ‘on’: 1, ‘mat’: 1} |
9.2 Counter — Count Occurrences Instantly
from collections import Counter# Count characters in a stringtext = 'mississippi'char_count = Counter(text)print(char_count)# Most common elementsprint(char_count.most_common(3))# Count words in a sentencewords = 'apple banana apple cherry banana apple'.split()fruit_count = Counter(words)print(fruit_count)# Arithmetic on Countersc1 = Counter({'a': 3, 'b': 1})c2 = Counter({'a': 1, 'b': 2, 'c': 5})print(c1 + c2) # add counts
OUTPUT: Counter operations
| Expression | Output |
| Counter(‘mississippi’) | Counter({‘s’: 4, ‘i’: 4, ‘p’: 2, ‘m’: 1}) |
| char_count.most_common(3) | [(‘s’, 4), (‘i’, 4), (‘p’, 2)] |
| Counter(words) | Counter({‘apple’: 3, ‘banana’: 2, ‘cherry’: 1}) |
| c1 + c2 | Counter({‘a’: 4, ‘c’: 5, ‘b’: 3}) |
9.3 OrderedDict — Explicit Order Control
from collections import OrderedDict# Useful before Python 3.7 (regular dicts now preserve order too)od = OrderedDict()od['first'] = 1od['second'] = 2od['third'] = 3print(od)# Move a key to the end or beginningod.move_to_end('first')print(od) # 'first' is now lastod.move_to_end('third', last=False)print(od) # 'third' is now first
OUTPUT: OrderedDict ordering operations
| State | Output |
| Initial | OrderedDict([(‘first’,1), (‘second’,2), (‘third’,3)]) |
| After move_to_end(‘first’) | OrderedDict([(‘second’,2), (‘third’,3), (‘first’,1)]) |
| After move_to_end(‘third’, last=False) | OrderedDict([(‘third’,3), (‘second’,2), (‘first’,1)]) |
10. Membership Testing & Key Checking
config = {'debug': True, 'host': 'localhost', 'port': 8080, 'timeout': 30}# in — check if key existsprint('host' in config) # Trueprint('password' in config) # False# not inprint('api_key' not in config) # True# Check value existence (use .values())print(8080 in config.values()) # True# Conditional access patternif 'debug' in config and config['debug']: print('Debug mode is ON')
OUTPUT: Membership and existence checks
| Expression | Output |
| ‘host’ in config | True |
| ‘password’ in config | False |
| ‘api_key’ not in config | True |
| 8080 in config.values() | True |
| Debug mode is ON | (printed by if block) |
11. Sorting a Dictionary
Dictionaries themselves are not sorted by value, but you can produce sorted views or convert to a sorted structure easily.
11.1 Sort by Key
prices = {'banana': 20, 'apple': 45, 'cherry': 80, 'date': 35}sorted_by_key = dict(sorted(prices.items()))print(sorted_by_key)
OUTPUT: Sorted by key (alphabetical)
| Output |
| {‘apple’: 45, ‘banana’: 20, ‘cherry’: 80, ‘date’: 35} |
11.2 Sort by Value
# Ascending by valuesorted_by_val_asc = dict(sorted(prices.items(), key=lambda item: item[1]))print('Ascending:', sorted_by_val_asc)# Descending by valuesorted_by_val_desc = dict(sorted(prices.items(), key=lambda item: item[1], reverse=True))print('Descending:', sorted_by_val_desc)
OUTPUT: Sorted by value ascending and descending
| Order | Output |
| Ascending | {‘banana’: 20, ‘date’: 35, ‘apple’: 45, ‘cherry’: 80} |
| Descending | {‘cherry’: 80, ‘apple’: 45, ‘date’: 35, ‘banana’: 20} |
12. Merging Dictionaries
Python 3 offers several ways to merge dictionaries, each with slightly different semantics.
d1 = {'a': 1, 'b': 2, 'c': 3}d2 = {'c': 30, 'd': 4, 'e': 5} # 'c' exists in both — d2 wins# Method 1: update() (in-place, modifies d1)merged1 = d1.copy()merged1.update(d2)print('update():', merged1)# Method 2: ** unpacking (creates new dict)merged2 = {**d1, **d2}print('** unpack:', merged2)# Method 3: | operator (Python 3.9+, creates new dict)merged3 = d1 | d2print('| operator:', merged3)# Method 4: |= operator (Python 3.9+, in-place)d3 = {'x': 10, 'y': 20}d3 |= {'y': 99, 'z': 30}print('|= operator:', d3)
OUTPUT: All four merge methods
| Method | Output |
| update() | {‘a’: 1, ‘b’: 2, ‘c’: 30, ‘d’: 4, ‘e’: 5} |
| ** unpack | {‘a’: 1, ‘b’: 2, ‘c’: 30, ‘d’: 4, ‘e’: 5} |
| | operator | {‘a’: 1, ‘b’: 2, ‘c’: 30, ‘d’: 4, ‘e’: 5} |
| |= operator | {‘x’: 10, ‘y’: 99, ‘z’: 30} |
13. Copying Dictionaries — Shallow vs Deep
import copyoriginal = {'name': 'Anjali', 'scores': [90, 85, 78]}# Shallow copy — top-level keys are independent, nested objects are sharedshallow = original.copy()shallow['name'] = 'Priya' # does NOT affect originalshallow['scores'].append(95) # DOES affect original (shared list!)print('original:', original)print('shallow: ', shallow)# Deep copy — fully independent at all levelsdeep = copy.deepcopy(original)deep['scores'].append(100) # does NOT affect originalprint('After deep change:')print('original:', original)print('deep: ', deep)
OUTPUT: Shallow vs deep copy behaviour
| After shallow[‘scores’].append(95) | Value |
| original[‘scores’] | [90, 85, 78, 95] (shared — affected!) |
| shallow[‘scores’] | [90, 85, 78, 95] (same list object) |
| original[‘name’] | Anjali (string is independent — not affected) |
| shallow[‘name’] | Priya |
Rule: use dict.copy() or {**d} for flat (no nested) dicts. Use copy.deepcopy() whenever values contain mutable objects like lists, sets, or other dicts.
14. Best Practices
- Use .get(key, default) instead of direct bracket access when a key may be absent.
- Use dict comprehensions instead of for-loop-with-setitem for clean, readable transformations.
- Prefer defaultdict(list/int/set) when building group-by or counting structures.
- Use Counter for any counting task — it is far more readable than a manual dict.
- Use copy.deepcopy() when cloning dicts that contain mutable nested values.
- Use the | merge operator (Python 3.9+) for clean, non-mutating dict merges.
- Keep dictionary keys consistent types (all strings, all ints) for predictability.
- For large, frequently-read dicts, consider __slots__ or dataclasses as a faster alternative.
- Use meaningful key names — avoid abbreviations that reduce readability.
- Use dict.items() in for-loops instead of dict[key] lookups for cleaner, faster iteration.
15. Quick Reference Cheat Sheet
| Operation | Syntax / Example | Notes |
| Create | d = {‘k’: v} or dict(k=v) | Literal or constructor |
| Access | d[‘key’] or d.get(‘key’, default) | .get() is safer |
| Add / Update | d[‘key’] = value | Creates or overwrites |
| Delete key | del d[‘key’] or d.pop(‘key’) | del raises KeyError if missing |
| All keys | d.keys() | Returns view object |
| All values | d.values() | Returns view object |
| All pairs | d.items() | Returns (k,v) tuples |
| Merge (in-place) | d.update(other) | Overwrites duplicates |
| Merge (new dict) | d1 | d2 or {**d1, **d2} | 3.9+ for | |
| Check key | ‘key’ in d | O(1) lookup |
| Set if missing | d.setdefault(‘key’, default) | Inserts only if absent |
| Remove last pair | d.popitem() | Returns (k, v) tuple |
| Remove all | d.clear() | Empties the dict |
| Shallow copy | d.copy() or {**d} | Nested objects shared |
| Deep copy | copy.deepcopy(d) | Fully independent |
| Comprehension | {k: v for k, v in items if cond} | Concise transformation |
| Count occurrences | Counter(iterable) | collections.Counter |
| Auto-default | defaultdict(list) | collections.defaultdict |
| Sort by key | dict(sorted(d.items())) | Alphabetical by default |
| Sort by value | dict(sorted(d.items(), key=lambda x: x[1])) | lambda on value |
16. Conclusion
Python dictionaries are far more than a simple key-value store. With O(1) lookups, preserved insertion order, a rich set of methods, and powerful variants like defaultdict and Counter, they are the go-to data structure for a vast range of programming tasks — from counting and grouping to configuration management and JSON parsing.
The foundation is simple: create with {}, access with .get(), iterate with .items(), and transform with comprehensions. Layer on defaultdict for automatic defaults, Counter for frequency analysis, and copy.deepcopy() for safe cloning — and you have a complete dictionary toolkit for any Python project.
Happy Coding!
Discover more from DataSangyan
Subscribe to get the latest posts sent to your email.