Open dg-pb opened 1 month ago
Note, this has been discussed and rejected multiple times in the past. Please specifically address these comments:
Also see:
Leaving this open for further comment and reconsideration by other developers.
[quote="Raymond Hettinger, post:20, topic:19163, username:rhettinger"] I’m 100% opposed to including better-partial or anything like it in the standard library. At best, it would be an attractive nuisance. [/quote]
In https://discuss.python.org/t/functools-partial-placeholder-arguments/53487 I have laid out several examples and use cases for this. But the main gain here is speed improvement by being able to efficiently utilise functions from operator
module to use as predicates to functionals, where argument order is not convenient. I have been lately working on iterator
recipes and from my experience the partial
wrap is 20 ns vs lambda call
being ~50ns can result in considerable speed-ups. Especially if it is used more than once.
[quote="Raymond Hettinger, post:9, topic:19163, username:rhettinger"]
Every variant I’ve seen is more complex, less speedy, and harder to debug than a simple def
or lambda
. Every variant required something extra to learn and remember, unlike def
or lambda
which are Python basics.
[/quote]
functools.partial
as it is now.p3 = partial2(opr.sub, _, 1)
print(inspect.signature(p3.func)) # <Signature (a, b, /)>
print(p3.args) # (<partial2.Placeholder object at 0x111e95ad0>, 1)
Placeholder sentinel still needs work, it would have much nicer representation. E.g: (functools.PPHoldder, 1)
Personally, I would make use of this functionality in algorithm recipes, iterator recipes, utility functions and similar places of lower level code where performance is important. And I don't see this as lambda
or def replacement, but rather functionality that is well suited for a clear purpose.
Also, partial
already exists with many of the problems that are being used against this functionality. But given the fact that partial
exists already why not make it better and make use of it in the area where it excels (performance)?
Why I think this deserves reconsideration is that this hasn't been implemented and benchmarked before. All the previous attempts were in python, where instead of performance gain which has led me to this point, the result was a significant performance loss - up to 20x slower than current standard library implementation. So naturally, those are not used, because lambda
/def
is a simpler, much faster than better_partial
or any other 3rd party implementation and has all the flexibility.
While before supporting reasoning was convenience, Linters, MyPy issues and similar, my main argument is performance.
I've left this open for comment and no one else has stepped in to opine, so I'm going to go ahead and approve it.
The API is a minimal addition that gives more flexibility without venturing into creating a mini-DSL. It is unclear whether it will present any typing challenges beyond what partial()
already does, but I expect that will only be a minor obstacle.
The PR needs a lot of work but the concept is sound. I've been trying it out for a while and it looks reasonable in real code. It supports multiple placeholders but in the examples I've found only one is needed.
I'm dubious about the motivation as an optimization (PyPy doesn't need this and CPython may yet optimize a plain lambda
version). However, it does make partial
more broadly useful and that is a sufficient reason.
FWIW, the example I most enjoyed is bin8 = partial(format, Placeholder, '08b')
.
Personally, most of the time I need a placeholder for is when I want right partialization, not left partialization. While there are times where partialization in arbitrary order makes sense, an alternative would to provide rpartial
interface without introducing new singletons or immortal objects. The specs on how to use rpartial
would still remain an open question (i.e., is rpartial(f, a, b)(x)
for f(x, a, b)
or f(x, b, a)
?)
rpartial()
could be simpler, and it would cover most of the use cases, but the placeholder provides more flexibility. If Raymond is fine with this, we should choose this option.
Although, there is a case which is supported by rpartial()
, but not be the proposed feature with a placeholder -- when we want to add positional arguments after the variable number of positional arguments for the partial function. I think that we could support *Placeholder
as a placeholder for variable number of arguments. But this can be implemented in a separate issue, for the first step it is enough to support Placeholder
for individual arguments.
I like functional programming a lot (proof: https://github.com/dry-python/returns), but I don't think that this API is nice. Here are my thoughts:
_
usage in argument passing, because right now _
marks an unused variable for most cases.partial
is already special-cased in mypy (https://github.com/python/mypy/blob/master/mypy/plugins/functools.py), it would make the plugin even more complexIt is nice as a separate package, but I don't feel like I would like to use it in the stdlib. Or teach how to use it in the stdlib. Sorry :(
5. This solution is very specific: only right positional arguments.
This is either not correct or I am failing to understand what you mean.
Feature or enhancement
Proposal:
I know that this has been brought up in the past, but I would appreciate if this was reconsidered. I would be happy to bring this up to a nice level.
The logic is as follows:
vectorcall
change looks like:Has this already been discussed elsewhere?
I have already discussed this feature proposal on Discourse
Links to previous discussion of this feature:
https://discuss.python.org/t/functools-partial-placeholder-arguments/53487
Linked PRs