3

I want to use a function which accepts some arguments in a toolz.pipe, but data input is a tuple. I know how to solve it, but I think there must be some solution in builtin python libraries or in toolz, I just wasnt able to find it.

Example:

def my_func(a, b):
    return a + b

result = pipe(
    (10, 20),
    my_func,
    ...,
)

For those unfamiliar with toolz, pipe is something like:

def pipe(val, *functions):
    data = val
    for fn in functions:
        data = fn(data)
    return data

What I tried:

I know how to solve it, for example this way:

result = pipe(
    (10, 20),
    lambda x: my_func(*x),
    ...,
)

But I find this ugly and I'd like to be able to use some sort of apply function which encapsulates that lambda. I defined mine apply_ like this:

from toolz import pipe

def my_func(a, b):
    return a + b

def apply_(func):
    return lambda x: func(*x)

result = pipe(
    (10, 20),
    apply_(my_func),
)
print(result)

But this seems like something so basic, I'm almost convinced it must exist in builtin python libraries or in toolz package..

Question:

Is there some form of apply wrapper like I described above (apply_), which I have overlooked?

Jan Spurny
  • 5,219
  • 1
  • 33
  • 47

4 Answers4

1

For example:

import toolz
import itertools

def my_func(a, b):
    return a + b


list(toolz.pipe(
    [(10, 20), (30, 40)],
    toolz.curry(itertools.starmap)(my_func)
))

The output:

[30, 70]

Edit I was wrong with my intial solution, but I think itertools's starmap is the way to go.

Drey
  • 3,314
  • 2
  • 21
  • 26
  • But this would just call my_func twice and with one parameter each time.. – Jan Spurny Aug 08 '19 at 16:43
  • Your are right. I edited the post and now here is what I hope is what you were looking for. – Drey Aug 08 '19 at 17:50
  • I'm sorry, but you made it into a different problem - I don't want any mapping over a sequence, I just needed to "paste" a sequence into function's arguments, which is not what your solution does.. – Jan Spurny Aug 08 '19 at 23:34
  • That is what `starmap` does - its pastes a sequence into function's arguments, e.g. it pastes (10, 20) into `my_func`. It would be kind of you to provide a minimal working example of what you want to do. But `pipe( (10, 20), lambda x: my_func(*x), ..., )` and `pipe( [(10, 20)], toolz.curry(itertools.starmap)(my_func), ..., )` produce the same results. – Drey Aug 09 '19 at 04:09
  • 1
    Well you wrote is yourself - I CAN'T use starmap when I have `(10, 20)` in pipe - your solution works only if I put the tuple into a list - which is NOT what I want. But you're right about the example, I didn't provide _complete_ working example. I'll fix that - somehow, because in my question, I'm asking how to do something which I don't know how to do, so _complete_ example doesn't make much sense. But I'll complete my `apply_` piece of code at least. – Jan Spurny Aug 09 '19 at 12:27
1

toolz have the c.map that you can use:

import toolz as tz
from toolz import curried as c

tz.pipe(data, c.map(function), ...)
  • 2
    Unfortunately, no. Curried `map(my_func)` has `(Iterable) -> Iterable` signature. You can see for yourself if you switch `apply_` in my example for `c.map` (and add `list` to pipe). It will try call `my_func` for each member of (10, 20) - which would not work, because `my_func` has signature `(param, param)` and `map` (curried or not) assumes it has only one parameter signature. – Jan Spurny Feb 14 '20 at 14:11
0

Two years late but I think what you're looking for is reduce:

from toolz import pipe, partial, reduce
    
def my_func(a, b):
    return a + b

result = pipe(
    (10, 20),
    partial(reduce, my_func),
)
print(result)
MonkChen
  • 26
  • 1
  • 1
    That's interesting "hack", but it only works for 2-value tuples (which I used in the question for simplicity). It won't work for different number of values in the tuple.. – Jan Spurny Apr 20 '22 at 14:13
0

Seems like starmap as suggested elsewhere is a great approach, but you don't want to iterate over the elements of your tuple, you want the tuple to be the (only) element of an iterable:

from itertools import starmap
from operator import methodcaller as dot
from toolz.curried import partial, partition, pipe

def my_func(a, b, c):
    return f"a:{a}, b:{b}, c:{c}"

pipe(
    (1,2,3),
    partition(3),  # length of the tuple
    partial(starmap, my_func),
    dot("__next__"),
)

gives

'a:1, b:2, c:3'
Al Aric
  • 1
  • 1