This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Give Partial the ability to skip positionals
Type: enhancement Stage:
Components: None Versions:
process
Status: closed Resolution: rejected
Dependencies: Superseder:
Assigned To: Nosy List: belopolsky, ironfroggy, jpe, loewis, rhettinger
Priority: normal Keywords:

Created on 2007-04-24 00:18 by ironfroggy, last changed 2022-04-11 14:56 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
partial_skip.patch ironfroggy, 2007-04-24 03:45 Patch implementing the requested feature
Messages (13)
msg55079 - (view) Author: Calvin Spealman (ironfroggy) Date: 2007-04-24 00:18
There are some situations where you want to skip positional arguments in a use of a partial function. In other words, you want to create a partial that applies positional arguments out of order or without applying values to one or more lower positional arguments. In some cases keyword arguments can be used instead, but this has two obvious drawbacks. Firstly, it causes the caller to rely on the name of a positional in a callee, which breaks encapsulation. Secondly, on the case of the function being applied to being a builtin, it fails completely, as they will not take positional arguments by name at all. I propose a class attribute to the partial type, 'skip', which will be a singleton to pass to a partial object signifying this skipping of positionals. The following example demonstrates.

from functools import partial

def add(a, b):
    return a + b

append_abc = partial(add, partial.skip, "abc")
assert append_abc("xyz") == "xyzabc"

Obviously this example would break if used as partial(add, b="abc") and the maintainer of add changed the positional names to 'first' and 'second' or 'pre' and 'post', which is perfectly reasonable. We do not need to expect the names of our positional arguments are depended upon. It would also break when someone gets smart and replaces the add function with operator.add, of course.
msg55080 - (view) Author: Calvin Spealman (ironfroggy) Date: 2007-04-24 03:45
File Added: partial_skip.patch
msg55081 - (view) Author: Martin v. Löwis (loewis) * (Python committer) Date: 2007-04-24 05:41
I would not call it partial.skip, but partial.unbound (or find yet a better name that indicates that the argument is not skipped, but instead will be an argument of the resulting partial function).
msg55082 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2007-04-26 02:31
Names I've seen used for this purpose elsewhere: slot, arg, missing.
msg55083 - (view) Author: Calvin Spealman (ironfroggy) Date: 2007-04-26 02:58
If anyone has a serious argument against the partial.skip name, I would take partial.unbound as my second choice, but I definitely prefer partial.skip to it. Third would be partial.latebound. I still can't figure out how my code broke subclassing of partial, so if anyone can take a look, I'd appreciate it hugely.
msg55084 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2007-04-26 03:21
The current patch does not compile:

Modules/_functoolsmodule.c: In function 'init_functools':
Modules/_functoolsmodule.c:306: error: too many arguments to function 'PyObject_CallObject'

Once I removed the extra NULL argument, it seems to work fine.  What exactly is broken?  Can you add unit tests for the new functionality?
msg55085 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2007-04-26 03:34
OK, I've got it.  Your patch breaks test_functools.  This is because you have blown up partial_type.tp_dict . 
msg55086 - (view) Author: Calvin Spealman (ironfroggy) Date: 2007-04-26 04:10
Hmm, I didn't get such an error here on VC with the extra argument. I'll change that here, too.

I figured the breaking of the subclassing of partial was related to what I do with tp_dict, but I don't understand how. How did I blow it up? And, yes, I will write tests for the new functionality, of course.
msg55087 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2007-04-26 04:14
If you remove partial_type.tp_dict = PyDict_New(); at line 309, the patch will pass test_functools.

A few comments:

Design:
1. partial.skip should be an instance of a singleton class (look at NoneType implementation in object.c)
2. repr(partial.skip) should be 'functools.partial.skip' (easy to implement once #1 is done)

Implementation:
1.  In the loop over pto->args you know that i < npargs, so you can use PyTuple_GET_ITEM and there is no need to check for arg==NULL
2. You should check PyTuple_GetItem(args, pull_index) for null and return with error if too few arguments is supplied.  Better yet, find number of supplied args outside the loop and raise your own error if pool_index grows to that number.
3. It looks like you are leaking references. I don't see where you decref ptoargscopy and arg after concatenation.
msg55088 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2007-04-26 06:15
-1 on the concept for this patch.  We're working too hard to avoid simple uses of lambda or def.  The additonal complexity and impact on readability isn't work it.
msg55089 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2007-04-26 06:32
I am actually -1 on the concept as well.  If you go to the trouble of having a skip singleton in the language, then partial application syntax should just be call syntax as in add(skip, 2). However this will not be python anymore.  Other languages that have partial application support use special syntax such as add(,2) or add(:,2).  Since adding syntax is out of the question, it is hard to argue that partial(add, partial.skip, 2) is so much better than lambda x: add(x,2).
msg55090 - (view) Author: Martin v. Löwis (loewis) * (Python committer) Date: 2007-04-26 07:50
Based on this feedback, I'm rejecting the patch. Thanks for proposing it, anyway.
msg81092 - (view) Author: John Ehresman (jpe) * Date: 2009-02-03 19:26
Commenting because this was brought up on python-dev -- I'd like this,
primarily for the string method & itertools optimization.  It's ugly,
but it would be better than writing C code.  The alternative might be to
somehow optimize trivial functions to execute without bytecode, but that
would be considerably harder.
History
Date User Action Args
2022-04-11 14:56:23adminsetgithub: 44888
2009-02-03 19:26:11jpesetnosy: + jpe
messages: + msg81092
2007-04-24 00:18:53ironfroggycreate