[Python-Dev] PEP 457: Syntax For Positional-Only Parameters (original) (raw)

Steven D'Aprano steve at pearwood.info
Wed Oct 9 02:15:40 CEST 2013


On Wed, Oct 09, 2013 at 01:33:26AM +0200, Larry Hastings wrote:

This PEP proposes a syntax for positional-only parameters in Python. Positional-only parameters are parameters without an externally-usable name; when a function accepting positional-only parameters is called, positional arguments are mapped to these parameters based solely on their position.

Nicely done. A few comments follow:

Positional-only parameters can be optional, but the mechanism is significantly different from positional-or-keyword or keyword-only parameters. Positional-only parameters don't accept default values. Instead, positional-only parameters can be specified in optional "groups". Groups of parameters are surrounded by square brackets, like so::

def addch([y, x,] ch, [attr], /):

I think you need to explain the motivation for this, particularly in light of the fact (below) that they will in fact receive a default value, namely the proposed "undefined" singleton.

Personally, if I have a function like this:

def mypower(x, y, [n,] /): if n is undefined: n = 1 ...

I would much prefer to write it like this:

def mypower(x, y, [n=1,] /): ...

The PEP should explain why I cannot, or accept that I should be able to.

* For clarity and consistency, the comma for a parameter always comes immediately after the parameter name. It's a syntax error to specify a square bracket between the name of a parameter and the following comma. (This is far more readable than putting the comma outside the square bracket, particularly for nested groups.)

I note that in your example above, you put the comma outside the square bracket:

def addch([y, x,] ch, [attr], /):

which seems perfectly readable to me.

I think that a much better explanation for prohibiting the comma outside the brackets is logical consistency: if you put the comma outside the brackets, and the attr is left out, you get two commas in a row and we don't want that.

It's possible to specify a function prototype where the mapping of arguments to parameters is ambiguous. Consider::

def range([start,] stop, [range], /):

And you do it again, putting the comma outside of the square brackets :-)

If we decide to implement positional-only parameters in a future version of Python, we'd have to do some additional work to preserve their semantics. The problem: how do we inform a parameter that no value was passed in for it when the function was called?

The obvious solution: add a new singleton constant to Python that is passed in when a parameter is not mapped to an argument. I propose that the value be called called undefined, and be a singleton of a special class called Undefined. If a positional-only parameter did not receive an argument when called, its value would be set to undefined.

I would much prefer Undefined and UndefinedType. That matches other singletons like None, NotImplemented, Ellipsis, even True and False.

What (if any) methods and attributes would Undefined have? If it doesn't have any, doesn't that make it functionally equivalent to None? Why not just use None? The PEP needs to explain why it needs to invent yet another singleton that quacks like None.

[Bikeshed: perhaps Missing is more appropriate than Undefined? After all, the parameter is defined, only the value is missing.]

But this raises a further problem. How do can we tell the difference between "this positional-only parameter did not receive an argument" and "the caller passed in undefined for this parameter"?

Why treat this as a problem to be solved? If somebody wants to go to the trouble of writing:

addchr('c', Undefined)

instead of just:

addchr('c')

why not let them?

It'd be nice to make it illegal to pass undefined in as an argument to a function--to, say, raise an exception. But that would slow Python down, and the "consenting adults" rule appears applicable here. So making it illegal should probably be strongly discouraged but not outright prevented.

An argument for allowing Undefined (or None, as the case may be): sometimes you have a situation like this:

c = get_char() attr = get_attr() if attr is Undefined: # or None addchr(c) else: addchr(c, attr)

That sucks. But this isn't much better:

c = get_char() attr = get_attr() if attr is Undefined: # or None t = (c,) else: t = (c, attr) addchr(*t)

Being able to pass Undefined explicitly avoids this ugliness.

However, it should be allowed (and encouraged) for user functions to specify undefined as a default value for parameters.

Because I think this is important, I'm going to raise it again: I think it is important for the PEP to justify why user functions cannot specify arbitrary values as defaults, not just Undefined.

Thanks again for tackling this.

-- Steven



More information about the Python-Dev mailing list