-
Notifications
You must be signed in to change notification settings - Fork 265
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
Change curry
to use functools.partial
#150
Conversation
…Not finished. This includes a significant change to curried objects: they never return `functools.partial` objects. Instead, `Curry` *uses* `partial`. This is because `partial` behaves differently. Let me illustrate: `curry(lambda x, y=0: x+y)(y=2)` is valid. `partial(lambda x, y=0: x+y)(y=2)` returns a TypeError. More documentation and tests are needed. PRs (and comments) are welcome.
Calling `call` on a curried object is an efficient way to evaluate the function
These get the attributes from the `functools.partial` instance, and like `partial` instances, they are now read-only.
I performed the following benchmarks for old and new from toolz import curry
from functools import partial
def f(*args, **kwargs):
pass
c1 = curry(f)
p1 = partial(f)
%timeit p1()
%timeit c1()
if hasattr(c1, 'call'):
%timeit c1.call()
%timeit p1(1)
%timeit c1(1)
%timeit p1(1, 2)
%timeit c1(1, 2)
%timeit p1(x=1)
%timeit c1(x=1)
%timeit p1(x=1, y=2)
%timeit c1(x=1, y=2)
c2 = curry(f, 1, 2)
p2 = partial(f, 1, 2)
%timeit p2()
%timeit c2()
%timeit p2(3)
%timeit c2(3)
%timeit p2(3, 4)
%timeit c2(3, 4)
%timeit p2(x=3)
%timeit c2(x=3)
%timeit p2(x=3, y=4)
%timeit c2(x=3, y=4)
c3 = curry(f, a=1, b=2)
p3 = partial(f, a=1, b=2)
%timeit p3()
%timeit c3()
%timeit p3(3)
%timeit c3(3)
%timeit p3(3, 4)
%timeit c3(3, 4)
%timeit p3(x=3)
%timeit c3(x=3)
%timeit p3(x=3, y=4)
%timeit c3(x=3, y=4) |
While I like the idea of precomputing a partial and keeping it around in call I think I'm mostly against this change. To me a 2-3x slowdown on curried function calling is a big deal. I actually use At the same time I only rare notice that I've been given a Anyway, I'm -0.5 on this as is. Thoughts? |
1. Once again, `Curry` is a class and `curry` is a function. 2. `Curry`and `curry` may return a partial instance if there is a single argument remaining and no keywords. 3. `curried_object.call` was changed to `curried_object._call`. As per usual convention, `_call` is an implementation detail that may occasionally be useful (as it is explicit and faster), but there are no guarantees that it will remain in future releases of `toolz`.
Take two. See latest push. I'll discuss more later. |
I expected this PR would need vetting, and I'm sure it will continue to develop. I agree that a factor of 2x is important. One approach to achieve this is to sometimes return a Let me illustrate what prompted some of the changes in this PR: curried_get = curry(get)
fast_cheetah = curried_get('cheetah')
fast_cheetah(zoo) # this is a fast partial
slow_cheetah = curry(get, 'cheetah')
slow_cheetah(zoo) # this is a slow curry with the current implementation While developing the @curry
def memoize(f, cache=None, key=None):
...
add_cache = {}
@memoize(cache=add_cache)
def add(x, y):
return x + y
I'll continue this discussion later. Allowing |
…#155 pytoolz#159 1. Idempotent such that `curry(curry(f))` is equivalent to `curry(f)`; see pytoolz#147 2. Comparable via equality and hashable based on `func`, `args`, and `keywords`; see pytoolz#159 3. `func`, `args`, and `keywords` attributes are readonly 4. `__name__` and `__doc__` attributes are writable 5. More efficient implementation using `partial`; see pytoolz#150 6. Curried objects never transmogrigy into `partial` objects; see pytoolz#155
Closing. Superseded by #170. |
curry
instances always return othercurry
instance if the arguments are not fully applied; previously, they could sometimes returnfunctools.partial
instances.The performance of calling
curry
instances is better than before, but still not as good as callingpartial
. To achieve the performance ofpartial
, usecurried_func.call
.This PR addresses the idempotency issue from #147. It does not, however, split
curry
intoclass Curry/def curry
as discussed in #147 (or as indicated by the name of this branch). I thinkcurry
performs just fine as a single class.