Thanks to visit codestin.com
Credit goes to github.com

Skip to content

functools.partial: Allowing trailing Placeholders #128644

Closed as not planned
Closed as not planned
@dg-pb

Description

@dg-pb

Feature or enhancement

Proposal:

from functools import partial, Placeholder

def foo(a):
    return None

# Now
partial(foo, Placeholder)   # TypeError

# After
partial(foo, Placeholder)    # Success
# Also, converts all Placeholder arguments to positional only

1. Rationale

So, I considered automatic trimming of trailing Placeholders instead of forbidding such. However it didn't sit well with me.

So I thought this over. Not having trailing Placeholders works well, however both approaches of achieving this have flaws:
a) Forbidding them - results in unexpected errors for the user.
b) Trimming automatically - obfuscates inner workings by implicit input modifications.

Furthermore, it sometimes could be desired to have a trailing Placeholder to convert argument to positional only.

So I think it would be best not to have this restriction at all. This makes things more explicit and predictable. E.g. both would work:

class A:
    def func(self, a):
        pass

    p1 = partial(func)
    p2 = partial(func, Placeholder)

And as expected, self of p1 can be both positional or keyword, while self of p2 becomes restricted to positional only.

2. Implementation

This simplifies the code by not having to do error checks or trimming routines. Existing implementation handles trailing placeholders without modifications (except minor Fast Path fix in C).

3. Relation to partialmethod

This has come out during #124788, but it turned out to be orthogonal.

partialmethod stays as it was before - if no positionals provided argument kind of self is unaffected, while if args are provided then self becomes positional only.

4. Performance

Slightly faster for both cases (with and without Placeholders) when merging args of previous partial with new args.

S="
from functools import partial, Placeholder
def f(a, b):
    pass
p1 = partial(f, 1, 2)
p2 = partial(f, Placeholder, 2)
"
C0='partial(f)'
C1='partial(f, 1, 2)'
C2='partial(p1, 1, 2)'
C3='partial(p2, 1, 2)'
                            # BEFORE | AFTER
$PYEXE -m timeit -s $S $C0  # 135 ns | 135 ns
$PYEXE -m timeit -s $S $C1  # 170 ns | 170 ns
$PYEXE -m timeit -s $S $C2  # 195 ns | 165 ns
$PYEXE -m timeit -s $S $C3  # 195 ns | 165 ns

I think this is more flexible, simple and "straight" (I.e. If base logic is understood, the implications are clear and behaviour is predictable).

Has this already been discussed elsewhere?

This is a minor feature, which does not need previous discussion elsewhere

Or to be more precise, this is an update for new unreleased functionality.

Links to previous discussion of this feature:

No response

Linked PRs

Metadata

Metadata

Assignees

Labels

type-featureA feature request or enhancement

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions