Adding and Removing Dictionary Items in Python Guide

Master Python dictionary operations with comprehensive examples of adding and removing items, performance tips, and best practices for developers.

Adding and Removing Dictionary Items in Python

Table of Contents

1. [Introduction to Dictionary Operations](#introduction) 2. [Adding Items to Dictionaries](#adding-items) 3. [Removing Items from Dictionaries](#removing-items) 4. [Advanced Dictionary Operations](#advanced-operations) 5. [Performance Considerations](#performance) 6. [Common Use Cases and Examples](#use-cases) 7. [Best Practices](#best-practices)

Introduction to Dictionary Operations {#introduction}

Python dictionaries are mutable data structures that store key-value pairs. One of their most powerful features is the ability to dynamically add and remove items during program execution. Understanding these operations is crucial for effective Python programming, as dictionaries are frequently used for data storage, caching, configuration management, and many other applications.

Dictionary operations for adding and removing items are fundamental to maintaining dynamic data structures. These operations allow programs to respond to changing requirements, user input, and evolving data sets. The efficiency and flexibility of these operations make dictionaries one of the most versatile data structures in Python.

Adding Items to Dictionaries {#adding-items}

Direct Assignment Method

The most straightforward way to add items to a dictionary is through direct assignment using square bracket notation. This method allows you to add single key-value pairs or modify existing values.

`python

Creating an empty dictionary

student_grades = {}

Adding items using direct assignment

student_grades['Alice'] = 95 student_grades['Bob'] = 87 student_grades['Charlie'] = 92

print(student_grades)

Output: {'Alice': 95, 'Bob': 87, 'Charlie': 92}

Modifying existing values

student_grades['Alice'] = 98 print(student_grades)

Output: {'Alice': 98, 'Bob': 87, 'Charlie': 92}

`

Notes: - Direct assignment overwrites existing values if the key already exists - This method is the most efficient for adding single items - Keys must be immutable types (strings, numbers, tuples)

Using the update() Method

The update() method provides multiple ways to add items to a dictionary. It can accept another dictionary, an iterable of key-value pairs, or keyword arguments.

`python

Starting with a base dictionary

inventory = {'apples': 50, 'bananas': 30}

Method 1: Update with another dictionary

new_items = {'oranges': 25, 'grapes': 40} inventory.update(new_items) print(inventory)

Output: {'apples': 50, 'bananas': 30, 'oranges': 25, 'grapes': 40}

Method 2: Update with keyword arguments

inventory.update(pears=35, mangoes=20) print(inventory)

Output: {'apples': 50, 'bananas': 30, 'oranges': 25, 'grapes': 40, 'pears': 35, 'mangoes': 20}

Method 3: Update with iterable of pairs

additional_items = [('kiwis', 15), ('strawberries', 60)] inventory.update(additional_items) print(inventory)

Output: {'apples': 50, 'bananas': 30, 'oranges': 25, 'grapes': 40, 'pears': 35, 'mangoes': 20, 'kiwis': 15, 'strawberries': 60}

`

Notes: - update() can add multiple items simultaneously - Existing keys will be overwritten with new values - More efficient than multiple direct assignments for bulk operations

Using the setdefault() Method

The setdefault() method adds an item only if the key doesn't already exist. If the key exists, it returns the existing value without modification.

`python

Initialize dictionary

user_preferences = {'theme': 'dark', 'language': 'english'}

Add new key with default value

result1 = user_preferences.setdefault('notifications', True) print(f"Added notifications: {result1}") print(user_preferences)

Output: Added notifications: True

Output: {'theme': 'dark', 'language': 'english', 'notifications': True}

Attempt to add existing key

result2 = user_preferences.setdefault('theme', 'light') print(f"Theme remains: {result2}") print(user_preferences)

Output: Theme remains: dark

Output: {'theme': 'dark', 'language': 'english', 'notifications': True}

`

Notes: - setdefault() prevents accidental overwriting of existing values - Returns the value associated with the key (existing or newly set) - Useful for initializing nested data structures

Dictionary Comprehensions for Adding Items

Dictionary comprehensions provide a concise way to create new dictionaries or add computed items based on existing data.

`python

Create a dictionary with computed values

numbers = [1, 2, 3, 4, 5] squares = {num: num2 for num in numbers} print(squares)

Output: {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

Adding items based on conditions

original_dict = {'a': 1, 'b': 2, 'c': 3, 'd': 4} filtered_and_modified = {k: v*2 for k, v in original_dict.items() if v > 2} print(filtered_and_modified)

Output: {'c': 6, 'd': 8}

Combining with existing dictionary

base_config = {'debug': False, 'version': '1.0'} extended_config = {base_config, {'timeout': 30, 'retries': 3}} print(extended_config)

Output: {'debug': False, 'version': '1.0', 'timeout': 30, 'retries': 3}

`

Removing Items from Dictionaries {#removing-items}

Using the del Statement

The del statement removes items by specifying their keys. It raises a KeyError if the key doesn't exist.

`python

Sample dictionary

product_catalog = { 'laptop': 999.99, 'mouse': 25.50, 'keyboard': 75.00, 'monitor': 299.99, 'speakers': 89.99 }

Remove single item

del product_catalog['speakers'] print(product_catalog)

Output: {'laptop': 999.99, 'mouse': 25.50, 'keyboard': 75.00, 'monitor': 299.99}

Attempting to delete non-existent key raises KeyError

try: del product_catalog['webcam'] except KeyError as e: print(f"Key not found: {e}")

Output: Key not found: 'webcam'

`

Notes: - del permanently removes the key-value pair - Raises KeyError for non-existent keys - Most efficient method for removing known keys

Using the pop() Method

The pop() method removes and returns the value associated with a specified key. It can accept a default value to return if the key doesn't exist.

`python

Sample dictionary

server_config = { 'host': 'localhost', 'port': 8080, 'debug': True, 'ssl': False, 'timeout': 30 }

Remove and get value

removed_debug = server_config.pop('debug') print(f"Removed debug setting: {removed_debug}") print(server_config)

Output: Removed debug setting: True

Output: {'host': 'localhost', 'port': 8080, 'ssl': False, 'timeout': 30}

Pop with default value for non-existent key

max_connections = server_config.pop('max_connections', 100) print(f"Max connections (default): {max_connections}") print(server_config)

Output: Max connections (default): 100

Output: {'host': 'localhost', 'port': 8080, 'ssl': False, 'timeout': 30}

Pop without default for non-existent key raises KeyError

try: server_config.pop('database_url') except KeyError as e: print(f"Key not found: {e}") `

Notes: - pop() returns the removed value, making it useful for processing - Default parameter prevents KeyError exceptions - Combines removal and value retrieval in one operation

Using the popitem() Method

The popitem() method removes and returns an arbitrary key-value pair from the dictionary. In Python 3.7+, it removes the last inserted item due to insertion order preservation.

`python

Sample dictionary

task_queue = { 'task_1': 'process_data', 'task_2': 'send_email', 'task_3': 'backup_files', 'task_4': 'generate_report' }

print("Original queue:", task_queue)

Remove last item (LIFO - Last In, First Out)

removed_task = task_queue.popitem() print(f"Removed task: {removed_task}") print("Updated queue:", task_queue)

Output: Removed task: ('task_4', 'generate_report')

Output: Updated queue: {'task_1': 'process_data', 'task_2': 'send_email', 'task_3': 'backup_files'}

Remove another item

another_task = task_queue.popitem() print(f"Removed task: {another_task}") print("Updated queue:", task_queue)

Output: Removed task: ('task_3', 'backup_files')

Output: Updated queue: {'task_1': 'process_data', 'task_2': 'send_email'}

popitem() on empty dictionary raises KeyError

empty_dict = {} try: empty_dict.popitem() except KeyError: print("Cannot pop from empty dictionary") `

Notes: - popitem() removes the last inserted item in Python 3.7+ - Useful for implementing stack-like behavior (LIFO) - Raises KeyError when called on empty dictionary

Using the clear() Method

The clear() method removes all items from the dictionary, making it empty.

`python

Sample dictionary

cache_data = { 'user_123': {'name': 'John', 'last_login': '2024-01-15'}, 'user_456': {'name': 'Jane', 'last_login': '2024-01-14'}, 'user_789': {'name': 'Bob', 'last_login': '2024-01-13'} }

print("Before clear:", len(cache_data)) print("Cache contents:", cache_data)

Clear all items

cache_data.clear()

print("After clear:", len(cache_data)) print("Cache contents:", cache_data)

Output: After clear: 0

Output: Cache contents: {}

`

Notes: - clear() removes all key-value pairs efficiently - The dictionary object remains the same, only its contents are removed - More efficient than deleting and recreating the dictionary

Advanced Dictionary Operations {#advanced-operations}

Conditional Removal with Dictionary Comprehension

Dictionary comprehensions can be used to create new dictionaries with items removed based on conditions.

`python

Original data

sales_data = { 'January': 15000, 'February': 18000, 'March': 12000, 'April': 22000, 'May': 8000, 'June': 25000 }

Remove months with sales below threshold

threshold = 15000 filtered_sales = {month: sales for month, sales in sales_data.items() if sales >= threshold} print("Sales above threshold:", filtered_sales)

Output: Sales above threshold: {'January': 15000, 'February': 18000, 'April': 22000, 'June': 25000}

Remove items based on key characteristics

user_data = { 'admin_user': {'role': 'admin', 'active': True}, 'guest_user': {'role': 'guest', 'active': False}, 'power_user': {'role': 'power', 'active': True}, 'temp_admin': {'role': 'admin', 'active': False} }

Keep only active users

active_users = {username: data for username, data in user_data.items() if data['active']} print("Active users:", active_users)

Output: Active users: {'admin_user': {'role': 'admin', 'active': True}, 'power_user': {'role': 'power', 'active': True}}

`

Bulk Operations with Multiple Methods

Combining different methods for complex operations on dictionaries.

`python class InventoryManager: def __init__(self): self.inventory = {} def add_items(self, items_dict): """Add multiple items using update()""" self.inventory.update(items_dict) return f"Added {len(items_dict)} items" def remove_items(self, item_names): """Remove multiple items and return removed items""" removed_items = {} for name in item_names: if name in self.inventory: removed_items[name] = self.inventory.pop(name) return removed_items def remove_low_stock(self, threshold): """Remove items below stock threshold""" to_remove = [name for name, quantity in self.inventory.items() if quantity < threshold] return self.remove_items(to_remove) def get_inventory(self): return self.inventory.copy()

Example usage

manager = InventoryManager()

Add initial inventory

initial_stock = { 'apples': 100, 'bananas': 5, 'oranges': 75, 'grapes': 2, 'pears': 50 }

print(manager.add_items(initial_stock)) print("Current inventory:", manager.get_inventory())

Remove low stock items

removed_low_stock = manager.remove_low_stock(10) print("Removed low stock items:", removed_low_stock) print("Updated inventory:", manager.get_inventory())

Remove specific items

specific_removals = manager.remove_items(['apples', 'pears']) print("Specifically removed:", specific_removals) print("Final inventory:", manager.get_inventory()) `

Performance Considerations {#performance}

Time Complexity Analysis

Understanding the performance characteristics of different dictionary operations is crucial for writing efficient code.

| Operation | Method | Time Complexity | Space Complexity | Notes | |-----------|--------|----------------|------------------|-------| | Add single item | dict[key] = value | O(1) average | O(1) | Most efficient for single additions | | Add multiple items | dict.update() | O(n) | O(n) | n = number of items being added | | Remove single item | del dict[key] | O(1) average | O(1) | Fastest removal method | | Remove with return | dict.pop(key) | O(1) average | O(1) | Slightly slower due to return value | | Remove last item | dict.popitem() | O(1) | O(1) | Efficient for LIFO operations | | Clear all items | dict.clear() | O(n) | O(1) | n = number of items in dictionary | | Conditional removal | Dictionary comprehension | O(n) | O(n) | Creates new dictionary |

Performance Comparison Example

`python import time import random

def performance_test(): # Setup test data test_size = 100000 test_dict = {f"key_{i}": random.randint(1, 1000) for i in range(test_size)} # Test direct assignment vs update for adding items new_items = {f"new_key_{i}": random.randint(1, 1000) for i in range(1000)} # Method 1: Direct assignment test_dict_1 = test_dict.copy() start_time = time.time() for key, value in new_items.items(): test_dict_1[key] = value direct_time = time.time() - start_time # Method 2: Update method test_dict_2 = test_dict.copy() start_time = time.time() test_dict_2.update(new_items) update_time = time.time() - start_time print(f"Direct assignment time: {direct_time:.6f} seconds") print(f"Update method time: {update_time:.6f} seconds") print(f"Update is {direct_time/update_time:.2f}x faster") # Test removal methods keys_to_remove = [f"key_{i}" for i in range(0, 1000, 10)] # Method 1: del statement test_dict_3 = test_dict.copy() start_time = time.time() for key in keys_to_remove: if key in test_dict_3: del test_dict_3[key] del_time = time.time() - start_time # Method 2: pop method test_dict_4 = test_dict.copy() start_time = time.time() for key in keys_to_remove: test_dict_4.pop(key, None) pop_time = time.time() - start_time print(f"Del statement time: {del_time:.6f} seconds") print(f"Pop method time: {pop_time:.6f} seconds") print(f"Del is {pop_time/del_time:.2f}x faster than pop")

Run performance test

performance_test() `

Common Use Cases and Examples {#use-cases}

Configuration Management

Dictionaries are commonly used for managing application configurations, where settings need to be added, modified, or removed dynamically.

`python class ConfigManager: def __init__(self): self.config = { 'database': { 'host': 'localhost', 'port': 5432, 'name': 'myapp' }, 'cache': { 'enabled': True, 'ttl': 3600 }, 'logging': { 'level': 'INFO', 'file': '/var/log/app.log' } } def set_config(self, path, value): """Set configuration value using dot notation path""" keys = path.split('.') current = self.config # Navigate to the parent of target key for key in keys[:-1]: current = current.setdefault(key, {}) # Set the final value current[keys[-1]] = value def remove_config(self, path): """Remove configuration value using dot notation path""" keys = path.split('.') current = self.config try: # Navigate to the parent of target key for key in keys[:-1]: current = current[key] # Remove the final key removed_value = current.pop(keys[-1]) return removed_value except KeyError: return None def get_config(self, path=None): """Get configuration value or entire config""" if path is None: return self.config keys = path.split('.') current = self.config try: for key in keys: current = current[key] return current except KeyError: return None def merge_config(self, new_config): """Merge new configuration with existing""" def deep_merge(base, update): for key, value in update.items(): if key in base and isinstance(base[key], dict) and isinstance(value, dict): deep_merge(base[key], value) else: base[key] = value deep_merge(self.config, new_config)

Example usage

config_manager = ConfigManager()

Add new configuration

config_manager.set_config('api.timeout', 30) config_manager.set_config('api.retries', 3)

Merge additional configuration

additional_config = { 'database': { 'pool_size': 10 }, 'security': { 'encryption': True, 'key_rotation': 86400 } } config_manager.merge_config(additional_config)

print("Updated configuration:") print(config_manager.get_config())

Remove configuration

removed = config_manager.remove_config('cache.ttl') print(f"Removed cache.ttl: {removed}") print("Final configuration:") print(config_manager.get_config()) `

Caching System

Implementing a simple caching system that adds and removes items based on usage patterns.

`python import time from collections import OrderedDict

class LRUCache: def __init__(self, capacity): self.capacity = capacity self.cache = OrderedDict() self.access_times = {} def get(self, key): """Get item from cache and update access time""" if key in self.cache: # Move to end (most recently used) value = self.cache.pop(key) self.cache[key] = value self.access_times[key] = time.time() return value return None def put(self, key, value): """Add item to cache, removing oldest if necessary""" if key in self.cache: # Update existing item self.cache.pop(key) elif len(self.cache) >= self.capacity: # Remove least recently used item oldest_key = next(iter(self.cache)) self.cache.pop(oldest_key) self.access_times.pop(oldest_key, None) # Add new item self.cache[key] = value self.access_times[key] = time.time() def remove(self, key): """Manually remove item from cache""" removed_value = self.cache.pop(key, None) self.access_times.pop(key, None) return removed_value def clear_expired(self, max_age_seconds): """Remove items older than specified age""" current_time = time.time() expired_keys = [ key for key, access_time in self.access_times.items() if current_time - access_time > max_age_seconds ] removed_items = {} for key in expired_keys: removed_items[key] = self.remove(key) return removed_items def get_stats(self): """Get cache statistics""" return { 'size': len(self.cache), 'capacity': self.capacity, 'items': list(self.cache.keys()) }

Example usage

cache = LRUCache(capacity=3)

Add items

cache.put('user_1', {'name': 'Alice', 'role': 'admin'}) cache.put('user_2', {'name': 'Bob', 'role': 'user'}) cache.put('user_3', {'name': 'Charlie', 'role': 'user'})

print("Cache after adding 3 items:", cache.get_stats())

Add fourth item (should remove oldest)

cache.put('user_4', {'name': 'Diana', 'role': 'admin'}) print("Cache after adding 4th item:", cache.get_stats())

Access an item (moves to end)

user_data = cache.get('user_2') print(f"Retrieved user_2: {user_data}") print("Cache after accessing user_2:", cache.get_stats())

Simulate time passage and clear expired items

time.sleep(1) expired = cache.clear_expired(0.5) print(f"Expired items: {expired}") print("Cache after clearing expired:", cache.get_stats()) `

Data Processing Pipeline

Using dictionaries for data transformation where items are added, processed, and removed based on business logic.

`python class DataProcessor: def __init__(self): self.raw_data = {} self.processed_data = {} self.failed_data = {} self.processing_stats = { 'total_received': 0, 'successfully_processed': 0, 'failed_processing': 0 } def add_raw_data(self, data_id, data): """Add raw data for processing""" self.raw_data[data_id] = { 'data': data, 'timestamp': time.time(), 'attempts': 0 } self.processing_stats['total_received'] += 1 def process_item(self, data_id): """Process a single data item""" if data_id not in self.raw_data: return False item = self.raw_data[data_id] item['attempts'] += 1 try: # Simulate processing logic raw_value = item['data'] # Example processing: convert to uppercase if string, multiply by 2 if number if isinstance(raw_value, str): processed_value = raw_value.upper() elif isinstance(raw_value, (int, float)): processed_value = raw_value * 2 else: raise ValueError(f"Unsupported data type: {type(raw_value)}") # Move to processed data self.processed_data[data_id] = { 'original': raw_value, 'processed': processed_value, 'processed_at': time.time(), 'attempts': item['attempts'] } # Remove from raw data self.raw_data.pop(data_id) self.processing_stats['successfully_processed'] += 1 return True except Exception as e: if item['attempts'] >= 3: # Move to failed data after 3 attempts self.failed_data[data_id] = { 'data': item['data'], 'error': str(e), 'attempts': item['attempts'], 'failed_at': time.time() } self.raw_data.pop(data_id) self.processing_stats['failed_processing'] += 1 return False def process_all_pending(self): """Process all items in raw_data queue""" pending_ids = list(self.raw_data.keys()) results = {} for data_id in pending_ids: results[data_id] = self.process_item(data_id) return results def remove_old_processed(self, max_age_seconds): """Remove processed items older than specified age""" current_time = time.time() old_items = {} for data_id, item in list(self.processed_data.items()): if current_time - item['processed_at'] > max_age_seconds: old_items[data_id] = self.processed_data.pop(data_id) return old_items def get_status(self): """Get current processing status""" return { 'stats': self.processing_stats.copy(), 'queues': { 'raw_data': len(self.raw_data), 'processed_data': len(self.processed_data), 'failed_data': len(self.failed_data) } } def clear_failed_data(self): """Clear all failed data items""" cleared_count = len(self.failed_data) self.failed_data.clear() return cleared_count

Example usage

processor = DataProcessor()

Add various types of data

test_data = { 'item_1': 'hello world', 'item_2': 42, 'item_3': 3.14, 'item_4': 'python programming', 'item_5': [1, 2, 3], # This will fail processing 'item_6': 'data processing' }

for data_id, data in test_data.items(): processor.add_raw_data(data_id, data)

print("Status after adding data:", processor.get_status())

Process all items

processing_results = processor.process_all_pending() print("Processing results:", processing_results) print("Status after processing:", processor.get_status())

Show processed data

print("\nProcessed data:") for data_id, item in processor.processed_data.items(): print(f" {data_id}: {item['original']} -> {item['processed']}")

Show failed data

print("\nFailed data:") for data_id, item in processor.failed_data.items(): print(f" {data_id}: {item['data']} (Error: {item['error']})")

Clean up old processed data

time.sleep(1) old_items = processor.remove_old_processed(0.5) print(f"\nRemoved {len(old_items)} old processed items")

Clear failed data

cleared_count = processor.clear_failed_data() print(f"Cleared {cleared_count} failed items")

print("Final status:", processor.get_status()) `

Best Practices {#best-practices}

Error Handling and Safety

When working with dictionary operations, proper error handling is essential to prevent runtime errors and ensure robust code.

`python class SafeDictionaryOperations: @staticmethod def safe_add(dictionary, key, value, overwrite=True): """Safely add item to dictionary with optional overwrite protection""" if not overwrite and key in dictionary: return False, f"Key '{key}' already exists" try: dictionary[key] = value return True, f"Successfully added '{key}'" except Exception as e: return False, f"Error adding '{key}': {str(e)}" @staticmethod def safe_remove(dictionary, key, default=None): """Safely remove item from dictionary with default return""" try: return True, dictionary.pop(key) except KeyError: if default is not None: return False, default return False, f"Key '{key}' not found" @staticmethod def safe_bulk_update(dictionary, updates, validate_keys=True): """Safely update dictionary with validation""" if not isinstance(updates, dict): return False, "Updates must be a dictionary" if validate_keys: # Validate all keys are hashable for key in updates.keys(): try: hash(key) except TypeError: return False, f"Key '{key}' is not hashable" try: original_size = len(dictionary) dictionary.update(updates) new_size = len(dictionary) return True, f"Updated dictionary: {new_size - original_size} new keys added" except Exception as e: return False, f"Error during bulk update: {str(e)}" @staticmethod def safe_clear_with_backup(dictionary): """Clear dictionary with backup option""" backup = dictionary.copy() dictionary.clear() return backup

Example usage of safe operations

test_dict = {'existing_key': 'existing_value'}

Safe adding

success, message = SafeDictionaryOperations.safe_add(test_dict, 'new_key', 'new_value') print(f"Add result: {success}, Message: {message}")

Safe adding with overwrite protection

success, message = SafeDictionaryOperations.safe_add(test_dict, 'existing_key', 'updated_value', overwrite=False) print(f"Protected add result: {success}, Message: {message}")

Safe removal

success, value = SafeDictionaryOperations.safe_remove(test_dict, 'nonexistent_key', 'default_value') print(f"Remove result: {success}, Value: {value}")

Safe bulk update

updates = {'key1': 'value1', 'key2': 'value2'} success, message = SafeDictionaryOperations.safe_bulk_update(test_dict, updates) print(f"Bulk update result: {success}, Message: {message}")

print("Final dictionary:", test_dict) `

Memory Management and Optimization

Efficient memory usage is important when working with large dictionaries or in memory-constrained environments.

`python import sys from collections import defaultdict

class OptimizedDictionaryManager: def __init__(self): self.data = {} self.memory_threshold = 1000000 # 1MB threshold def get_memory_usage(self): """Calculate approximate memory usage of the dictionary""" return sys.getsizeof(self.data) + sum( sys.getsizeof(key) + sys.getsizeof(value) for key, value in self.data.items() ) def add_with_memory_check(self, key, value): """Add item with memory usage monitoring""" # Estimate memory impact estimated_size = sys.getsizeof(key) + sys.getsizeof(value) current_usage = self.get_memory_usage() if current_usage + estimated_size > self.memory_threshold: # Trigger cleanup before adding self.cleanup_old_entries() self.data[key] = value return current_usage + estimated_size def cleanup_old_entries(self, keep_ratio=0.7): """Remove oldest entries to free memory""" current_size = len(self.data) target_size = int(current_size * keep_ratio) if current_size <= target_size: return 0 # Remove oldest entries (assuming insertion order matters) items_to_remove = current_size - target_size keys_to_remove = list(self.data.keys())[:items_to_remove] removed_count = 0 for key in keys_to_remove: self.data.pop(key, None) removed_count += 1 return removed_count def batch_remove_by_pattern(self, pattern_func): """Remove items based on pattern matching function""" keys_to_remove = [ key for key in self.data.keys() if pattern_func(key, self.data[key]) ] removed_items = {} for key in keys_to_remove: removed_items[key] = self.data.pop(key) return removed_items def get_statistics(self): """Get detailed statistics about the dictionary""" return { 'item_count': len(self.data), 'memory_usage_bytes': self.get_memory_usage(), 'memory_usage_mb': self.get_memory_usage() / (1024 * 1024), 'average_key_size': sum(sys.getsizeof(k) for k in self.data.keys()) / len(self.data) if self.data else 0, 'average_value_size': sum(sys.getsizeof(v) for v in self.data.values()) / len(self.data) if self.data else 0 }

Example usage

manager = OptimizedDictionaryManager()

Add many items to test memory management

for i in range(1000): key = f"item_{i:04d}" value = f"data_{'x' * (i % 100)}" # Variable length values memory_used = manager.add_with_memory_check(key, value) if i % 200 == 0: stats = manager.get_statistics() print(f"After {i+1} items: {stats['item_count']} items, {stats['memory_usage_mb']:.2f} MB")

Remove items based on pattern

def remove_pattern(key, value): # Remove items where key ends with even numbers return key.endswith(('0', '2', '4', '6', '8'))

removed_items = manager.batch_remove_by_pattern(remove_pattern) print(f"Removed {len(removed_items)} items matching pattern")

Final statistics

final_stats = manager.get_statistics() print("Final statistics:", final_stats) `

Thread Safety Considerations

When working with dictionaries in multi-threaded environments, proper synchronization is crucial.

`python import threading import time from collections import defaultdict from contextlib import contextmanager

class ThreadSafeDictionary: def __init__(self): self._data = {} self._lock = threading.RLock() # Reentrant lock self._operation_stats = defaultdict(int) @contextmanager def _thread_safe_operation(self, operation_name): """Context manager for thread-safe operations""" with self._lock: start_time = time.time() try: yield self._operation_stats[f"{operation_name}_success"] += 1 except Exception as e: self._operation_stats[f"{operation_name}_error"] += 1 raise finally: duration = time.time() - start_time self._operation_stats[f"{operation_name}_total_time"] += duration def set_item(self, key, value): """Thread-safe item setting""" with self._thread_safe_operation("set_item"): self._data[key] = value return True def get_item(self, key, default=None): """Thread-safe item retrieval""" with self._thread_safe_operation("get_item"): return self._data.get(key, default) def remove_item(self, key): """Thread-safe item removal""" with self._thread_safe_operation("remove_item"): return self._data.pop(key, None) def bulk_update(self, items): """Thread-safe bulk update""" with self._thread_safe_operation("bulk_update"): self._data.update(items) return len(items) def clear_all(self): """Thread-safe clear operation""" with self._thread_safe_operation("clear_all"): count = len(self._data) self._data.clear() return count def get_snapshot(self): """Get thread-safe snapshot of current data""" with self._thread_safe_operation("get_snapshot"): return self._data.copy() def get_stats(self): """Get operation statistics""" with self._lock: return dict(self._operation_stats) def atomic_increment(self, key, increment=1): """Atomically increment a numeric value""" with self._thread_safe_operation("atomic_increment"): current_value = self._data.get(key, 0) new_value = current_value + increment self._data[key] = new_value return new_value

Example usage with multiple threads

def worker_thread(thread_id, shared_dict, operations_per_thread): """Worker function for testing thread safety""" for i in range(operations_per_thread): # Mix of different operations key = f"thread_{thread_id}_item_{i}" # Add item shared_dict.set_item(key, f"value_{i}") # Increment counter shared_dict.atomic_increment(f"thread_{thread_id}_counter") # Sometimes remove items if i % 10 == 0 and i > 0: remove_key = f"thread_{thread_id}_item_{i-5}" shared_dict.remove_item(remove_key) # Brief pause to simulate real work time.sleep(0.001)

Test thread safety

print("Testing thread-safe dictionary operations...") shared_dict = ThreadSafeDictionary()

Create multiple threads

threads = [] num_threads = 5 operations_per_thread = 50

start_time = time.time()

for thread_id in range(num_threads): thread = threading.Thread( target=worker_thread, args=(thread_id, shared_dict, operations_per_thread) ) threads.append(thread) thread.start()

Wait for all threads to complete

for thread in threads: thread.join()

end_time = time.time()

Get final results

final_snapshot = shared_dict.get_snapshot() operation_stats = shared_dict.get_stats()

print(f"Completed in {end_time - start_time:.2f} seconds") print(f"Final dictionary size: {len(final_snapshot)}") print("Operation statistics:") for operation, count in operation_stats.items(): if operation.endswith('_success'): print(f" {operation}: {count}")

Show counter values

counter_items = {k: v for k, v in final_snapshot.items() if 'counter' in k} print("Thread counters:", counter_items) `

This comprehensive guide covers the essential aspects of adding and removing dictionary items in Python, providing practical examples, performance considerations, and best practices for real-world applications. The examples demonstrate various scenarios from simple operations to complex, thread-safe implementations suitable for production environments.

Tags

  • Python
  • data-structures
  • dictionaries
  • programming fundamentals

Related Articles

Related Books - Expand Your Knowledge

Explore these Python books to deepen your understanding:

Browse all IT books

Popular Technical Articles & Tutorials

Explore our comprehensive collection of technical articles, programming tutorials, and IT guides written by industry experts:

Browse all 8+ technical articles | Read our IT blog

Adding and Removing Dictionary Items in Python Guide