formations/python-avancé/examples/metaclass-overloading.py
2023-09-26 09:13:47 +02:00

64 lines
1.5 KiB
Python

from collections import defaultdict
from functools import wraps
def affinity(args):
def keyfunc(function):
return sum(arg == annotation for arg, annotation in zip(args[1:], function.__annotations__.values())) - len(function.__annotations__)
return keyfunc
def best_match(args, functions):
return max(functions, key=affinity(args))
def make_dispatcher(alternatives):
@wraps(alternatives[0])
def dispatch(*args, **kwargs):
return best_match(args, alternatives)(*args, **kwargs)
return dispatch
class MultiNamespaces(dict):
def __getitem__(self, key):
return super().__getitem__(key)[-1]
def __setitem__(self, key, value):
self.setdefault(key, []).append(value)
def to_dict(self):
frozen = {}
for key, values in self.items():
if callable(values[-1]):
frozen[key] = make_dispatcher(values)
else:
frozen[key] = values[-1]
return frozen
class Overloader(type):
@classmethod
def __prepare__(cls, name, bases, **kwds):
return MultiNamespaces()
def __new__(cls, name, bases, ns, **kwargs):
return super().__new__(cls, name, bases, ns.to_dict(), **kwargs)
class Overloading(metaclass=Overloader):
...
class Fib(Overloading):
def fib(self, n: 0):
return 1
def fib(self, n: 1):
return 1
def fib(self, n):
return self.fib(n - 1) + self.fib(n - 2)
print(Fib().fib(10))