Python Design Patterns Standards
PythonDesign PatternsSoftware ArchitectureProgramming StandardsBest Practices
Description
Standards for Python design patterns implementation
Globs
**/*.py---
description: Standards for Python design patterns implementation
globs: **/*.py
---
# Python Design Patterns Standards
<rule>
name: python_design_patterns
description: Guidelines for implementing common design patterns in Python
filters:
  - type: file_extension
    pattern: "\.py$"
actions:
  - type: suggest
    message: |
      ## Python Design Patterns Guidelines
      ### Creational Patterns
      1. Singleton:
         ```python
         class Singleton:
             _instance = None
             def __new__(cls, *args, **kwargs):
                 if cls._instance is None:
                     cls._instance = super().__new__(cls)
                 return cls._instance
         ```
         - Use sparingly - often better alternatives exist
         - Consider using module-level variables instead
         - For thread safety, use locks or metaclasses
      2. Factory Method:
         ```python
         class Creator:
             def factory_method(self):
                 raise NotImplementedError
             def operation(self):
                 product = self.factory_method()
                 return product.operation()
         class ConcreteCreator(Creator):
             def factory_method(self):
                 return ConcreteProduct()
         ```
         - Use when object creation logic should be separate from usage
         - Consider using class methods as factories
         - Leverage Python's dynamic typing for flexibility
      3. Builder:
         ```python
         class Product:
             def __init__(self):
                 self.parts = []
         class Builder:
             def __init__(self):
                 self.product = Product()
             def build_part_a(self):
                 self.product.parts.append("Part A")
                 return self
             def build_part_b(self):
                 self.product.parts.append("Part B")
                 return self
             def get_result(self):
                 return self.product
         ```
         - Use for complex object construction
         - Consider using method chaining for fluent interfaces
         - In Python, often simplified with keyword arguments
      ### Structural Patterns
      1. Adapter:
         ```python
         class Target:
             def request(self):
                 pass
         class Adaptee:
             def specific_request(self):
                 return "Specific behavior"
         class Adapter(Target):
             def __init__(self, adaptee):
                 self.adaptee = adaptee
             def request(self):
                 return f"Adapted: {self.adaptee.specific_request()}"
         ```
         - Use to make incompatible interfaces work together
         - Consider using multiple inheritance when appropriate
         - Can be implemented with functions in Python
      2. Decorator:
         ```python
         class Component:
             def operation(self):
                 pass
         class ConcreteComponent(Component):
             def operation(self):
                 return "Basic operation"
         class Decorator(Component):
             def __init__(self, component):
                 self.component = component
             def operation(self):
                 return self.component.operation()
         class ConcreteDecorator(Decorator):
             def operation(self):
                 return f"Enhanced: {super().operation()}"
         ```
         - Use Python's built-in decorator syntax when possible
         - Consider using functools.wraps for function decorators
         - Class decorators can modify class behavior
      3. Composite:
         ```python
         from abc import ABC, abstractmethod
         class Component(ABC):
             @abstractmethod
             def operation(self):
                 pass
         class Leaf(Component):
             def operation(self):
                 return "Leaf operation"
         class Composite(Component):
             def __init__(self):
                 self.children = []
             def add(self, component):
                 self.children.append(component)
             def operation(self):
                 results = []
                 for child in self.children:
                     results.append(child.operation())
                 return f"Composite: {', '.join(results)}"
         ```
         - Use for tree-like object structures
         - Consider using collections.abc interfaces
         - Implement appropriate iteration methods
      ### Behavioral Patterns
      1. Observer:
         ```python
         class Subject:
             def __init__(self):
                 self._observers = []
             def attach(self, observer):
                 self._observers.append(observer)
             def detach(self, observer):
                 self._observers.remove(observer)
             def notify(self):
                 for observer in self._observers:
                     observer.update(self)
         class Observer:
             def update(self, subject):
                 pass
         ```
         - Consider using Python's built-in Observer pattern alternatives:
           - Event hooks
           - Callback functions
           - Signal/slot mechanisms (e.g., PyQt)
      2. Strategy:
         ```python
         class Context:
             def __init__(self, strategy):
                 self._strategy = strategy
             def set_strategy(self, strategy):
                 self._strategy = strategy
             def execute_strategy(self, data):
                 return self._strategy.execute(data)
         class Strategy:
             def execute(self, data):
                 pass
         ```
         - In Python, often simplified using callable objects or functions
         - Consider using function parameters instead of strategy classes
         - Leverage higher-order functions when appropriate
      3. Command:
         ```python
         class Command:
             def execute(self):
                 pass
         class ConcreteCommand(Command):
             def __init__(self, receiver):
                 self._receiver = receiver
             def execute(self):
                 self._receiver.action()
         class Invoker:
             def __init__(self):
                 self._command = None
             def set_command(self, command):
                 self._command = command
             def execute_command(self):
                 self._command.execute()
         ```
         - In Python, often simplified using callable objects
         - Consider using higher-order functions
         - Use for undo/redo functionality or command queuing
      ### Python-Specific Patterns
      1. Context Manager:
         ```python
         class ResourceManager:
             def __init__(self, resource):
                 self.resource = resource
             def __enter__(self):
                 # Setup code
                 return self.resource
             def __exit__(self, exc_type, exc_val, exc_tb):
                 # Cleanup code
                 self.resource.close()
                 return False  # Re-raise exceptions
         ```
         - Use for resource management (files, connections, locks)
         - Consider using contextlib.contextmanager for function-based approach
         - Always handle exceptions properly in __exit__
      2. Descriptor:
         ```python
         class Descriptor:
             def __get__(self, instance, owner):
                 # Return computed or stored value
             def __set__(self, instance, value):
                 # Validate and store value
             def __delete__(self, instance):
                 # Cleanup
         ```
         - Use for attribute access control
         - Consider using property for simple cases
         - Useful for validation, caching, and computed attributes
      ### Anti-Patterns to Avoid
      1. God Object:
         - Classes that know or do too much
         - Violates single responsibility principle
         - Refactor into smaller, focused classes
      2. Spaghetti Code:
         - Tangled, unstructured code flow
         - Use proper function decomposition
         - Follow @python-code-organization guidelines
      3. Reinventing the Wheel:
         - Reimplementing standard library functionality
         - Use built-in modules and functions
         - Leverage established third-party packages
      ### Related Guidelines
      - For code organization, see @python-code-organization
      - For refactoring techniques, see @python-refactoring
      - For API design, see @python-api-design
examples:
  - input: |
      # Bad: Complex creation logic mixed with business logic
      def process_data(data_type, source):
          if data_type == "csv":
              processor = CSVProcessor(source)
          elif data_type == "json":
              processor = JSONProcessor(source)
          elif data_type == "xml":
              processor = XMLProcessor(source)
          else:
              raise ValueError(f"Unsupported data type: {data_type}")
          return processor.process()
      # Good: Factory pattern
      class ProcessorFactory:
          @classmethod
          def create(cls, data_type, source):
              processors = {
                  "csv": CSVProcessor,
                  "json": JSONProcessor,
                  "xml": XMLProcessor
              }
              processor_class = processors.get(data_type)
              if not processor_class:
                  raise ValueError(f"Unsupported data type: {data_type}")
              return processor_class(source)
      # Usage
      processor = ProcessorFactory.create(data_type, source)
      result = processor.process()
      # Bad: Resource management without context manager
      def process_file(path):
          f = open(path, 'r')
          try:
              data = f.read()
              return process_data(data)
          finally:
              f.close()
      # Good: Using context manager
      def process_file(path):
          with open(path, 'r') as f:
              data = f.read()
              return process_data(data)
metadata:
  priority: high
  version: 1.0
  tags: ["python", "design-patterns", "architecture", "best-practices"]
</rule>