Skip to content

Latest commit

 

History

History
566 lines (442 loc) · 19.1 KB

File metadata and controls

566 lines (442 loc) · 19.1 KB

Enhancing Python Readability with Set-Based Membership Checks

Slide 1: Introduction to Set-Based Membership Checks

Set-based membership checking in Python provides a more elegant and performant alternative to multiple OR conditions. This fundamental technique leverages Python's built-in set data structure, which offers constant-time O(1) lookup operations, making it significantly faster than sequential comparisons.

# Traditional OR-based approach vs Set-based approach
def check_membership_comparison():
    value = 5
    # Multiple OR conditions - less efficient
    if value == 1 or value == 2 or value == 3 or value == 4 or value == 5:
        return "Found using OR"
    
    # Set-based approach - more efficient
    valid_numbers = {1, 2, 3, 4, 5}
    if value in valid_numbers:
        return "Found using set"

print(check_membership_comparison())  # Output: Found using set

Slide 2: Performance Analysis of Set vs OR Operations

Understanding the performance implications between set-based and OR-based approaches requires measuring execution time for different input sizes. Sets maintain constant lookup time regardless of size, while OR conditions scale linearly with the number of comparisons.

import time
import random

def compare_performance(size):
    # Generate test data
    numbers = list(range(size))
    search_value = random.randint(0, size-1)
    
    # Test OR conditions
    start_time = time.perf_counter()
    result = any(search_value == x for x in numbers)
    or_time = time.perf_counter() - start_time
    
    # Test set membership
    number_set = set(numbers)
    start_time = time.perf_counter()
    result = search_value in number_set
    set_time = time.perf_counter() - start_time
    
    return or_time, set_time

# Test with different sizes
sizes = [100, 1000, 10000]
for size in sizes:
    or_time, set_time = compare_performance(size)
    print(f"Size {size:6d}: OR: {or_time:.6f}s, Set: {set_time:.6f}s")

Slide 3: String Pattern Matching Using Sets

Set-based matching excels in scenarios involving string pattern recognition, particularly when dealing with character classifications or allowed sequences. This approach simplifies complex string validation logic while maintaining high performance.

def validate_password_chars(password):
    # Define valid character sets
    lowercase = set('abcdefghijklmnopqrstuvwxyz')
    uppercase = set('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
    digits = set('0123456789')
    special = set('!@#$%^&*()_+-=[]{}|;:,.<>?')
    
    # Convert password to set for efficient checking
    password_chars = set(password)
    
    # Validate using set operations
    has_lower = bool(password_chars & lowercase)
    has_upper = bool(password_chars & uppercase)
    has_digit = bool(password_chars & digits)
    has_special = bool(password_chars & special)
    
    return all([has_lower, has_upper, has_digit, has_special])

# Test the validator
test_password = "Secure123!"
print(f"Password valid: {validate_password_chars(test_password)}")  # Output: True

Slide 4: Dynamic Set Operations in Data Processing

When processing data streams or dynamic collections, set operations provide efficient mechanisms for filtering and transformation. This approach is particularly valuable when dealing with large datasets requiring frequent membership checks.

class DataStreamProcessor:
    def __init__(self):
        self.valid_categories = {'A', 'B', 'C'}
        self.processed_ids = set()
        
    def process_data_point(self, data_point):
        item_id = data_point['id']
        category = data_point['category']
        
        # Efficient checks using set operations
        if (item_id not in self.processed_ids and 
            category in self.valid_categories):
            self.processed_ids.add(item_id)
            return True
        return False

# Example usage
processor = DataStreamProcessor()
data_stream = [
    {'id': 1, 'category': 'A'},
    {'id': 2, 'category': 'D'},
    {'id': 1, 'category': 'A'}  # Duplicate
]

for data in data_stream:
    result = processor.process_data_point(data)
    print(f"Processed {data}: {result}")

Slide 5: Set-Based State Machine Implementation

Modern state machines can leverage sets for efficient state transition validation and processing. This implementation demonstrates how sets simplify complex state management while maintaining clear and maintainable code.

class StateMachine:
    def __init__(self):
        self.valid_states = {'INIT', 'PROCESSING', 'COMPLETE', 'ERROR'}
        self.valid_transitions = {
            'INIT': {'PROCESSING'},
            'PROCESSING': {'COMPLETE', 'ERROR'},
            'COMPLETE': {'INIT'},
            'ERROR': {'INIT'}
        }
        self.current_state = 'INIT'
    
    def transition(self, new_state):
        if (new_state in self.valid_states and 
            new_state in self.valid_transitions[self.current_state]):
            self.current_state = new_state
            return True
        return False

# Example usage
state_machine = StateMachine()
transitions = [
    'PROCESSING',
    'COMPLETE',
    'INVALID',
    'INIT'
]

for new_state in transitions:
    success = state_machine.transition(new_state)
    print(f"Transition to {new_state}: {'Success' if success else 'Failed'}")

Slide 6: Set-Based Data Deduplication

Set operations excel at removing duplicates from complex data structures while preserving data integrity. This implementation showcases how sets can efficiently handle deduplication of nested data structures with multiple attributes.

class DataDeduplicator:
    def __init__(self):
        self.seen_items = set()
    
    def deduplicate_records(self, records):
        cleaned_records = []
        
        for record in records:
            # Create immutable representation for set storage
            record_key = tuple(sorted(record.items()))
            
            if record_key not in self.seen_items:
                self.seen_items.add(record_key)
                cleaned_records.append(dict(record_key))
        
        return cleaned_records

# Example usage
data = [
    {'id': 1, 'name': 'John', 'age': 30},
    {'id': 2, 'name': 'Jane', 'age': 25},
    {'id': 1, 'name': 'John', 'age': 30},  # Duplicate
    {'id': 3, 'name': 'Bob', 'age': 35}
]

deduplicator = DataDeduplicator()
unique_records = deduplicator.deduplicate_records(data)
print(f"Original records: {len(data)}")
print(f"Unique records: {len(unique_records)}")

Slide 7: Set-Based Event Handler Registry

Event-driven systems benefit from set-based callback registration, providing efficient management of event handlers while preventing duplicate registrations and enabling quick event dispatch.

class EventSystem:
    def __init__(self):
        self.event_handlers = {}
    
    def register_handler(self, event_type, handler):
        if event_type not in self.event_handlers:
            self.event_handlers[event_type] = set()
        self.event_handlers[event_type].add(handler)
    
    def unregister_handler(self, event_type, handler):
        if event_type in self.event_handlers:
            self.event_handlers[event_type].discard(handler)
    
    def trigger_event(self, event_type, event_data):
        if event_type in self.event_handlers:
            for handler in self.event_handlers[event_type]:
                handler(event_data)

# Example usage
def log_event(data):
    print(f"Logged: {data}")

def process_event(data):
    print(f"Processed: {data}")

event_system = EventSystem()
event_system.register_handler('user_login', log_event)
event_system.register_handler('user_login', process_event)
event_system.trigger_event('user_login', {'user_id': 123, 'timestamp': '2024-12-11'})

Slide 8: Advanced Set-Based Permission System

A robust permission system implementation using sets demonstrates the power of set operations for complex authorization logic, supporting hierarchical permissions and efficient permission checking.

class PermissionSystem:
    def __init__(self):
        self.role_permissions = {
            'admin': {'read', 'write', 'delete', 'manage_users'},
            'editor': {'read', 'write'},
            'viewer': {'read'}
        }
        self.user_roles = {}
    
    def assign_role(self, user_id, role):
        if role in self.role_permissions:
            self.user_roles[user_id] = role
    
    def check_permission(self, user_id, required_permission):
        if user_id not in self.user_roles:
            return False
        
        user_role = self.user_roles[user_id]
        return required_permission in self.role_permissions[user_role]
    
    def get_user_permissions(self, user_id):
        if user_id not in self.user_roles:
            return set()
        return self.role_permissions[self.user_roles[user_id]]

# Example usage
perm_system = PermissionSystem()
perm_system.assign_role(1, 'admin')
perm_system.assign_role(2, 'editor')

print(f"Admin permissions: {perm_system.get_user_permissions(1)}")
print(f"Editor can write: {perm_system.check_permission(2, 'write')}")
print(f"Editor can delete: {perm_system.check_permission(2, 'delete')}")

Slide 9: Set-Based Cache Implementation

This implementation showcases an efficient caching system using sets to track and manage cached items, demonstrating advanced memory management and optimization techniques.

class SetBasedCache:
    def __init__(self, max_size=1000):
        self.max_size = max_size
        self.cache = {}
        self.access_history = set()
        self.item_timestamps = {}
    
    def add_item(self, key, value):
        if len(self.cache) >= self.max_size:
            self._evict_oldest()
        
        self.cache[key] = value
        self.access_history.add(key)
        self.item_timestamps[key] = time.time()
    
    def get_item(self, key):
        if key in self.cache:
            self.access_history.add(key)
            self.item_timestamps[key] = time.time()
            return self.cache[key]
        return None
    
    def _evict_oldest(self):
        if not self.item_timestamps:
            return
        
        oldest_key = min(self.item_timestamps, key=self.item_timestamps.get)
        del self.cache[oldest_key]
        del self.item_timestamps[oldest_key]
        self.access_history.discard(oldest_key)

# Example usage
cache = SetBasedCache(max_size=2)
cache.add_item('key1', 'value1')
cache.add_item('key2', 'value2')
cache.add_item('key3', 'value3')  # This will evict the oldest item

print(f"key1 in cache: {cache.get_item('key1')}")
print(f"key3 in cache: {cache.get_item('key3')}")

Slide 10: Set-Based Data Validation Engine

A comprehensive data validation system utilizing sets for efficient rule checking and constraint validation. This implementation demonstrates how sets can simplify complex validation logic while maintaining high performance.

class DataValidator:
    def __init__(self):
        self.required_fields = {'name', 'email', 'age'}
        self.valid_domains = {'gmail.com', 'yahoo.com', 'hotmail.com'}
        self.validation_errors = set()
    
    def validate_record(self, record):
        self.validation_errors.clear()
        record_fields = set(record.keys())
        
        # Check missing required fields
        missing_fields = self.required_fields - record_fields
        if missing_fields:
            self.validation_errors.add(f"Missing fields: {missing_fields}")
        
        # Validate email domain
        if 'email' in record:
            domain = record['email'].split('@')[-1]
            if domain not in self.valid_domains:
                self.validation_errors.add(f"Invalid email domain: {domain}")
        
        # Validate age
        if 'age' in record:
            if not isinstance(record['age'], int) or record['age'] < 0:
                self.validation_errors.add("Invalid age value")
        
        return len(self.validation_errors) == 0
    
    def get_errors(self):
        return self.validation_errors

# Example usage
validator = DataValidator()
test_records = [
    {'name': 'John', 'email': 'john@gmail.com', 'age': 30},
    {'name': 'Jane', 'email': 'jane@invalid.com', 'age': -5},
    {'email': 'bob@yahoo.com', 'age': 25}
]

for record in test_records:
    is_valid = validator.validate_record(record)
    print(f"\nValidating record: {record}")
    print(f"Valid: {is_valid}")
    if not is_valid:
        print(f"Errors: {validator.get_errors()}")

Slide 11: Set-Based Graph Implementation

An efficient graph representation using sets for storing edges and performing common graph operations. This implementation shows how sets can simplify graph algorithms while maintaining optimal performance.

class SetGraph:
    def __init__(self):
        self.graph = {}
    
    def add_vertex(self, vertex):
        if vertex not in self.graph:
            self.graph[vertex] = set()
    
    def add_edge(self, vertex1, vertex2):
        self.add_vertex(vertex1)
        self.add_vertex(vertex2)
        self.graph[vertex1].add(vertex2)
        self.graph[vertex2].add(vertex1)
    
    def get_neighbors(self, vertex):
        return self.graph.get(vertex, set())
    
    def find_path(self, start, end, path=None):
        if path is None:
            path = set()
        
        path.add(start)
        if start == end:
            return path
        
        for neighbor in self.get_neighbors(start):
            if neighbor not in path:
                new_path = self.find_path(neighbor, end, path.copy())
                if new_path:
                    return new_path
        return None

# Example usage
graph = SetGraph()
edges = [('A', 'B'), ('B', 'C'), ('C', 'D'), ('A', 'D')]
for v1, v2 in edges:
    graph.add_edge(v1, v2)

path = graph.find_path('A', 'C')
print(f"Path from A to C: {path}")
print(f"Neighbors of B: {graph.get_neighbors('B')}")

Slide 12: Set-Based Text Analysis Engine

A sophisticated text analysis system leveraging sets for efficient word frequency analysis and pattern matching, demonstrating advanced text processing capabilities.

class TextAnalyzer:
    def __init__(self):
        self.stop_words = {'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at'}
        self.punctuation = set('.,!?;:()[]{}')
    
    def analyze_text(self, text):
        # Convert text to lowercase and split into words
        words = text.lower().split()
        
        # Remove punctuation and stop words
        clean_words = set()
        for word in words:
            word = ''.join(char for char in word if char not in self.punctuation)
            if word and word not in self.stop_words:
                clean_words.add(word)
        
        # Calculate word frequencies
        word_freq = {}
        for word in clean_words:
            word_freq[word] = text.lower().count(word)
        
        return {
            'unique_words': len(clean_words),
            'word_frequencies': word_freq,
            'top_words': sorted(word_freq.items(), key=lambda x: x[1], reverse=True)[:5]
        }

# Example usage
analyzer = TextAnalyzer()
sample_text = """
The quick brown fox jumps over the lazy dog.
The fox was quick and the dog was lazy!
"""

results = analyzer.analyze_text(sample_text)
print(f"Unique words: {results['unique_words']}")
print(f"Top words: {results['top_words']}")

Slide 13: Set-Based Custom Iterator Implementation

This implementation showcases an advanced iterator pattern using sets for maintaining state and ensuring unique element traversal, demonstrating efficient memory usage and iteration control.

class UniqueIterator:
    def __init__(self, data):
        self.data = set(data)  # Convert input to set for uniqueness
        self.processed = set()
        self._current = None
    
    def __iter__(self):
        return self
    
    def __next__(self):
        remaining = self.data - self.processed
        if not remaining:
            raise StopIteration
        
        self._current = min(remaining)  # Get smallest remaining element
        self.processed.add(self._current)
        return self._current
    
    def peek(self):
        """Preview next item without advancing iterator"""
        remaining = self.data - self.processed
        return min(remaining) if remaining else None
    
    def reset(self):
        """Reset iterator state"""
        self.processed.clear()
        self._current = None

# Example usage
iterator = UniqueIterator([3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5])
print("Iterating through unique elements:")
for num in iterator:
    print(f"Current: {num}")

iterator.reset()
print("\nAfter reset:")
print(f"Next element (peek): {iterator.peek()}")

Slide 14: Performance Benchmarking Results

A comprehensive analysis of the performance benefits when using set-based operations compared to traditional approaches, with real-world benchmarking data and metrics.

import timeit
import random
import statistics

def benchmark_set_vs_list():
    # Setup test data
    data_sizes = [100, 1000, 10000]
    results = {'set': {}, 'list': {}}
    
    for size in data_sizes:
        # Generate test data
        test_data = list(range(size))
        test_set = set(test_data)
        search_value = size - 1  # Worst case scenario
        
        # Benchmark set membership
        set_time = timeit.timeit(
            lambda: search_value in test_set,
            number=10000
        )
        
        # Benchmark list membership
        list_time = timeit.timeit(
            lambda: search_value in test_data,
            number=10000
        )
        
        results['set'][size] = set_time
        results['list'][size] = list_time
    
    return results

# Run benchmarks
results = benchmark_set_vs_list()

# Display results
print("Performance Comparison (lower is better):")
print("\nTime taken for 10,000 lookups (seconds):")
for size in results['set'].keys():
    set_time = results['set'][size]
    list_time = results['list'][size]
    speedup = list_time / set_time
    print(f"\nData size: {size}")
    print(f"Set:  {set_time:.6f}")
    print(f"List: {list_time:.6f}")
    print(f"Speedup factor: {speedup:.2f}x")

Slide 15: Additional Resources