Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

thread_as function #541

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ Functoolz
juxt
memoize
pipe
thread_as
thread_first
thread_last

Expand Down
2 changes: 2 additions & 0 deletions toolz/curried/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
peek,
pipe,
second,
thread_as,
thread_first,
thread_last,
)
Expand Down Expand Up @@ -98,6 +99,7 @@
update_in = toolz.curry(toolz.update_in)
valfilter = toolz.curry(toolz.valfilter)
valmap = toolz.curry(toolz.valmap)
thread_as = toolz.curry(toolz.thread_as)

del exceptions
del toolz
48 changes: 45 additions & 3 deletions toolz/functoolz.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
PYPY = hasattr(sys, 'pypy_version_info') and sys.version_info[0] > 2


__all__ = ('identity', 'apply', 'thread_first', 'thread_last', 'memoize',
'compose', 'compose_left', 'pipe', 'complement', 'juxt', 'do',
'curry', 'flip', 'excepts')
__all__ = ('identity', 'apply', 'thread_as', 'thread_first', 'thread_last',
'memoize', 'compose', 'compose_left', 'pipe', 'complement', 'juxt',
'do', 'curry', 'flip', 'excepts')

PYPY = hasattr(sys, 'pypy_version_info')

Expand Down Expand Up @@ -65,6 +65,7 @@ def thread_first(val, *forms):
g(f(x), y, z)

See Also:
thread_as
thread_last
"""
def evalform_front(val, form):
Expand Down Expand Up @@ -104,6 +105,7 @@ def thread_last(val, *forms):
[2, 4]

See Also:
thread_as
thread_first
"""
def evalform_back(val, form):
Expand All @@ -116,6 +118,45 @@ def evalform_back(val, form):
return reduce(evalform_back, forms, val)


def thread_as(val, name, *forms):
""" Thread named value through a sequence of functions/forms

The function, and its inputs, should be specified in a tuple.
The given value name is replaced with the actual value, wherever present.

>>> def double(x): return 2*x
>>> def inc(x): return x + 1
>>> v = "somevalname"
>>> thread_as(1, v, (inc, v), (double, v))
4

>>> def add(x, y): return x + y
>>> def pow(x, y): return x**y
>>> v = "someothervalname"
>>> thread_as(1, v, (add, 4, v), (pow, v, 3))
125

So in general
thread_as(x, v, (f, v), (g, y, v, z))
expands to
g(y, f(x), z)

See Also:
thread_first
thread_last
"""
def arg_val(val, arg):
return val if arg == name else arg

def evalform_as(val, form):
if isinstance(form, tuple):
func, args = \
form[0],\
map(partial(arg_val, val), form[1:])
return func(*args)
return reduce(evalform_as, forms, val)


def instanceproperty(fget=None, fset=None, fdel=None, doc=None, classval=None):
""" Like @property, but returns ``classval`` when used as a class attribute

Expand Down Expand Up @@ -621,6 +662,7 @@ def pipe(data, *funcs):
See Also:
compose
compose_left
thread_as
thread_first
thread_last
"""
Expand Down
16 changes: 14 additions & 2 deletions toolz/tests/test_functoolz.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import toolz
from toolz.functoolz import (thread_first, thread_last, memoize, curry,
compose, compose_left, pipe, complement, do, juxt,
flip, excepts, apply)
from operator import add, mul, itemgetter
flip, excepts, apply, thread_as)
from operator import add, mul, itemgetter, pow
from toolz.utils import raises
from functools import partial

Expand Down Expand Up @@ -64,6 +64,18 @@ def test_thread_last():
assert thread_last(2, (add, 5), double) == 14


def test_thread_as():
name = "somevalname"
assert thread_as(2, name) == 2
assert thread_as(2, name, (inc, name)) == 3
assert thread_as(2, name, (inc, name), (inc, name)) == 4
assert thread_as(2, name, (double, name), (inc, name)) == 5
assert thread_as(2, name, (add, name, 5), (double, name)) == 14
assert list(thread_as([1, 2, 3], name, (map, inc, name), (filter, iseven, name))) == [2, 4]
assert list(thread_as([1, 2, 3], name, (map, inc, name), (filter, isodd, name))) == [3]
assert thread_as(1, name, (add, 4, name), (pow, name, 3)) == 5**3


def test_memoize():
fn_calls = [0] # Storage for side effects

Expand Down