1. Database Connection Pool using Singleton and Context Manager
Problem: Managing database connections efficiently in a multi-threaded application.
Solution: Use the Singleton pattern to ensure a single connection pool, and a context manager for safe connection handling.
import threading
from contextlib import contextmanager
class DatabasePool:
_instance = None
_lock = threading.Lock()
def __new__(cls):
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.connections = []
return cls._instance
def create_connection(self):
# Simulating connection creation
return f"Connection-{len(self.connections)}"
def get_connection(self):
if not self.connections:
return self.create_connection()
return self.connections.pop()
def return_connection(self, connection):
self.connections.append(connection)
@contextmanager
def db_connection():
pool = DatabasePool()
connection = pool.get_connection()
try:
yield connection
finally:
pool.return_connection(connection)
# Usage
def worker():
with db_connection() as conn:
print(f"Thread {threading.current_thread().name} using {conn}")
threads = [threading.Thread(target=worker) for _ in range(5)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
This solution uses the Singleton pattern to ensure a single pool of connections and a context manager to safely acquire and release connections.
2. Configurable Data Validation using Descriptors
Problem: Implementing reusable and configurable data validation for class attributes.
Solution: Use descriptors to create a flexible validation system.
class Validator:
def __init__(self, min_value=None, max_value=None, regex=None):
self.min_value = min_value
self.max_value = max_value
self.regex = regex
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__.get(self.name)
def __set__(self, instance, value):
if self.min_value is not None and value < self.min_value:
raise ValueError(f"{self.name} must be at least {self.min_value}")
if self.max_value is not None and value > self.max_value:
raise ValueError(f"{self.name} must be no more than {self.max_value}")
if self.regex and not re.match(self.regex, str(value)):
raise ValueError(f"{self.name} must match the pattern {self.regex}")
instance.__dict__[self.name] = value
class Person:
age = Validator(min_value=0, max_value=150)
name = Validator(regex=r'^[A-Za-z]+$')
def __init__(self, name, age):
self.name = name
self.age = age
# Usage
try:
person = Person("John123", 30) # Raises ValueError for invalid name
except ValueError as e:
print(e)
try:
person = Person("John", 200) # Raises ValueError for invalid age
except ValueError as e:
print(e)
person = Person("John", 30) # Valid
print(f"Name: {person.name}, Age: {person.age}")
This solution uses descriptors to create a reusable validation system that can be easily applied to multiple attributes and classes.
3. Plugin System using Abstract Base Classes and Metaclasses
Problem: Creating a flexible plugin system for a text processing application.
Solution: Use Abstract Base Classes to define the plugin interface and a metaclass for automatic plugin registration.
from abc import ABC, abstractmethod
class PluginMeta(type):
plugins = {}
def __new__(cls, name, bases, attrs):
new_cls = type.__new__(cls, name, bases, attrs)
if name != 'Plugin':
cls.plugins[new_cls.name] = new_cls
return new_cls
class Plugin(ABC, metaclass=PluginMeta):
@property
@abstractmethod
def name(self):
pass
@abstractmethod
def process(self, text):
pass
class UppercasePlugin(Plugin):
name = 'uppercase'
def process(self, text):
return text.upper()
class ReversePlugin(Plugin):
name = 'reverse'
def process(self, text):
return text[::-1]
class TextProcessor:
def __init__(self):
self.plugins = PluginMeta.plugins
def process(self, text, plugin_name):
if plugin_name not in self.plugins:
raise ValueError(f"Plugin {plugin_name} not found")
return self.plugins[plugin_name]().process(text)
# Usage
processor = TextProcessor()
text = "Hello, World!"
print(processor.process(text, 'uppercase')) # Output: HELLO, WORLD!
print(processor.process(text, 'reverse')) # Output: !dlroW ,olleH
This solution uses an Abstract Base Class to define the plugin interface and a metaclass for automatic plugin registration, allowing for easy extension of the text processing capabilities.
4. Efficient Data Storage using Properties and Slots
Problem: Optimizing memory usage for a large number of instances with a fixed set of attributes.
Solution: Use __slots__
to restrict attribute creation and properties for controlled access.
class Point:
__slots__ = ('_x', '_y')
def __init__(self, x, y):
self._x = x
self._y = y
@property
def x(self):
return self._x
@x.setter
def x(self, value):
if not isinstance(value, (int, float)):
raise TypeError("x must be a number")
self._x = value
@property
def y(self):
return self._y
@y.setter
def y(self, value):
if not isinstance(value, (int, float)):
raise TypeError("y must be a number")
self._y = value
def __repr__(self):
return f"Point({self.x}, {self.y})"
# Usage
points = [Point(i, i*2) for i in range(1000000)]
print(points[500000]) # Output: Point(500000, 1000000)
# This will raise an AttributeError
try:
points[0].z = 5
except AttributeError as e:
print(e)
This solution uses __slots__
to restrict attribute creation, saving memory, and properties to provide controlled access to the attributes.