functools.partial placeholders
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.
from functools import partial from partial2 import partial as partial2, Placeholder _ = VOID = Placeholder() %timeit partial(opr.sub, 1, 2) # 130 ns %timeit partial2(opr.sub, 1, 2) # 155 ns p1 = partial(opr.sub, 1) p2 = partial2(opr.sub, 1) p3 = partial2(opr.sub, _, 1) p4 = partial2(opr.sub, VOID, 1) print(p1(2)) # -1 print(p2(2)) # -1 print(p3(2)) # 1 print(p4(2)) # 1 %timeit p1(2) # 48 ns %timeit p2(2) # 52 ns %timeit p3(2) # 54 ns %timeit p4(2) # 54 ns
The logic is as follows:
def partial(func, *p_args, **p_kwds): p_args = list(p_args) nvoid = sum(a is VOID for a in p_args) arng = range(len(p_args)) def wrapper(*args, **kwds): if len(args) < nvoid: raise ValueError('Not all VOIDs are filled') t_args = p_args.copy() j = 0 for i in arng: if p_args[i] is VOID: t_args[i] = args[j] j += 1 t_args.extend(args[j:]) return func(*t_args, **{**p_kwds, **kwds}) return wrapper
vectorcall change looks like:
if (np) { nargs_new = pto_nargs + nargs - np; Py_ssize_t ip = 0; for (Py_ssize_t i=0; i < pto_nargs; i++) { if (PyObject_TypeCheck(pto_args[i], &placeholderType)){ memcpy(stack + i, args + ip, 1 * sizeof(PyObject*)); ip += 1; } else { memcpy(stack + i, pto_args + i, 1 * sizeof(PyObject*)); } } if (nargs_total > np){ memcpy(stack + pto_nargs, args + np, (nargs_total - np) * sizeof(PyObject*)); } // EO Placeholders ------ } else {
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