Adrian Holovaty wrote:
> On 9/6/06, Martin <[EMAIL PROTECTED]> wrote:
>> def curry(fct, *args, **kwargs):
>>     def _curried(*moreargs, **morekwargs):
>>         return fct (* (args+moreargs), **dict(kwargs, ** morekwargs))
>>     return _curried
>>
>> It's just a performance issue (saving a few list operations) andbecause
>> I have to use CGI I care about performance at bit more than others ...
> 
> Thanks for the great catch and fix, Martin! I've checked in your
> change in changeset 3733. Please do let us know if you find any other
> places we could optimize.
> 
> Adrian
> 
Martin's version is indeed faster, but has a 'gotcha' in that it precludes 
passing a keyword arguments named 'fct' to a curried function...

 >>> def curry1(*args, **kwargs):
...     def _curried(*moreargs, **morekwargs):
...         return args[0](*(args[1:]+moreargs), **dict(kwargs.items() +
...     morekwargs.items()))
...     return _curried
...
...
 >>> def curry2(fct, *args, **kwargs):
...     def _curried(*moreargs, **morekwargs):
...         return fct (* (args+moreargs), **dict(kwargs, ** morekwargs))
...     return _curried
...

Check the speed up...

 >>> def adder(a,b,c,d):
...     return a+b+c+d
...
 >>> adder_cur1 = curry1(adder, 1, 2)
 >>> adder_cur2 = curry2(adder, 1, 2)
 >>> shell.timefunc(adder_cur1, 3, 4)
'_curried(...)  37276 iterations, 13.41usec per call'

 >>> shell.timefunc(adder_cur2, 3, 4)
'_curried(...)  54986 iterations, 9.09usec per call'

which is about 30% faster in this case


However...

 >>> def adder2(a,b,c,d, fct=sum):
...     return fct([a,b,c,d])
...
 >>> import sys
 >>> p1 = curry1(1,2, fct="".join)
 >>> p2 = curry2(1,2, fct="".join)
...
Traceback (most recent call last):
   File "<input>", line 1, in ?
TypeError: curry2() got multiple values for keyword argument 'fct'
 >>>

Oops.

Better to rename fct to something less likely to clash, such as _curried_fct

If you're really hunting for speed, there is a significant boost available in 
the common special case of a python function curried with one positional 
argument.  In this case, you can [ab]use the method binding machinery, e.g.:

def curry3(_curried_fct, *args, **kwargs):
     if len(args) == 1:
         # Special case where we try to abuse the descriptor
         try:
             return _curried_fct.__get__(args[0])
         except AttributeError:
             # built-ins fail - handle them in the normal way
             pass

     def _curried(*moreargs, **morekwargs):
         return _curried_fct (*(args+moreargs), **dict(kwargs, ** morekwargs))

     return _curried


This is about twice as fast as curry2 for the case of a python function curried 
with one argument. It also offers better introspection than either curry1 or 
curry2, since the original signature is preserved.  It's a bigger change than 
the curry2 though, since it changes the type of the curry from function to 
bound 
method.

Michael


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to django-developers@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/django-developers
-~----------~----~----~----~------~----~------~--~---

Reply via email to