Class Method Composition
Solution 1:
I've been thinking about this for a few days.
You should definitely not use global
. The simple method for you is to put the entire thing inside another class, then access container
as an instance variable.
But I think there's a trivial way that you've missed. This is similar to what you've done where the class has to predefine each specific method it wants to be able to chain.
classFoo:
def__init__(self, n):
self._n = n
@propertydefdata(self):
return self._n
defadd(self, x):
self._n += x
return self
defdiv(self, x):
self._n /= x
return self
print(repr(Foo(10).add(2).div(2).data))
# 6.0
I don't like this because it's not very generic; it operates on the basis of side-effects; and you need to have everything defined from the outset.
So here's a more generic setup where you have a Composer
which manages the functions you can chain, and a Chainable
which alternates between two states:
- Storing the "current value" and the next function
- Storing the result of the previous function
The composer has a decorator built in which allows you to register other generic functions at a later time.
from typing import *
from dataclasses import dataclass
T = TypeVar("T") # the initial data
Func = Callable[[T], T] # the function that transforms the data
RegisteredFunc = Callable[[Any], Func] # the function that returns the function that transforms the dataclassComposer:functions: Dict[str, RegisteredFunc]
def__init__(self):
self.functions = {}
defregister(self, func: RegisteredFunc) -> RegisteredFunc:self.functions[func.__name__] = func
return func
@dataclassclassChainable:data: T
fun: Optional[RegisteredFunc] = None
composer: Optional["Composer"] = None
def__getattr__(self, item) -> "Chainable":
return Chainable(self.data, self.composer.functions[item], self.composer)
def__call__(self, *args, **kwargs) -> "Chainable":
next_data = self.fun(*args, **kwargs)(self.data) ifself.fun elseself.data
return Chainable(next_data, composer=self.composer)
def__str__(self):
return str(self.data)
def__repr__(self):
return repr(self.data)
comp = Composer()
@comp.register
defadd(x) -> Func:returnlambdan: n + x
@comp.register
defdiv(x) -> Func:returnlambdan: n / x
print(repr(Chainable(10, composer=comp).add(2).div(2)))
# 6.0
print(repr(Chainable(1, composer=comp).add(2).add(1).add(2).div(4)))
# 1.5
Post a Comment for "Class Method Composition"