Table of contents generated with markdown-toc
…
Strong form of association where the composed objects cannot exist independently of the parent object.
class Engine:
pass
class Transmission:
pass
class Wheel:
pass
class Car:
def __init__(self):
self.engine = Engine()
self.transmission = Transmission()
self.wheels = [Wheel() for _ in range(4)]
Weak form of association where objects can exist independently of the parent object.
class Player:
pass
class Team:
def __init__(self):
self.players = []
def add_player(self, player):
self.players.append(player)
player1 = Player()
player2 = Player()
team = Team()
team.add_player(player1)
team.add_player(player2)
…
…
Programming languages that allow only single inheritance don’t have the problem. Anyway Python allows multiple inheritance…:
class A:
def __init__(self):
print("A")
class B(A):
def __init__(self):
print("B")
super().__init__()
class C(A):
def __init__(self):
print("C")
super().__init__()
class D(B, C):
def __init__(self):
print("D")
super().__init__()
if __name__ == "__main__":
d = D()
D
B
C
A
Just like class is objects factory, a metaclass is classes factory. In Python everything is an object (there is not actual ‘primitive’ types) and its actual implementation contains many classes and metaclasses. Metaclasses are a fundamental part of Python’s type system.
Use cases e.g.:
class PluginRegistry(type):
def __new__(cls, name, bases, dct):
new_class = super().__new__(cls, name, bases, dct)
if not hasattr(cls, 'plugins'):
cls.plugins = []
else:
cls.plugins.append(new_class)
return new_class
@classmethod
def get_plugins(cls):
return cls.plugins
class PluginBase(metaclass=PluginRegistry):
pass
class PluginOne(PluginBase):
def run(self):
print("PluginOne running")
class PluginTwo(PluginBase):
def run(self):
print("PluginTwo running")
plugin1 = PluginOne()
plugin2 = PluginTwo()
registered_plugins = PluginRegistry.get_plugins()
print("Registered Plugins:")
for plugin in registered_plugins:
print(plugin.__name__)
class EndpointRegistrationMeta(type):
def __new__(cls, name, bases, dct):
# Check if the class defines an 'endpoints' attribute
if 'endpoints' not in dct:
raise ValueError(f"Class '{name}' must define 'endpoints' attribute for endpoint registration")
# Validate the 'endpoints' attribute
endpoints = dct['endpoints']
if not isinstance(endpoints, dict):
raise ValueError("Endpoints must be defined as a dictionary")
for endpoint_name, endpoint_func in endpoints.items():
# Check if each endpoint is a callable function
if not callable(endpoint_func):
raise ValueError(f"Invalid endpoint '{endpoint_name}'. Endpoints must be callable functions")
# Check if endpoint name follows snake_case convention
if not endpoint_name.islower() or '_' in endpoint_name:
raise ValueError(f"Invalid endpoint name '{endpoint_name}'. Endpoint names must be in snake_case")
return super().__new__(cls, name, bases, dct)
# Example HTTP client class enforcing endpoint registration
class HTTPClient(metaclass=EndpointRegistrationMeta):
endpoints = {}
@classmethod
def register_endpoint(cls, name, func):
cls.endpoints[name] = func
# Usage example
class MyHTTPClient(HTTPClient):
endpoints = {
'get_user': lambda user_id: f"GET /users/{user_id}",
'create_user': lambda data: f"POST /users - {data}",
# Invalid endpoint name
'InvalidEndpoint': lambda: "Invalid endpoint"
}