Skip to content

Commit 005ed8f

Browse files
committed
curry memoize by default.
This allows a cache to be provided when using `memoize` as a decorator.
1 parent 00f462e commit 005ed8f

File tree

4 files changed

+78
-62
lines changed

4 files changed

+78
-62
lines changed

examples/fib.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,9 @@ def fib(n):
2525
fib = memoize(fib)
2626

2727

28-
# Provide a cache with initial values to `memoize`
29-
from toolz import curry
30-
31-
32-
@curry(memoize, cache={0: 0, 1: 1})
28+
# Provide a cache with initial values to `memoize`. This works as a decorator
29+
# because `memoize` is curried (see `toolz.curry`) by default.
30+
@memoize(cache={0: 0, 1: 1})
3331
def fib(n):
3432
""" Functional definition of Fibonacci numbers with initial terms cached.
3533

toolz/curried.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def nargs(f):
4040
def should_curry(f):
4141
do_curry = set((toolz.map, toolz.filter, toolz.sorted, toolz.reduce))
4242
return (callable(f) and nargs(f) and nargs(f) > 1
43-
or f in do_curry)
43+
and not isinstance(f, curry) or f in do_curry)
4444

4545

4646
d = dict((name, curry(f) if '__' not in name and should_curry(f) else f)

toolz/functoolz/core.py

+65-56
Original file line numberDiff line numberDiff line change
@@ -80,62 +80,6 @@ def evalform_back(val, form):
8080
return reduce(evalform_back, forms, val)
8181

8282

83-
def memoize(func, cache=None):
84-
""" Cache a function's result for speedy future evaluation
85-
86-
Considerations:
87-
Trades memory for speed.
88-
Only use on pure functions.
89-
90-
>>> def add(x, y): return x + y
91-
>>> add = memoize(add)
92-
93-
Or use as a decorator
94-
95-
>>> @memoize
96-
... def add(x, y):
97-
... return x + y
98-
"""
99-
if cache is None:
100-
cache = {}
101-
102-
try:
103-
spec = inspect.getargspec(func)
104-
may_have_kwargs = bool(not spec or spec.keywords or spec.defaults)
105-
# Is unary function (single arg, no variadic argument or keywords)?
106-
is_unary = (spec and spec.varargs is None and not may_have_kwargs
107-
and len(spec.args) == 1)
108-
except TypeError:
109-
may_have_kwargs = True
110-
is_unary = False
111-
112-
def memof(*args, **kwargs):
113-
try:
114-
if is_unary:
115-
key = args[0]
116-
elif may_have_kwargs:
117-
key = (args, frozenset(kwargs.items()))
118-
else:
119-
key = args
120-
in_cache = key in cache
121-
except TypeError:
122-
raise TypeError("Arguments to memoized function must be hashable")
123-
124-
if in_cache:
125-
return cache[key]
126-
else:
127-
result = func(*args, **kwargs)
128-
cache[key] = result
129-
return result
130-
131-
try:
132-
memof.__name__ = func.__name__
133-
except AttributeError:
134-
pass
135-
memof.__doc__ = func.__doc__
136-
return memof
137-
138-
13983
def _num_required_args(func):
14084
""" Number of args for func
14185
@@ -239,6 +183,71 @@ def __call__(self, *args, **_kwargs):
239183
return curry(self.func, *args, **kwargs)
240184

241185

186+
@curry
187+
def memoize(func, cache=None):
188+
""" Cache a function's result for speedy future evaluation
189+
190+
Considerations:
191+
Trades memory for speed.
192+
Only use on pure functions.
193+
194+
>>> def add(x, y): return x + y
195+
>>> add = memoize(add)
196+
197+
Or use as a decorator
198+
199+
>>> @memoize
200+
... def add(x, y):
201+
... return x + y
202+
203+
Use the ``cache`` keyword to provide a dict-like object as an initial cache
204+
205+
>>> @memoize(cache={(1, 2): 3})
206+
... def add(x, y):
207+
... return x + y
208+
209+
Note that the above works as a decorator because ``memoize`` is curried.
210+
"""
211+
if cache is None:
212+
cache = {}
213+
214+
try:
215+
spec = inspect.getargspec(func)
216+
may_have_kwargs = bool(not spec or spec.keywords or spec.defaults)
217+
# Is unary function (single arg, no variadic argument or keywords)?
218+
is_unary = (spec and spec.varargs is None and not may_have_kwargs
219+
and len(spec.args) == 1)
220+
except TypeError:
221+
may_have_kwargs = True
222+
is_unary = False
223+
224+
def memof(*args, **kwargs):
225+
try:
226+
if is_unary:
227+
key = args[0]
228+
elif may_have_kwargs:
229+
key = (args, frozenset(kwargs.items()))
230+
else:
231+
key = args
232+
in_cache = key in cache
233+
except TypeError:
234+
raise TypeError("Arguments to memoized function must be hashable")
235+
236+
if in_cache:
237+
return cache[key]
238+
else:
239+
result = func(*args, **kwargs)
240+
cache[key] = result
241+
return result
242+
243+
try:
244+
memof.__name__ = func.__name__
245+
except AttributeError:
246+
pass
247+
memof.__doc__ = func.__doc__
248+
return memof
249+
250+
242251
class Compose(object):
243252
""" A composition of functions
244253

toolz/functoolz/tests/test_core.py

+9
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,15 @@ def test_memoize_key_signature():
122122
assert mf(1) == 2
123123

124124

125+
def test_memoize_curry_cache():
126+
@memoize(cache={1: True})
127+
def f(x):
128+
return False
129+
130+
assert f(1) is True
131+
assert f(2) is False
132+
133+
125134
def test_curry_simple():
126135
cmul = curry(mul)
127136
double = cmul(2)

0 commit comments

Comments
 (0)