xbe.at

Python / Python for ML and AI

Python Enums Keys Values and Items with LRU Cache

9 min read, 14 sections

Contents
  1. Introduction to Python Enums
  2. Custom Values in Enums
  3. Enum Methods and Properties
  4. Using keys(), values(), and items() with Enums
  5. Implementing LRU Cache with Enum Methods
  6. Real-World Example - State Machine Implementation
  7. Extending the State Machine Example
  8. Advanced Enum Features with Caching
  9. Performance Optimization with Cached Enum Methods
  10. Real-World Implementation - Role-Based Access Control
  11. Caching Enum Calculations in Memory-Sensitive Environments
  12. Advanced Enum Inheritance with Cached Properties
  13. Real-World Example - Configuration Management System
  14. Additional Resources

Introduction to Python Enums

Enumerations in Python provide a way to create a set of symbolic names bound to unique constant values. They help prevent bugs by ensuring type safety and providing meaningful names for discrete values rather than using magic numbers or strings.

from enum import Enum, auto

class Status(Enum):
    PENDING = auto()    # Automatically assigns incremental values
    RUNNING = auto()
    COMPLETED = auto()
    FAILED = auto()

# Usage example
current_status = Status.PENDING
print(f"Status: {current_status}")        # Output: Status: Status.PENDING
print(f"Name: {current_status.name}")     # Output: Name: PENDING
print(f"Value: {current_status.value}")   # Output: Value: 1

Custom Values in Enums

Python Enums allow assigning custom values to enumeration members, enabling more complex use cases where specific values are required. This is particularly useful when interfacing with external systems or databases.

from enum import Enum

class HttpStatus(Enum):
    OK = 200
    CREATED = 201
    BAD_REQUEST = 400
    UNAUTHORIZED = 401
    NOT_FOUND = 404
    
    def is_success(self):
        return 200 <= self.value < 300

# Usage example
status = HttpStatus.OK
print(f"Is success: {status.is_success()}")  # Output: Is success: True
print(f"Status code: {status.value}")        # Output: Status code: 200

Enum Methods and Properties

Enums provide several built-in methods and properties to access and manipulate enumeration members. Understanding these features enables more efficient handling of enumerated values in Python applications.

from enum import Enum

class Direction(Enum):
    NORTH = 1
    SOUTH = 2
    EAST = 3
    WEST = 4

# Demonstrating various Enum methods
print(list(Direction))                  # All enum members
print(Direction.__members__)            # Dict of name-member pairs
print(Direction.NORTH in Direction)     # Membership testing
print([member.name for member in Direction])  # List of names
print([member.value for member in Direction]) # List of values

Using keys(), values(), and items() with Enums

Enums can be treated similar to dictionaries using keys(), values(), and items() methods through the members attribute, allowing for versatile iteration and access patterns.

from enum import Enum

class Weekday(Enum):
    MONDAY = 1
    TUESDAY = 2
    WEDNESDAY = 3
    THURSDAY = 4
    FRIDAY = 5

# Accessing enum members like a dictionary
print(list(Weekday.__members__.keys()))    # Names
print(list(Weekday.__members__.values()))  # Enum members
print(list(Weekday.__members__.items()))   # (name, member) pairs

Implementing LRU Cache with Enum Methods

The functools.lru_cache decorator can optimize enum operations by caching method results, improving performance for frequently accessed enum calculations or transformations.

from enum import Enum
from functools import lru_cache

class Temperature(Enum):
    CELSIUS = 'C'
    FAHRENHEIT = 'F'
    KELVIN = 'K'
    
    @lru_cache(maxsize=128)
    def convert_to(self, value: float, target: 'Temperature') -> float:
        if self == target:
            return value
        
        # Convert to Celsius first
        if self == Temperature.FAHRENHEIT:
            celsius = (value - 32) * 5/9
        elif self == Temperature.KELVIN:
            celsius = value - 273.15
        else:
            celsius = value
            
        # Convert from Celsius to target
        if target == Temperature.FAHRENHEIT:
            return (celsius * 9/5) + 32
        elif target == Temperature.KELVIN:
            return celsius + 273.15
        return celsius

# Usage example
temp = Temperature.FAHRENHEIT
result = temp.convert_to(98.6, Temperature.CELSIUS)
print(f"98.6°F = {result:.2f}°C")  # Output: 98.6°F = 37.00°C

Real-World Example - State Machine Implementation

An implementation of a state machine using enums demonstrates how to model complex system states and transitions while leveraging caching for performance optimization.

from enum import Enum
from functools import lru_cache
from typing import Set, Dict, Optional

class OrderState(Enum):
    CREATED = 'created'
    PAID = 'paid'
    PROCESSING = 'processing'
    SHIPPED = 'shipped'
    DELIVERED = 'delivered'
    CANCELLED = 'cancelled'
    
    @lru_cache(maxsize=None)
    def allowed_transitions(self) -> Set['OrderState']:
        transitions = {
            OrderState.CREATED: {OrderState.PAID, OrderState.CANCELLED},
            OrderState.PAID: {OrderState.PROCESSING, OrderState.CANCELLED},
            OrderState.PROCESSING: {OrderState.SHIPPED, OrderState.CANCELLED},
            OrderState.SHIPPED: {OrderState.DELIVERED},
            OrderState.DELIVERED: set(),
            OrderState.CANCELLED: set()
        }
        return transitions[self]
    
    def can_transition_to(self, new_state: 'OrderState') -> bool:
        return new_state in self.allowed_transitions()

Extending the State Machine Example

Building upon the previous slide, we'll implement a complete order processing system that demonstrates practical usage of the state machine with event logging and transition validation.

from datetime import datetime
from typing import List, Dict, Optional

class Order:
    def __init__(self, order_id: str):
        self.order_id = order_id
        self.state = OrderState.CREATED
        self.history: List[Dict] = []
        self._log_transition(None, self.state)
    
    @lru_cache(maxsize=32)
    def get_available_actions(self) -> Set[OrderState]:
        return self.state.allowed_transitions()
    
    def _log_transition(self, from_state: Optional[OrderState], to_state: OrderState):
        self.history.append({
            'timestamp': datetime.now().isoformat(),
            'from_state': from_state.value if from_state else None,
            'to_state': to_state.value
        })
    
    def transition_to(self, new_state: OrderState) -> bool:
        if not self.state.can_transition_to(new_state):
            raise ValueError(f"Invalid transition from {self.state} to {new_state}")
        
        old_state = self.state
        self.state = new_state
        self._log_transition(old_state, new_state)
        return True

# Usage example
order = Order("ORD-001")
print(f"Initial state: {order.state}")
order.transition_to(OrderState.PAID)
print(f"Current state: {order.state}")
print(f"History: {order.history}")

Advanced Enum Features with Caching

Implementing complex enum behavior with caching strategies for both instance and class-level methods to optimize performance in high-throughput scenarios.

from enum import Enum, auto
from functools import lru_cache
from typing import Dict, Any

class CachedEnum(Enum):
    @classmethod
    @lru_cache(maxsize=None)
    def _missing_(cls, value: Any) -> Optional['CachedEnum']:
        for member in cls:
            if member.value == value:
                return member
        return None

class ApiEndpoint(CachedEnum):
    USERS = '/api/v1/users'
    PRODUCTS = '/api/v1/products'
    ORDERS = '/api/v1/orders'
    
    @lru_cache(maxsize=128)
    def with_params(self, **params) -> str:
        query_string = '&'.join(f"{k}={v}" for k, v in params.items())
        return f"{self.value}?{query_string}" if params else self.value

# Usage example
endpoint = ApiEndpoint.USERS
url = endpoint.with_params(page=1, limit=10)
print(f"Generated URL: {url}")  # Output: Generated URL: /api/v1/users?page=1&limit=10

Performance Optimization with Cached Enum Methods

Understanding how to optimize enum operations using strategic caching for both lookup operations and computed properties, essential for high-performance applications.

from enum import Enum
from functools import lru_cache
import time

class PermissionLevel(Enum):
    GUEST = 0
    USER = 1
    MODERATOR = 2
    ADMIN = 3
    
    @lru_cache(maxsize=None)
    def has_permission(self, required_level: 'PermissionLevel') -> bool:
        # Simulate complex permission calculation
        time.sleep(0.1)  # In real code, this would be actual computation
        return self.value >= required_level.value
    
    @classmethod
    @lru_cache(maxsize=None)
    def get_all_with_permission(cls, min_level: 'PermissionLevel') -> Set['PermissionLevel']:
        return {level for level in cls if level.value >= min_level.value}

# Performance comparison
def measure_time(func):
    start = time.time()
    result = func()
    end = time.time()
    return end - start, result

# With cache
admin = PermissionLevel.ADMIN
t1, _ = measure_time(lambda: admin.has_permission(PermissionLevel.USER))
t2, _ = measure_time(lambda: admin.has_permission(PermissionLevel.USER))  # Cached
print(f"First call: {t1:.3f}s, Second call: {t2:.3f}s")

Real-World Implementation - Role-Based Access Control

A comprehensive implementation of a role-based access control system using enums with cached permissions and validation logic.

from enum import Flag, auto
from functools import lru_cache
from typing import Set, FrozenSet

class Permission(Flag):
    NONE = 0
    READ = auto()
    WRITE = auto()
    DELETE = auto()
    ADMIN = READ | WRITE | DELETE

class Resource(Enum):
    DOCUMENT = 'document'
    PROJECT = 'project'
    USER = 'user'
    SYSTEM = 'system'

class Role(Enum):
    VIEWER = 'viewer'
    EDITOR = 'editor'
    MANAGER = 'manager'
    ADMIN = 'admin'
    
    @lru_cache(maxsize=None)
    def get_permissions(self, resource: Resource) -> Permission:
        permissions_map = {
            Role.VIEWER: {
                Resource.DOCUMENT: Permission.READ,
                Resource.PROJECT: Permission.READ,
                Resource.USER: Permission.NONE,
                Resource.SYSTEM: Permission.NONE
            },
            Role.EDITOR: {
                Resource.DOCUMENT: Permission.READ | Permission.WRITE,
                Resource.PROJECT: Permission.READ | Permission.WRITE,
                Resource.USER: Permission.READ,
                Resource.SYSTEM: Permission.NONE
            },
            Role.MANAGER: {
                Resource.DOCUMENT: Permission.READ | Permission.WRITE | Permission.DELETE,
                Resource.PROJECT: Permission.READ | Permission.WRITE | Permission.DELETE,
                Resource.USER: Permission.READ | Permission.WRITE,
                Resource.SYSTEM: Permission.READ
            },
            Role.ADMIN: {
                resource: Permission.ADMIN for resource in Resource
            }
        }
        return permissions_map[self][resource]

# Usage example
role = Role.MANAGER
resource = Resource.DOCUMENT
perms = role.get_permissions(resource)
print(f"{role.name} permissions for {resource.name}: {perms}")

Caching Enum Calculations in Memory-Sensitive Environments

Understanding how to implement size-limited caches for enum calculations while maintaining performance in memory-constrained environments through strategic cache management.

from enum import Enum
from functools import lru_cache
from typing import Dict, Tuple
import sys

class ColorSpace(Enum):
    RGB = 'rgb'
    HSL = 'hsl'
    HSV = 'hsv'
    CMYK = 'cmyk'
    
    @lru_cache(maxsize=1000)
    def convert_color(self, values: Tuple[float, ...], target: 'ColorSpace') -> Tuple[float, ...]:
        # Cache size monitoring
        cache_info = self.convert_color.cache_info()
        cache_size = sys.getsizeof(cache_info)
        
        if self == target:
            return values
            
        if self == ColorSpace.RGB and target == ColorSpace.HSL:
            r, g, b = values
            # Complex conversion logic here
            return (h, s, l)  # Example values
            
        # Other conversion implementations
        
    def clear_cache(self):
        self.convert_color.cache_clear()
        
    @classmethod
    def cache_statistics(cls) -> Dict[str, int]:
        stats = cls.RGB.convert_color.cache_info()
        return {
            'hits': stats.hits,
            'misses': stats.misses,
            'current_size': stats.currsize,
            'max_size': stats.maxsize
        }

# Usage example
color = ColorSpace.RGB
result = color.convert_color((255, 0, 0), ColorSpace.HSL)
print(f"Cache stats: {ColorSpace.cache_statistics()}")

Advanced Enum Inheritance with Cached Properties

Implementing sophisticated enum inheritance patterns while maintaining efficient caching mechanisms for derived properties and methods.

from enum import Enum
from functools import lru_cache
from typing import Type, Set

class BasePermission(Enum):
    @classmethod
    @lru_cache(maxsize=None)
    def get_hierarchy(cls) -> Dict[str, Set[str]]:
        return {member.name: member._get_implied_permissions() 
                for member in cls}
    
    @lru_cache(maxsize=32)
    def _get_implied_permissions(self) -> Set[str]:
        return {self.name}

class FilePermission(BasePermission):
    READ = 'read'
    WRITE = 'write'
    EXECUTE = 'execute'
    OWNER = 'owner'
    
    @lru_cache(maxsize=32)
    def _get_implied_permissions(self) -> Set[str]:
        implications = {
            'OWNER': {'READ', 'WRITE', 'EXECUTE'},
            'WRITE': {'READ'},
            'EXECUTE': {'READ'}
        }
        return {self.name} | implications.get(self.name, set())

# Usage example
permission = FilePermission.OWNER
implied = permission._get_implied_permissions()
print(f"{permission.name} implies: {implied}")
hierarchy = FilePermission.get_hierarchy()
print(f"Complete permission hierarchy: {hierarchy}")

Real-World Example - Configuration Management System

A practical implementation of a configuration management system using enums with cached validation and transformation capabilities.

from enum import Enum
from functools import lru_cache
from typing import Any, Optional
import json
import re

class ConfigType(Enum):
    STRING = 'string'
    INTEGER = 'integer'
    FLOAT = 'float'
    BOOLEAN = 'boolean'
    JSON = 'json'
    
    @lru_cache(maxsize=256)
    def validate_and_transform(self, value: str) -> Tuple[bool, Optional[Any], Optional[str]]:
        try:
            if self == ConfigType.STRING:
                return True, value, None
            elif self == ConfigType.INTEGER:
                transformed = int(value)
                return True, transformed, None
            elif self == ConfigType.FLOAT:
                transformed = float(value)
                return True, transformed, None
            elif self == ConfigType.BOOLEAN:
                if value.lower() in ('true', '1', 'yes', 'on'):
                    return True, True, None
                elif value.lower() in ('false', '0', 'no', 'off'):
                    return True, False, None
                return False, None, "Invalid boolean value"
            elif self == ConfigType.JSON:
                transformed = json.loads(value)
                return True, transformed, None
        except Exception as e:
            return False, None, str(e)
            
    @lru_cache(maxsize=32)
    def get_regex_pattern(self) -> str:
        patterns = {
            ConfigType.STRING: r'.*',
            ConfigType.INTEGER: r'^-?\d+$',
            ConfigType.FLOAT: r'^-?\d*\.?\d+$',
            ConfigType.BOOLEAN: r'^(true|false|1|0|yes|no|on|off)$',
            ConfigType.JSON: r'^[\{\[].*[\}\]]$'
        }
        return patterns[self]

class ConfigKey(Enum):
    DEBUG_MODE = (ConfigType.BOOLEAN, 'false')
    MAX_CONNECTIONS = (ConfigType.INTEGER, '100')
    API_URL = (ConfigType.STRING, 'http://localhost:8080')
    RETRY_INTERVAL = (ConfigType.FLOAT, '1.5')
    ALLOWED_ORIGINS = (ConfigType.JSON, '["localhost"]')
    
    def __init__(self, config_type: ConfigType, default_value: str):
        self.config_type = config_type
        self.default_value = default_value
    
    @lru_cache(maxsize=64)
    def validate(self, value: str) -> Tuple[bool, Optional[Any], Optional[str]]:
        return self.config_type.validate_and_transform(value)

# Usage example
config = ConfigKey.MAX_CONNECTIONS
is_valid, transformed, error = config.validate("200")
print(f"Validation result: valid={is_valid}, value={transformed}, error={error}")

Additional Resources

More in Python for ML and AI

  1. Python String Methods startswith() and endswith()7 min

    The startswith() and endswith() methods in Python are commonly used to check if a string begins or ends with a specific substring. However, a lesser-known...

  2. Python args and kwargs Explained with Code Examples8 min

    The \args parameter allows functions to accept an arbitrary number of positional arguments by packing them into a tuple. This powerful feature enables...

  3. Python vs C++ Exception Handling and Debugging6 min

    Python uses try-except blocks to handle exceptions gracefully.

  4. Probabilistic Models in Python10 min

    Probabilistic models are mathematical frameworks that use probability theory to represent and analyze uncertainty in complex systems. These models are...

  5. Python with Essential Data Libraries5 min

    Python's ecosystem is enriched by numerous libraries that extend its capabilities. These libraries simplify complex tasks, enabling developers to focus on...

  6. Probabilistic Graphical Models in Python15 min

    Probabilistic graphical models (PGMs) are powerful tools for representing and reasoning about complex systems with uncertainty. They combine probability...