Re: Small change to utils.functional.curry
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 "", 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 -~--~~~~--~~--~--~---
Re: Small change to utils.functional.curry
Marc D.M. wrote: > On Thu, 2006-09-07 at 13:28 -0500, Adrian Holovaty wrote: >> On 9/7/06, Michael Spencer <[EMAIL PROTECTED]> wrote: >> >>> 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.: >>> [...] >>> 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. >> I was with you until "it changes the type of the curry from function >> to bound method." How does it do that? >> >> Adrian > > I think what's going on is that in the try : except clause, the return > value is the actual bound method, retrieved using the __get__ > magic-method. > Yep. > And it doesn't work very well that way either. I've tried Michael's > method and the whole house comes tumbling down. See traceback below when > trying to load a flatpage. > [snip...house falling down] I should have included a guard to ensure that the shortcut applies only to functions, not bound methods. Something like the following... import types def curry3(_curried_fct, *args, **kwargs): if len(args) == 1 and type(_curried_fct) is types.FunctionType: # 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 still just a sketch, but my own django app runs with curry defined this way. 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 -~--~~~~--~~--~--~---
Re: Small change to utils.functional.curry
Marc D.M. wrote: > On Wed, 2006-09-06 at 22:49 -0700, Michael Spencer wrote: >> 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... > > ...it changes the type of the curry from function to bound >> method. >> > I like this idea, but I'm curious about that last sentence. Is this > necessarily a bad thing? Or maybe I should ask, why does it matter, > function vs bound method. Aren't they both callable? > > Or am I missing something? > Yes, they're both callable. The differences are likely to matter in edge cases - but you found one already: the behavior of the __get__ 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 -~--~~~~--~~--~--~---