On Sat, Jul 27, 2019 at 12:47:39PM -0000, Dominik Vilsmeier wrote:
I propose adding a function to `functools` that works with placeholders and thus offers even greater flexibility. The Ellipsis literal `...` seems a intuitive choice for that task. When eventually calling such a "partial placeholder" object, it would fill in placeholders from the left and add remaining `args` to the right. In terms of implementation this can be realized as a subclass of `partial` itself.
If you're going to add support for a placeholder, there is no need to create a new kind of partial function?
Support for a placeholder is completely backwards compatible, if you use a brand new special sentinel that doesn't yet exist.
# May want to give it a nicer repr, but this is the # minimal version that will work. SKIP = object()
There's only one public function, ``partial``, regardless of whether the caller uses the sentinel or not:
def function(a, b, c, d): pass
# Existing use is unchanged: from functools import partial partial(function, 1, 2) # Fill in a, b.
# New functionality is triggered by the use of the sentinel: from functools import partial, SKIP partial(function, SKIP, 1, SKIP, 2) # Fill in b, d.
If you don't like the use of a named sentinel, we could still use Ellipsis, but that will break anyone's partial functions that happen to already use Ellipsis as a value. That will probably require a warning in 3.9 and the new functionality only gets added in 3.10.
Sure we could also use a `lambda` instead
I believe that partial is faster than lambda.
[steve@ando cpython]$ ./python -m timeit \ -s "from functools import partial" \ -s "f = partial(lambda x,y: x+y, 1)" \ "f(100)" 100000 loops, best of 5: 1.98 usec per loop
[steve@ando cpython]$ ./python -m timeit \ -s "g = lambda x,y: x+y" \ -s "f = lambda a: g(1, a)" \ "f(100)" 100000 loops, best of 5: 2.44 usec per loop
To avoid the name lookup of "g", I tried this, but it was even slower:
[steve@ando cpython]$ ./python -m timeit \ -s "f = lambda a: (lambda x,y: x+y)(1, a)" \ "f(100)" 100000 loops, best of 5: 3.4 usec per loop
(Not surprising, as we've changed a name lookup into creating a new function object.)
Obviously this example is simple enough that we don't need partial at all, but its just an illustration demonstrating that partial seems to have lower overhead than normal function objects.