I’m a big fan of functools.partial myself. It allows you to take a function, preset some of its arguments and return another function which you can call with more arguments. Not sure if currying is the right term here, but I’ve heard people refer to functools.partial like that.
Today while browsing the source code for the futures module I came across this class:
[gist]https://gist.github.com/4048428[/gist]
The moment I saw that func, args and kwargs were saved as attributes in the instance I thought: “why not use partial and save a single attribute?”. Then I thought that maybe performance had something to do here, so I wrote a dead simple stupid test to check it out:
[gist]https://gist.github.com/4048405[/gist]
Here are the results using CPython 2.7.3:
In [3]: %timeit testpartial.do_test(testpartial.WorkItem)
100000 loops, best of 3: 5.26 us per loop
In [4]: %timeit testpartial.do_test(testpartial.WorkItemPartial)
100000 loops, best of 3: 2.65 us per loop
We are talking microseconds here, but still the version with partial is almost twice as fast.
Now, lets see how PyPy performs:
In [9]: %timeit -n 10000 testpartial.do_test(testpartial.WorkItem)
10000 loops, best of 3: 154 ns per loop
Compiler time: 554050.78 s
In [10]: %timeit -n 10000 testpartial.do_test(testpartial.WorkItemPartial)
10000 loops, best of 3: 2.49 us per loop
Fun, the version without partial goes into the nanoseconds! And the one with partial doesn’t improve much with regards to CPython. Interesting.
So what’s the take here? Well, whenever I see fit I use partial, code looks nicer and it’s apparently faster, so why not? 🙂
:wq
It is a bit less readable for the not so fond of functional programming right? I guess it is a trader off between code complexity and performance.
And apparently it saves you a stack frame!
http://tech.pro/tutorial/1520/partial-function-application-in-python
Pypy 2.1 (please note that the order of the tests is changed)
In [2]: %timeit testpartial.do_test(testpartial.WorkItemPartial)
1000000 loops, best of 3: 535 ns per loop
In [3]: %timeit testpartial.do_test(testpartial.WorkItem)
10000000 loops, best of 3: 58.5 ns per loop
Interesting 🙂 Still, saving a stack frame is nice for traceback printing 😉