- What is collections module — specialized container data types
- Counter — count items in a sequence
- defaultdict — dictionary with default values
- OrderedDict — dictionary that remembers order
- deque — fast operations from both ends
- namedtuple — lightweight immutable objects
What is Collections Module?
The collections module is a built-in Python module that provides specialized container data types that are more powerful than the basic ones like lists, tuples, and dictionaries.
Think of the collections module like a toolbox with specialized tools. A regular list or dictionary is like a hammer – it works for many jobs. But sometimes you need a specialized tool like a screwdriver or a wrench – that's what collections gives you.
💡 Key concept: Collections module gives you specialized data structures that make your code more efficient and easier to write.
Counter - Counting Items
Count How Many Times Each Item Appears
Counter is a dictionary subclass that counts how many times each item appears in a sequence. It's perfect for counting words, characters, or any items.
# Counter - Counting Items
from collections import Counter
print("=" * 50)
print("COUNTER - COUNTING ITEMS")
print("=" * 50)
# ============================================================
# BASIC COUNTER
# ============================================================
print("\n1. BASIC COUNTER")
# Count items in a list
colors = ["red", "blue", "red", "green", "blue", "red", "yellow"]
color_counts = Counter(colors)
print(f" Colors: {colors}")
print(f" Counts: {color_counts}")
# Access counts like a dictionary
print(f" Red count: {color_counts['red']}")
print(f" Blue count: {color_counts['blue']}")
print(f" Green count: {color_counts['green']}")
# ============================================================
# COUNTING WORDS IN A STRING
# ============================================================
print("\n2. COUNTING WORDS IN A STRING")
text = "the cat in the hat the cat sat on the mat"
words = text.split()
word_counts = Counter(words)
print(f" Text: {text}")
print(f" Word counts: {word_counts}")
# ============================================================
# MOST COMMON ITEMS
# ============================================================
print("\n3. MOST COMMON ITEMS")
# Get the most common items
print(f" Top 2 most common: {word_counts.most_common(2)}")
print(f" Top 3 most common: {word_counts.most_common(3)}")
# All items sorted by count
print(f" All sorted: {word_counts.most_common()}")
# ============================================================
# COUNTER METHODS
# ============================================================
print("\n4. COUNTER METHODS")
counter1 = Counter(a=3, b=2, c=1)
counter2 = Counter(a=1, b=2, c=3, d=1)
print(f" Counter 1: {counter1}")
print(f" Counter 2: {counter2}")
# Add two counters
print(f" Counter 1 + Counter 2: {counter1 + counter2}")
# Subtract counters
print(f" Counter 1 - Counter 2: {counter1 - counter2}")
# Intersection (min values)
print(f" Counter 1 & Counter 2: {counter1 & counter2}")
# Union (max values)
print(f" Counter 1 | Counter 2: {counter1 | counter2}")
# Total count
print(f" Total items in counter1: {sum(counter1.values())}")
# ============================================================
# UPDATING A COUNTER
# ============================================================
print("\n5. UPDATING A COUNTER")
counter = Counter()
print(f" Empty counter: {counter}")
# Add items
counter.update(["a", "b", "a", "c", "a"])
print(f" After update: {counter}")
# Add more items
counter.update(["b", "b", "d"])
print(f" After second update: {counter}")
# Add a dictionary
counter.update({"a": 2, "e": 1})
print(f" After dict update: {counter}")
# ============================================================
# REMOVING ITEMS
# ============================================================
print("\n6. REMOVING ITEMS")
counter = Counter(a=5, b=3, c=1, d=0)
print(f" Before: {counter}")
# Remove items with count <= 0
counter += Counter()
print(f" After removing zeros: {counter}")
# Delete a specific item
del counter['a']
print(f" After deleting 'a': {counter}")
Counter key points:
- Counts items — how many times each appears
- most_common() — returns items sorted by count
- Update — add more items to count
- Math operations — add, subtract, intersect, union
- Dictionary-like — works like a dictionary
Quick Check: How do you get the most common items from a Counter? (Answer: Use counter.most_common(n))
defaultdict - Auto Defaults
Dictionary with Automatic Default Values
defaultdict is like a regular dictionary, but it automatically provides a default value for keys that don't exist. No more KeyError!
# defaultdict - Auto Defaults
from collections import defaultdict
print("=" * 50)
print("DEFAULTDICT - AUTO DEFAULTS")
print("=" * 50)
# ============================================================
# BASIC DEFAULTDICT
# ============================================================
print("\n1. BASIC DEFAULTDICT")
# Regular dictionary - KeyError if key doesn't exist
regular = {}
try:
print(regular['missing'])
except KeyError:
print(" Regular dict: KeyError!")
# defaultdict - returns default value
dd = defaultdict(int) # int() returns 0
print(f" dd['missing']: {dd['missing']}") # Returns 0
print(f" dd: {dd}") # Key was created!
# ============================================================
# DIFFERENT DEFAULT TYPES
# ============================================================
print("\n2. DIFFERENT DEFAULT TYPES")
# Default: list (empty list)
dd_list = defaultdict(list)
dd_list['fruits'].append('apple')
dd_list['fruits'].append('banana')
print(f" Default list: {dd_list}")
# Default: int (0)
dd_int = defaultdict(int)
dd_int['count'] += 1
dd_int['count'] += 1
print(f" Default int: {dd_int}")
# Default: str (empty string)
dd_str = defaultdict(str)
dd_str['name'] += 'Alice'
print(f" Default str: {dd_str}")
# Default: set (empty set)
dd_set = defaultdict(set)
dd_set['tags'].add('python')
dd_set['tags'].add('programming')
print(f" Default set: {dd_set}")
# ============================================================
# CUSTOM DEFAULT
# ============================================================
print("\n3. CUSTOM DEFAULT")
# Your own default function
def default_value():
return "Not Found"
dd_custom = defaultdict(default_value)
print(f" dd_custom['missing']: {dd_custom['missing']}")
print(f" dd_custom: {dd_custom}")
# ============================================================
# GROUPING ITEMS WITH DEFAULTDICT
# ============================================================
print("\n4. GROUPING ITEMS")
fruits = [
("apple", 5), ("banana", 3), ("apple", 2),
("orange", 4), ("banana", 1), ("apple", 3)
]
# Group by fruit name
grouped = defaultdict(list)
for fruit, count in fruits:
grouped[fruit].append(count)
print(f" Fruits: {fruits}")
print(f" Grouped: {grouped}")
# Sum counts by fruit
total_counts = defaultdict(int)
for fruit, count in fruits:
total_counts[fruit] += count
print(f" Total counts: {total_counts}")
# ============================================================
# DEFAULTDICT VS REGULAR DICT
# ============================================================
print("\n5. DEFAULTDICT VS REGULAR DICT")
# Without defaultdict (more code)
groups = {}
for fruit, count in fruits:
if fruit not in groups:
groups[fruit] = []
groups[fruit].append(count)
print(f" Regular dict way: {groups}")
# With defaultdict (less code)
groups_dd = defaultdict(list)
for fruit, count in fruits:
groups_dd[fruit].append(count)
print(f" defaultdict way: {groups_dd}")
print(" defaultdict saves you from checking if keys exist!")
defaultdict key points:
- Auto default — no more KeyError
- Default types — int, list, set, str, custom
- Grouping — perfect for grouping items
- Less code — no need to check if key exists
- Default factory — function that creates default values
Quick Check: What happens when you access a missing key in a defaultdict? (Answer: It creates the key with a default value)
OrderedDict - Remember Order
Dictionary That Remembers Insertion Order
OrderedDict is a dictionary that remembers the order in which items were added. In Python 3.7+, regular dictionaries also remember order, but OrderedDict has additional methods.
# OrderedDict - Remember Order
from collections import OrderedDict
print("=" * 50)
print("ORDEREDDICT - REMEMBER ORDER")
print("=" * 50)
# ============================================================
# BASIC ORDEREDDICT
# ============================================================
print("\n1. BASIC ORDEREDDICT")
# Regular dict (Python 3.7+ also remembers order)
regular = {}
regular['first'] = 1
regular['second'] = 2
regular['third'] = 3
print(f" Regular dict: {regular}")
# OrderedDict
ordered = OrderedDict()
ordered['first'] = 1
ordered['second'] = 2
ordered['third'] = 3
print(f" OrderedDict: {ordered}")
# ============================================================
# MOVING ITEMS
# ============================================================
print("\n2. MOVING ITEMS")
ordered = OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
print(f" Original: {ordered}")
# Move to end
ordered.move_to_end('b')
print(f" Move 'b' to end: {ordered}")
# Move to beginning
ordered.move_to_end('b', last=False)
print(f" Move 'b' to start: {ordered}")
# ============================================================
# POPPING ITEMS
# ============================================================
print("\n3. POPPING ITEMS")
ordered = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
# Pop from the end (LIFO)
last = ordered.popitem()
print(f" Popped last: {last}")
print(f" Remaining: {ordered}")
# Pop from the beginning (FIFO)
first = ordered.popitem(last=False)
print(f" Popped first: {first}")
print(f" Remaining: {ordered}")
# ============================================================
# REVERSING ORDER
# ============================================================
print("\n4. REVERSING ORDER")
ordered = OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
print(f" Original: {ordered}")
reversed_od = OrderedDict(reversed(ordered.items()))
print(f" Reversed: {reversed_od}")
# ============================================================
# ORDEREDDICT VS REGULAR DICT
# ============================================================
print("\n5. ORDEREDDICT VS REGULAR DICT")
# Equality check with regular dict
od1 = OrderedDict([('a', 1), ('b', 2)])
od2 = OrderedDict([('b', 2), ('a', 1)])
print(f" OrderedDict 1: {od1}")
print(f" OrderedDict 2: {od2}")
print(f" od1 == od2: {od1 == od2}") # False - order matters!
# Regular dicts are equal regardless of order
d1 = {'a': 1, 'b': 2}
d2 = {'b': 2, 'a': 1}
print(f" Regular dict 1: {d1}")
print(f" Regular dict 2: {d2}")
print(f" d1 == d2: {d1 == d2}") # True - order doesn't matter
OrderedDict key points:
- Remembers order — insertion order is preserved
- move_to_end() — move items to start or end
- popitem() — pop from start or end
- Order matters — equality checks consider order
- Reversed — can be reversed
Quick Check: How do you move an item to the end in OrderedDict? (Answer: ordered.move_to_end(key))
deque - Fast Appends & Pops
Double-Ended Queue - Fast Operations on Both Ends
deque (pronounced "deck") is a double-ended queue. It's like a list but optimized for fast operations at both ends.
# deque - Fast Appends & Pops
from collections import deque
print("=" * 50)
print("DEQUE - FAST APPENDS & POPS")
print("=" * 50)
# ============================================================
# BASIC DEQUE
# ============================================================
print("\n1. BASIC DEQUE")
# Creating a deque
d = deque([1, 2, 3, 4, 5])
print(f" Deque: {d}")
# Append to right (end)
d.append(6)
print(f" Append 6: {d}")
# Append to left (start)
d.appendleft(0)
print(f" Appendleft 0: {d}")
# Pop from right
right = d.pop()
print(f" Pop right: {right}, deque: {d}")
# Pop from left
left = d.popleft()
print(f" Pop left: {left}, deque: {d}")
# ============================================================
# EXTENDING A DEQUE
# ============================================================
print("\n2. EXTENDING A DEQUE")
d = deque([1, 2, 3])
print(f" Original: {d}")
# Extend right
d.extend([4, 5, 6])
print(f" Extend right [4,5,6]: {d}")
# Extend left
d.extendleft([0, -1, -2])
print(f" Extend left [0,-1,-2]: {d}")
# ============================================================
# ROTATING A DEQUE
# ============================================================
print("\n3. ROTATING A DEQUE")
d = deque([1, 2, 3, 4, 5])
print(f" Original: {d}")
# Rotate right (positive steps)
d.rotate(2)
print(f" Rotate right 2: {d}")
# Rotate left (negative steps)
d.rotate(-3)
print(f" Rotate left 3: {d}")
# ============================================================
# MAXLEN - LIMIT SIZE
# ============================================================
print("\n4. MAXLEN - LIMIT SIZE")
# Limit to 3 items
d = deque(maxlen=3)
d.append(1)
d.append(2)
d.append(3)
print(f" Deque (maxlen=3): {d}")
# Adding more removes from the other end
d.append(4)
print(f" After append 4: {d}") # 1 is removed
d.appendleft(0)
print(f" After appendleft 0: {d}") # 3 is removed
# ============================================================
# DEQUE VS LIST PERFORMANCE
# ============================================================
print("\n5. DEQUE VS LIST PERFORMANCE")
import time
# List - inserting at beginning is slow
lst = list(range(10000))
start = time.time()
lst.insert(0, -1)
print(f" List insert at beginning: {time.time() - start:.6f}s")
# Deque - inserting at beginning is fast
dq = deque(range(10000))
start = time.time()
dq.appendleft(-1)
print(f" Deque appendleft: {time.time() - start:.6f}s")
print(" deque is much faster for operations at both ends!")
# ============================================================
# OTHER DEQUE OPERATIONS
# ============================================================
print("\n6. OTHER DEQUE OPERATIONS")
d = deque([1, 2, 3, 4, 5])
print(f" Deque: {d}")
# Count occurrences
print(f" Count of 3: {d.count(3)}")
# Remove first occurrence
d.remove(3)
print(f" Remove 3: {d}")
# Reverse
d.reverse()
print(f" Reversed: {d}")
# Clear
d.clear()
print(f" Cleared: {d}")
deque key points:
- Fast at both ends — O(1) operations
- appendleft/append — add to start/end
- popleft/pop — remove from start/end
- rotate — shift items
- maxlen — limit size
Quick Check: What is deque optimized for? (Answer: Fast operations at both ends of the sequence)
namedtuple - Lightweight Objects
Tuple with Field Names
namedtuple is a tuple subclass that gives you named fields. It's like a lightweight class that doesn't use much memory.
# namedtuple - Lightweight Objects
from collections import namedtuple
print("=" * 50)
print("NAMEDTUPLE - LIGHTWEIGHT OBJECTS")
print("=" * 50)
# ============================================================
# CREATING A NAMEDTUPLE
# ============================================================
print("\n1. CREATING A NAMEDTUPLE")
# Define a namedtuple
Point = namedtuple('Point', ['x', 'y'])
# Create instances
p1 = Point(1, 2)
p2 = Point(x=3, y=4)
print(f" Point 1: {p1}")
print(f" Point 2: {p2}")
# Access by index (like tuple)
print(f" p1[0]: {p1[0]}, p1[1]: {p1[1]}")
# Access by name (like class)
print(f" p1.x: {p1.x}, p1.y: {p1.y}")
# ============================================================
# MORE FIELDS
# ============================================================
print("\n2. MORE FIELDS")
# Define with more fields
Person = namedtuple('Person', ['name', 'age', 'city', 'email'])
person = Person("Alice", 30, "NYC", "alice@example.com")
print(f" Person: {person}")
print(f" Name: {person.name}")
print(f" Age: {person.age}")
print(f" City: {person.city}")
print(f" Email: {person.email}")
# ============================================================
# DEFAULT VALUES
# ============================================================
print("\n3. DEFAULT VALUES")
# With default values (Python 3.7+)
Person = namedtuple('Person', ['name', 'age', 'city'], defaults=['Unknown', 0, 'Unknown'])
p1 = Person("Alice", 30)
p2 = Person("Bob")
p3 = Person("Charlie", 25, "LA")
print(f" Person 1: {p1}")
print(f" Person 2: {p2}")
print(f" Person 3: {p3}")
# ============================================================
# CONVERTING TO DICT AND LIST
# ============================================================
print("\n4. CONVERTING TO DICT AND LIST")
Point = namedtuple('Point', ['x', 'y'])
p = Point(1, 2)
# Convert to dict
p_dict = p._asdict()
print(f" As dict: {p_dict}")
# Convert to list
p_list = list(p)
print(f" As list: {p_list}")
# Convert from dict
p_from_dict = Point(**p_dict)
print(f" From dict: {p_from_dict}")
# ============================================================
# METHODS AND PROPERTIES
# ============================================================
print("\n5. METHODS AND PROPERTIES")
Point = namedtuple('Point', ['x', 'y'])
p = Point(1, 2)
# Get all fields
print(f" Fields: {Point._fields}")
# Create a new instance with a replaced field
p_new = p._replace(x=10)
print(f" Original: {p}")
print(f" Replaced x: {p_new}")
# Create from another tuple
p_from_tuple = Point._make([5, 6])
print(f" From tuple: {p_from_tuple}")
# ============================================================
# NAMEDTUPLE VS CLASS
# ============================================================
print("\n6. NAMEDTUPLE VS CLASS")
# Regular class (more code)
class PointClass:
def __init__(self, x, y):
self.x = x
self.y = y
pc = PointClass(1, 2)
print(f" Class: PointClass(1, 2)")
# namedtuple (less code)
Point = namedtuple('Point', ['x', 'y'])
pn = Point(1, 2)
print(f" namedtuple: {pn}")
print(" namedtuple uses less memory and is faster")
print(" namedtuple is immutable (can't change values)")
print(" Class is mutable and can have methods")
namedtuple key points:
- Named fields — access by name or index
- Lightweight — uses less memory than classes
- Immutable — cannot be changed after creation
- Convertible — to dict, list, and back
- Default values — can set defaults
Quick Check: What's the advantage of namedtuple over a regular tuple? (Answer: You can access fields by name, not just by index)
Real-World Example
Building a Sales Analytics System
# Real-World Example: Sales Analytics System
from collections import Counter, defaultdict, deque, namedtuple, OrderedDict
import random
from datetime import datetime
print("=" * 60)
print("SALES ANALYTICS SYSTEM")
print("=" * 60)
# ============================================================
# DATA TYPES
# ============================================================
# Use namedtuple for sales records
Sale = namedtuple('Sale', ['product', 'category', 'price', 'quantity', 'customer', 'date'])
# Use OrderedDict for monthly reports
MonthlyReport = OrderedDict
# ============================================================
# GENERATE SAMPLE SALES DATA
# ============================================================
products = [
("Laptop", "Electronics", 999.99),
("Phone", "Electronics", 699.99),
("Headphones", "Electronics", 149.99),
("Book", "Books", 29.99),
("Pen", "Stationery", 2.99),
("Notebook", "Stationery", 4.99),
("Desk", "Furniture", 299.99),
("Chair", "Furniture", 199.99)
]
customers = ["Alice", "Bob", "Charlie", "Diana", "Eve", "Frank", "Grace", "Henry"]
def generate_sales(count=50):
"""Generate sample sales data"""
sales = []
for i in range(count):
product, category, price = random.choice(products)
quantity = random.randint(1, 5)
customer = random.choice(customers)
date = datetime(2024, random.randint(1, 12), random.randint(1, 28))
sales.append(Sale(product, category, price, quantity, customer, date))
return sales
sales_data = generate_sales(50)
print(f"\n1. Generated {len(sales_data)} sales records")
# ============================================================
# 1. COUNTER - Product Popularity
# ============================================================
print("\n2. PRODUCT POPULARITY (Counter)")
product_counts = Counter(sale.product for sale in sales_data)
print(" Top 5 products:")
for product, count in product_counts.most_common(5):
print(f" {product}: {count} sales")
# ============================================================
# 2. DEFAULTDICT - Sales by Category
# ============================================================
print("\n3. SALES BY CATEGORY (defaultdict)")
category_sales = defaultdict(list)
for sale in sales_data:
category_sales[sale.category].append(sale)
print(" Sales by category:")
for category, sales in category_sales.items():
total = sum(s.price * s.quantity for s in sales)
print(f" {category}: {len(sales)} sales, ${total:.2f} total")
# ============================================================
# 3. DEFAULTDICT - Customer Spending
# ============================================================
print("\n4. CUSTOMER SPENDING (defaultdict)")
customer_spending = defaultdict(float)
for sale in sales_data:
customer_spending[sale.customer] += sale.price * sale.quantity
print(" Customer spending:")
for customer, total in sorted(customer_spending.items(), key=lambda x: x[1], reverse=True):
print(f" {customer}: ${total:.2f}")
# ============================================================
# 4. DEQUE - Recent Sales
# ============================================================
print("\n5. RECENT SALES (deque)")
recent_sales = deque(maxlen=5)
for sale in sales_data[-10:]: # Last 10 sales
recent_sales.append(sale)
print(" Most recent 5 sales:")
for sale in recent_sales:
print(f" {sale.product} x{sale.quantity} - ${sale.price * sale.quantity:.2f}")
# ============================================================
# 5. ORDEREDDICT - Monthly Report
# ============================================================
print("\n6. MONTHLY REPORT (OrderedDict)")
monthly_report = OrderedDict()
for sale in sales_data:
month = sale.date.strftime("%Y-%m")
if month not in monthly_report:
monthly_report[month] = 0
monthly_report[month] += sale.price * sale.quantity
print(" Monthly revenue:")
for month, revenue in monthly_report.items():
print(f" {month}: ${revenue:.2f}")
# ============================================================
# 6. NAMEDTUPLE - Product Summary
# ============================================================
print("\n7. PRODUCT SUMMARY (namedtuple)")
ProductSummary = namedtuple('ProductSummary', ['name', 'sales_count', 'total_revenue'])
product_summaries = []
for product in set(sale.product for sale in sales_data):
count = sum(1 for s in sales_data if s.product == product)
revenue = sum(s.price * s.quantity for s in sales_data if s.product == product)
product_summaries.append(ProductSummary(product, count, revenue))
print(" Product summaries:")
for summary in sorted(product_summaries, key=lambda x: x.total_revenue, reverse=True)[:5]:
print(f" {summary.name}: {summary.sales_count} sales, ${summary.total_revenue:.2f}")
print("\n" + "=" * 60)
print("KEY TAKEAWAYS:")
print("=" * 60)
print("""
- Counter: Count product popularity
- defaultdict: Group by category and customer
- deque: Keep recent sales
- OrderedDict: Track monthly revenue
- namedtuple: Create lightweight data structures
""")
Real-world example key points:
- Counter — find most popular products
- defaultdict — group sales by category and customer
- deque — keep recent sales
- OrderedDict — track monthly revenue
- namedtuple — create sale records
Quick Check: Which collection type would you use to keep the last 10 sales? (Answer: deque with maxlen=10)
Best Practices
Using Collections Effectively
# Best Practices for Collections
print("=" * 60)
print("BEST PRACTICES FOR COLLECTIONS")
print("=" * 60)
from collections import Counter, defaultdict, OrderedDict, deque, namedtuple
# ============================================================
# 1. CHOOSE THE RIGHT TOOL
# ============================================================
print("\n1. CHOOSE THE RIGHT TOOL")
print("""
Use Counter when:
- You need to count occurrences
- You want most common items
Use defaultdict when:
- You need to group items
- You want to avoid KeyError
Use OrderedDict when:
- Order matters for comparisons
- You need to move items
Use deque when:
- You need fast operations at both ends
- You want a queue or stack
Use namedtuple when:
- You want a lightweight object
- You need named fields
""")
# ============================================================
# 2. USE DEFAULTDICT FOR GROUPING
# ============================================================
print("\n2. USE DEFAULTDICT FOR GROUPING")
# Good - clean and concise
data = [('a', 1), ('b', 2), ('a', 3), ('b', 4), ('c', 5)]
grouped = defaultdict(list)
for key, value in data:
grouped[key].append(value)
print(f" Grouped: {grouped}")
# Bad - manual checking
grouped_bad = {}
for key, value in data:
if key not in grouped_bad:
grouped_bad[key] = []
grouped_bad[key].append(value)
print(f" Grouped (manual): {grouped_bad}")
# ============================================================
# 3. USE DEQUE FOR QUEUES
# ============================================================
print("\n3. USE DEQUE FOR QUEUES")
# Good - fast queue operations
queue = deque()
queue.append("first")
queue.append("second")
queue.append("third")
print(f" Queue: {list(queue)}")
first = queue.popleft()
print(f" Popped: {first}")
print(f" Queue after pop: {list(queue)}")
# Bad - using list for queue (slow)
queue_bad = []
queue_bad.append("first")
queue_bad.append("second")
first = queue_bad.pop(0) # O(n) operation - slow!
# ============================================================
# 4. USE NAMEDTUPLE FOR SIMPLE DATA OBJECTS
# ============================================================
print("\n4. USE NAMEDTUPLE FOR SIMPLE DATA")
# Good - lightweight and clear
Point = namedtuple('Point', ['x', 'y'])
p = Point(1, 2)
print(f" Point: {p.x}, {p.y}")
# Bad - using tuple (hard to read)
p_tuple = (1, 2)
print(f" Tuple: {p_tuple[0]}, {p_tuple[1]}")
# ============================================================
# 5. USE COUNTER FOR FREQUENCY ANALYSIS
# ============================================================
print("\n5. USE COUNTER FOR FREQUENCY")
# Good - clean and efficient
words = "the cat in the hat the cat sat on the mat".split()
freq = Counter(words)
print(f" Frequency: {freq.most_common(2)}")
# Bad - manual counting
freq_bad = {}
for word in words:
freq_bad[word] = freq_bad.get(word, 0) + 1
print(f" Manual: {freq_bad}")
# ============================================================
# 6. SUMMARY
# ============================================================
print("\n" + "=" * 60)
print("BEST PRACTICES SUMMARY")
print("=" * 60)
print("""
- Choose the right collection for your need
- Use defaultdict for grouping
- Use deque for queues and stacks
- Use namedtuple for simple data objects
- Use Counter for counting and frequency
- Use OrderedDict when order matters
""")
Best practices summary:
- Choose right tool — each has a specific use
- defaultdict for grouping — cleaner code
- deque for queues — faster operations
- namedtuple for data — lightweight objects
- Counter for frequency — easiest way to count
- OrderedDict when order matters — explicit ordering
Quick Check: Which collection should you use for a fast queue? (Answer: deque)
Try It Yourself
Experiment with collections in the editor below.
COLLECTIONS - PRACTICE
==================================================
1. COUNTER
Numbers: [1, 2, 3, 2, 1, 3, 4, 5, 1, 2, 3, 1]
Counts: Counter({1: 4, 2: 3, 3: 3, 4: 1, 5: 1})
Most common: [(1, 4), (2, 3)]
2. DEFAULTDICT
Groups: defaultdict(
3. ORDEREDDICT
OrderedDict: OrderedDict([('a', 1), ('b', 2), ('c', 3)])
Move 'a' to end: OrderedDict([('b', 2), ('c', 3), ('a', 1)])
4. DEQUE
Deque: deque([0, 1, 2, 3, 4])
Pop right: 4
Pop left: 0
After pops: deque([1, 2, 3])
5. NAMEDTUPLE
Student 1: Student(name='Alice', grade=95, subject='Math')
Student 2: Student(name='Bob', grade=87, subject='Science')
Alice got 95 in Math
You've Got It!
You now understand the collections module in Python. You know how to use Counter, defaultdict, OrderedDict, deque, and namedtuple.
Quick Quiz
Test what you've learned:
Frequently Asked Questions
What is the collections module in Python?
What's the difference between defaultdict and regular dict?
Is OrderedDict still needed in Python 3.7+?
When should I use deque instead of list?
What's the advantage of namedtuple over a class?
Can I use Counter with custom objects?
Where to Go From Here
Now that you understand the collections module, check out these related topics:
Itertools Module
Learn about advanced iteration tools.
Learn More →Functools Module
Learn about higher-order functions.
Learn More →Datetime Module
Learn about working with dates and times.
Learn More →