[Python-Dev] PEP 309

Raymond Hettinger python at rcn.com
Sun Feb 27 01:26:11 CET 2005


> But this all reminds me of the discussion
> over itemgetter/attrgetter. They also special-case particular uses of
> lambda, and in those cases the stated benefits were speed and
> (arguably) readability (I still dislike the names, personally).

I wouldn't use those as justification for partial().  The names suck and
the speed-up is small.  They were directed at a specific and recurring
use case related to key= arguments.



> I think partial hits a similar spot - it covers a fair number of
> common cases, 

Are you sure about that?  Contriving examples is easy, but download a
few modules, scan them for use cases, and you may find, as I did, that
partial() rarely applies.  The argument order tends to be problematic.

Grepping through the standard library yields no favorable examples.  In
inspect.py, you could replace "formatvarkw=lambda name: '**' + name"
with
"partial(operator.add, '**') but that would not be an improvement.

Looking through the builtin functions also provides a clue:

  cmp(x,y)	      # partial(cmp, refobject) may be useful.
  coerce(x,y)     # not suitable for partial().
  divmod(x,y)     # we would want a right curry.
  filter(p,s)     # partial(filter, p) might be useful.
  getattr(o,n,d)  # we would want a right curry.
  hasattr(o,n)    # we would want a right curry.
  int(x,b)        # we would want a right curry.
  isinstance(o,c) # we would want a right curry.
  issubclass(a,b) # we would want a right curry.
  iter(o,s)       # we would want a right curry.
  long(x,b)       # we would want a right curry.
  map(f,s)        # partial(map, f) may be useful.
  pow(x,y,z)      # more likely to want to freeze y or z.
  range([a],b,[c])# not a good candidate.
  reduce(f,s,[i]) # could work for operator.add and .mul
  round(x, n)     # we would want a right curry.
  setattr(o,n,v)  # more likely to want to freeze n.
 


> the C implementation is quoted as providing a speed
> advantage over lambda. 

Your recent timings and my old timings show otherwise.



> Seriously, not needing to explicitly handle *args and **kw
> is a genuine readability benefit of partial.

I hope that is not the only real use case.  How often do you need to
curry a function with lots of positional and keyword arguments?  Even
when it does arise, it may a code smell indicating that subclassing
ought to be used.



> Now I wonder. Are my tests invalid, did lambda get faster, or is the
> "lambda is slow" argument a myth?

The test results are similar to what I got when I had tested the version
proposed for Py2.4.  The lambda version will win by an even greater
margin if you put it on an equal footing by factoring out the attribute
lookup with something like f=t.f.

Calling Python functions (whether defined with lambda or def) is slower
than C function calls because of the time to setup the stack-frame.
While partial() saves that cost, it has to spend some time building the
new argument tuple and forwarding the call.  You're timings show that to
be a net loss.

Sidenote:  Some C methods with exactly zero or one argument have
optimized paths that save time spent constructing, passing, and
unpacking an argument tuple.  Since partial() is aimed at multi-arg
functions, that part of "lambda is slower" is not relevant to the
comparison.



> Hmm, I'm starting to go round in circles here. 

I also wish that partial() ran faster than closures, that it didn't have
limitations, and that it applied in more situations.  C'est le vie.



Raymond



More information about the Python-Dev mailing list