osdir.com


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Explicit vararg values


I started to send this to python-ideas, but I'm having second thoughts.
Does tihs have merit?

---
I stumble on this a lot, and I see it in many python libraries:

def f(*args, **kwargs):
    ...

f(*[list comprehension])
f(**mydict)

It always seems a shame to carefully build up an object in order to explode
it, just to pack it into a near-identical object.

Today I was fiddling with the new python3.7 inspect.signature functionality
when I ran into this case:

def f(**kwargs): pass
sig = inspect.signature(f)
print(sig.bind(a=1, b=2))

The output is "<BoundArguments (kwargs={'a': 1, 'b': 2})>". I found this a
bit humorous since anyone attempting to bind values in this way, using
f(kwargs={'a': 1, 'b': 2}) will be sorely dissappointed. I also wondered
why BoundArguments didn't print '**kwargs' since that's the __str__ of that
parameter object.

The syntax I'm proposing is:
   f(**kwargs={'a': 1, 'b': 2})

as a synonym of f(a=1, b=2) when an appropriate dictionary is already on
hand.

---
I can argue for this another way as well.

1)
When both caller and callee have a known number of values to pass/receive,
that's the usual syntax:
def f(x) and f(1)

2)
When the caller has a fixed set of values, but the callee wants to handle a
variable number:   def f(*args) and f(1)

3)
Caller has a variable number of arguments (varargs) but the call-ee is
fixed, that's the splat operator: def f(x) and f(*args)

4)
When case 1 and 3 cross paths, and we have a vararg in both the caller and
callee, right now we're forced to splat both sides: def f(*args) and
f(*args), but I'd like the option of opting-in to passing along my list
as-is with no splat or collection operations involved: def f(*args) and
f(*args=args)

Currently the pattern to handle case 4 neatly is to define two versions of
a vararg function:

def f(*arg, **kwargs):
    return _f(args, kwargs)

return _f(args, kwargs):
    ...

Such that when internal calllers hit case 4, there's a simple and efficient
way forward -- use the internal de-vararg'd  definition of f. External
callers have no such option though, without breaking protected api
convention.

My proposal would simplify this implementation as well as allowing users to
make use of a similar calling convention that was only provided privately
before.

Examples:

log(*args) and _log(args) in logging.Logger
format and vformat of strings.Formatter