So just to step back a bit. Let’s take 2 base cases for this, which are both available without any changes:
Setup
from functools import partial, Placeholder as _
class MethodCaller:
def __init__(self, arg=None):
self.arg = arg
def __getattr__(self, name):
assert self.arg is None
return type(self)(name)
def __call__(self, *args, **kwds):
arg = self.arg
assert arg is not None
if type(arg) is str:
return type(self)((arg, args, kwds))
else:
assert len(arg) == 3 and len(args) == 1 and not kwds
return getattr(args[0], arg[0])(*arg[1], **arg[2])
MC = MethodCaller()
class P:
def __init__(self, *args, **kwds):
self.args = args
self.kwds = kwds
def __call__(self, func):
return partial(func, *self.args, **self.kwds)
__rmatmul__ = __ror__ = __rrshift__ = __call__
1. rpipe
object
class rpipe:
def __init__(self, obj):
self.obj = obj
def __iter__(self):
return self.obj
def __or__(self, func):
if func is type(self):
return self.obj
return type(self)(func(self.obj))
rpipe(1) | MC.as_integer_ratio() | sum | format@P(_, '.2f') | rpipe
- Infix operator
# Implementation left out as quite cumbersome
1 |x| MC.as_integer_ratio() |x| sum |x| format@P(_, '.2f') |x| x
So there are 2 working solutions which are NOT utterly inconvenient and don’t require anything too sophisticated or unreasonable imports.
So what could be improvements to above and which of those improvements are sensible when seen in the light of how much complexity they add versus marginal convenience?
1. So say, implementation of pipe operator |>
would make:
rpipe(1) | MC.as_integer_ratio() | sum | format@P(_, '.2f') | rpipe
1 |x| MC.as_integer_ratio() |x| sum |x| format@P(_, '.2f') |x| x
# INTO NEW:
1 |> MC.as_integer_ratio() |> sum |> format@P(_, '.2f')
As you said, it is not exactly call, so I would say __pipe__
/ __rpipe__
or __apply__
/ __rapply__
would be more accurate. (also, there aren’t any methods with __l
prefix - standard is right with __r
prefix and left is without)
2. extending current partial
would allow for full functionality that you had in mind
_ = functools.Placeholder
__ = functools.ArgsPlaceholder
___ = functools.KwdsPlaceholder
____ = functools.ArgsKwdsPlaceholder
1 |> foo@P(_)
1 |> foo@P(a=_)
[[1], [1]] |> zip@P(__)
{'a': 1, 'b': 2} |> foo@P(___)
([1, 1], {'a': 1, 'b': 2}) |> foo@P(____)
P.S. can we please take the following 2 as a benchmark case for the following ideas so that we can keep perspective on what we are improving?
rpipe(1) | MC.as_integer_ratio() | sum | format@P(_, '.2f') | rpipe
1 |x| MC.as_integer_ratio() |x| sum |x| format@P(_, '.2f') |x| x