In [2]:
from collections import OrderedDict
from toolz.curried import flip, juxt, map, partial, pipe, valmap
from types import LambdaType
from typing import Iterable, Any
__all__ = [
'Dispatch', 'DictCallable', 'TupleCallable', 'ListCallable', 'SetCallable'
]
In [6]:
class DictCallable(dict):
def __call__(self, *args, **kwargs):
return valmap(
lambda x: x(*args, **kwargs) if callable(x) else x, self
)
In [5]:
class Condictional(OrderedDict):
"""First key to satisfy the key condition executes.
"""
def key(self, x, *args, **kwargs)->bool:
return x(*args, **kwargs)
def __init__(self, args=[], default=None, key=None):
super().__init__(args)
self.default = default
if key:
self.key = key
def __call__(self, *args, **kwargs):
for key, value in self.items():
if self.key(key, *args, **kwargs):
return value(*args, **kwargs)
if self.default:
return self.default(*args, **kwargs)
raise KeyError(
"No conditions satisfied for types: " + args.__str__())
In [27]:
class Dispatch(Condictional):
"""An object that provides multiple dispatch when it is called.
"""
def key(self, key, *args, **kwargs):
if not isinstance(key, Iterable):
key = tuple([key])
if len(args) == len(key):
return all(
isinstance(arg, types) for arg, types in zip(args, key)
if isinstance(types, Iterable) or types != Any
)
return False
def __init__(self, args=[], default=None):
super().__init__(args)
self.default = default
In [5]:
class ListCallable(list):
def __call__(self, *args, **kwargs):
return list(juxt(*self)(
*args, **kwargs
))
In [6]:
class SetCallable(set):
def __call__(self, *args, **kwargs):
if pipe(self, map(
partial(flip(isinstance), LambdaType)
), any):
raise TypeError("Cannot interpolate a LambdaType.")
return pipe(
zip(
self, list(map(lambda x: x(*args, **kwargs), self))
), list, dict
)
In [7]:
class TupleCallable(tuple):
def __call__(self, *args, **kwargs):
return juxt(*self)(
*args, **kwargs
)