# [Python-3000] PEP 3102

Joel Bender jjb5 at cornell.edu
Fri Feb 15 17:46:28 CET 2008

```Dj Gilcrease wrote:
> On Thu, Feb 14, 2008 at 6:04 PM, Greg Ewing <greg.ewing at canterbury.ac.nz> wrote:
>> Guido van Rossum wrote:
>>  > Now can you come up with a syntax for positional-only
>>  > arguments? So far everybody has failed at that, and there are some use
>>  > cases where it's useful too.
>
>
> throwing some ideas around...

How about a BNFish notation?  Use brackets around optional parameters.

def test([arg1, [arg2,]] arg3):
...

I haven't found an example of what the value of an unspecified
positional only argument would be, I think it should be required:

def test([arg1=1,] arg2): return (arg1, arg2)

This could also accommodate trailing positional only args, even though

def test(arg1 [, arg2=2]): return (arg1, arg2)
assert test(1) == (1, 2)
assert test(1,3) == (1, 3)

If both were provided:

def test([arg1=1,] arg2 [, arg3=3]): ... # error

I would expect it to be an "ambiguous combination of optional
positional-only arguments" syntax error (as opposed to runtime error).

If you wanted arg2 specified when arg1 is:

def test([arg1=1, arg2=2,] arg3): return (arg1, arg2, arg3)
assert test(9) == (1, 2, 9)
assert test(7, 8, 9) == (7, 8, 9)

test(8, 9)                   # error

With nesting you can specify which one is more optional:

def test([[arg1=1,] arg2=2,] arg3): return (arg1, arg2, arg3)
assert test(9) == (1, 2, 9)
assert test(8, 9) == (1, 8, 9)

def test([arg1=1, [arg2=2,]] arg3): return (arg1, arg2, arg3)
assert test(8, 9) == (8, 2, 9)

Capturing all of the args except the last one:

def test([*arg1,] arg2): return (arg1, arg2)
assert test(9) == ((), 9)
assert test(8, 9) == ((8,), 9)
assert test(7, 8, 9) == ((7, 8), 9)

First is a little more important:

def test([arg1=1, *arg2,] arg3): return (arg1, arg2, arg3)
assert test(9) == (1, (), 9)
assert test(8, 9) == (8, (), 9)
assert test(7, 8, 9) == (7, (8,), 9)

Force the end of positional arguments to switch to keywords:

def test([*arg1,] arg2, *, arg3=3): return (arg1, arg2, arg3)

Specifying more than one dumping spot for additional positional
parameters is ambiguous:

def test([arg1=1, *arg2,] arg3, *arg4): ... # error

Specifying default values for a positional argument may make it ambiguous:

def test([arg1=1,] arg2, arg3=3): return (arg1, arg2, arg3)

This is OK, but not recommended, it's more complicated than it needs to be:

def test(arg1 [, arg2=1 [, arg3=2]]): return (arg1, arg2, arg3)

And now for something completely different:

def test(arg1 [[, arg2=2], arg3=3]): return (arg1, arg2, arg3)
assert test(8) == (8, 2, 3)
assert test(8, 9) == (8, 2, 9)
assert test(7, 8, 9) == (7, 8, 9)

Like the other dumping spot:

def test(arg1 [[, *arg2], arg3=3]): return (arg1, arg2, arg3)
assert test(8) == (8, (), 3)
assert test(8, 9) == (8, (), 9)
assert test(7, 8, 9) == (7, (8,), 9)

Nested galore:

def test([arg1=1, [[*arg2,] arg3=3,]] arg4):
return (arg1, arg2, arg3, arg4)
assert test(7) = (1, (), 3, 7)
assert test(7, 8) = (7, (), 3, 8)
assert test(7, 8, 9) = (7, (), 8, 9)
assert test(7, 8, 9, 10) = (7, (8,), 9, 10)

The notation for an optional tuple that gets ripped apart seems busy,
but is still grokable:

def test([(arg1, arg2)=(1, 2),] arg3): return (arg1, arg2, arg3)
assert test(8) == (1, 2, 8)
assert test((7, 8), 9) == (7, 8, 9)

Joel
```