# https://www.codewars.com/kata/5f24315eff32c4002efcfc6a import inspect # https://docs.python.org/3.8/reference/datamodel.html#customizing-class-creation # https://snarky.ca/unravelling-pythons-classes/ # https://gist.github.com/tmr232/c7214d62607ba1fc0e0a2e82d7e041cd class DispatchProxy: def __init__(self, name): self.registry = {} self.name = name def register(self, f): print(f"register {inspect.signature(f)}") argcount = f.__code__.co_argcount self.registry[argcount] = f """ def decorator(func): argcount = f.__code__.co_argcount self.registry[argcount] = f return func return decorator """ def __call__(self, *args): argcount = len(args) + 1 # add 1 because the first argument of a method is "self" if argcount in self.registry: resolved = self.registry[argcount] print(f"resolved: {resolved.__name__}{inspect.signature(resolved)}") return resolved(self, *args) else: raise AttributeError(f"No matching function for args: {args}") class MyNamespace(dict): def __getitem__(self, key): #(f"get {key}") return super().__getitem__(key) def __setitem__(self, key, value): #sign = inspect.signature(value) if callable(value) else value #print(f"set {key} {value}") # already exists => overload is_method = callable(value) and not value.__name__.startswith("__") if is_method and key in self: m = self[key] if not isinstance(m, DispatchProxy): m = DispatchProxy(self) m.register(self[key]) m.register(value) super().__setitem__(key, m) else: super().__setitem__(key, value) class Meta(type): @classmethod def __prepare__(cls, name, bases): return MyNamespace() def __new__(cls, name, bases, dct): x = super().__new__(cls, name, bases, dct) return x def __init__(self, name, bases, attrs): super(Meta, self).__init__(name, bases, attrs)