Positional-only parameters

Hi, For technical reasons, many functions of the Python standard libraries implemented in C have positional-only parameters. Example: ------- $ ./python Python 3.7.0a0 (default, Feb 25 2017, 04:30:32)
"a".replace(old="x", new="y") # ERR! TypeError: replace() takes at least 2 arguments (0 given)
When converting the methods of the builtin str type to the internal "Argument Clinic" tool (tool to generate the function signature, function docstring and the code to parse arguments in C), I asked if we should add support for keyword arguments in str.replace(). The answer was quick: no! It's a deliberate design choice. Quote of Yury Selivanov's message: """ I think Guido explicitly stated that he doesn't like the idea to always allow keyword arguments for all methods. I.e. `str.find('aaa')` just reads better than `str.find(needle='aaa')`. Essentially, the idea is that for most of the builtins that accept one or two arguments, positional-only parameters are better. """ http://bugs.python.org/issue29286#msg285578 I just noticed a module on PyPI to implement this behaviour on Python functions: https://pypi.python.org/pypi/positional My question is: would it make sense to implement this feature in Python directly? If yes, what should be the syntax? Use "/" marker? Use the @positional() decorator? Do you see concrete cases where it's a deliberate choice to deny passing arguments as keywords? Don't you like writing int(x="123") instead of int("123")? :-) (I know that Serhiy Storshake hates the name of the "x" parameter of the int constructor ;-)) By the way, I read that "/" marker is unknown by almost all Python developers, and [...] syntax should be preferred, but inspect.signature() doesn't support this syntax. Maybe we should fix signature() and use [...] format instead? Replace "replace(self, old, new, count=-1, /)" with "replace(self, old, new[, count=-1])" (or maybe even not document the default value?). Python 3.5 help (docstring) uses "S.replace(old, new[, count])". Victor

I'm +0.5 to add positional-only parameters. Pros: * A lot of people don't know what '/' currently means in functions signatures rendered by `help` and docs. Because it's not a real syntax, it's really hard to find what it means. * Some APIs do look better with positional-only parameters, especially functions with one argument. * We already have this feature half-implemented: some builtin methods have positional-only arguments, inspect.signature API supports it already. Cons: * Function declarations will become a bit more complex, making a bump in Python learning curve. * Performance? I'm not sure if adding another set of checks will make a huge impact, but we'll need to benchmark this. Yury On 2017-02-28 4:17 PM, Victor Stinner wrote:

I think that was started as pep 457: https://www.python.org/dev/peps/pep-0457/ (Syntax For Positional-Only Parameters) Still informal. +1, it's likely possible to backport it to previous version using a decorator and faking __signature__. -- M On Tue, Feb 28, 2017 at 2:03 PM, Yury Selivanov <yselivanov.ml@gmail.com> wrote:

On 28 Feb 2017, at 22:17, Victor Stinner wrote:
Tangential to the main topic, but this module doesn’t enforce positional-only arguments. It allows you enforce keyword-only arguments like on Python 3: >>> from positional import positional >>> @positional(1) ... def replace(old, new): ... ... ... >>> replace(old='a', new='b') >>> replace('a', 'b') Traceback (most recent call last): File "<stdin>", line 1, in <module> File “…/site-packages/positional/__init__.py", line 97, in inner raise TypeError(message) TypeError: replace takes at most 1 positional argument (2 given) Frazer

On 28.02.2017 22:17, Victor Stinner wrote:
Keyword argument handling is comparatively slow and rarely needed for non-optional positional parameters of built-ins. You might make a case for optional ones, but then: how often are these used in practice to warrant the drop in performance ? Note: All this is different for Python methods/functions. The overhead of keyword parsing is small compared to what Python has to do to setup new frames for execution.
... and that's another reason why to avoid them: the naming of positional parameters was never really taken into account when writing the C functions and it does even change sometimes without warning.
+1
Using "count=-1" looks confusing when the method doesn't allow keyword arguments, so it's probably better to use the 3.5 style and document the defaults in the doc-string. AFAIR, some of these don't even have a (documented) default value. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Experts (#1, Feb 28 2017)
::: We implement business ideas - efficiently in both time and costs ::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/ http://www.malemburg.com/

I just noticed a module on PyPI to implement this behaviour on Python functions:
This library seem to do the opposite of what you ask and force kwarg only. In [11]: @positional.positional(1) ...: def foo(a=1,b=2): ...: print(a,b) ...: In [12]: foo(a=1,b=2) # should have raised. 1 2 In [13]: foo(1,b=2) 1 2 This seem to be the right thing: https://pypi.python.org/pypi/positionalonly In [1]: @positionalonly(1) ...: def foo(a=1,b=2): ...: print(a,b) In [2]: foo(1, 2) 1 2 In [3]: foo(a=1,b=2) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-3-78db88131ebc> in <module>() ----> 1 foo(a=1,b=2) /Users/bussonniermatthias/anaconda/lib/python3.5/site-packages/positionalonly/__init__.py in fun(*args, **kwargs) 208 ]) 209 raise TypeError("The following parameters of `{}` are positional only.\n" --> 210 "They were used as keywords arguments:\n{}".format(function.__qualname__, lst)) 211 if insertn and len(args) >= insertn: 212 args = args[:insertn] + (None,) + args[insertn:] TypeError: The following parameters of `foo` are positional only. They were used as keywords arguments: - 'a' (1) should be in 0th position -- M On Tue, Feb 28, 2017 at 2:17 PM, M.-A. Lemburg <mal@egenix.com> wrote:

On 28.02.17 23:17, Victor Stinner wrote:
I'm strongly +1 for supporting positional-only parameters. The main benefit to me is that this allows to declare functions that takes arbitrary keyword arguments like Formatter.format() or MutableMapping.update(). Now we can't use even the "self" parameter and need to use a trick with parsing *args manually. This harms clearness and performance. The problem with the "/" marker is that it looks ugly. There was an excuse for the "*" marker -- it came from omitting the name in "*args". The "*" prefix itself means an iterable unpacking, but "/" is not used neither as prefix nor suffix.
Do you see concrete cases where it's a deliberate choice to deny passing arguments as keywords?
dict.__init__(), dict.update(), partial.__new__() and partial.__call__() are obvious examples. There are others. And there was performance reason. Just making the function supporting keyword arguments added an overhead even to calls with only positional arguments. This was changed recently, but I didn't checked whether some overhead is left.
I believe weird names like "x" was added when the support of "base" keyword was added due to the limitation of PyArg_ParseTupleAndKeywords(). All or nothing, either builtin function didn't support keyword arguments, or it supported passing by keyword for all arguments. But now it is possible to support passing by keyword only the part of parameters. I want to propose to deprecate badly designed keyword names of builtins.
[...] is not Python syntax too. And it is orthogonal to positional-only parameters. [...] doesn't mean that parameters are positional-only. They can be passed by keyword, but just don't have default value. On other side, mandatory parameters can be positional-only.

On Wed, Mar 1, 2017 at 11:25 AM, Serhiy Storchaka <storchaka@gmail.com> wrote:
Agreed.
It's in a sense a pun -- * and / are "opposites" in mathematics, and so are the usages here.
+1
FWIW in typeshed we've started using double leading underscore as a convention for positional-only parameters, e.g. here: https://github.com/python/typeshed/blob/master/stdlib/3/builtins.pyi#L936 FWIW I think 'self' should also be special-cased as positional-only. Nobody wants to write 'C.foo(self=C())'. :-) -- --Guido van Rossum (python.org/~guido <http://python.org/%7Eguido>)

On 3/1/2017 2:53 PM, Guido van Rossum wrote:
Agreed.
+ 1 also. When people write a Python equivalent of a built-in function for documentation or teaching purposes, they should be able to exactly mimic the API.
Besides which, '/' has traditionally be used in non-numeric contexts as a separator, and in unix paths. -- Terry Jan Reedy

2017-03-01 21:52 GMT+01:00 Terry Reedy <tjreedy@udel.edu>:
Yeah, Serhiy said basically the same thing: it's doable, but complex without builtin support for positional-only arguments. I dislike subtle differences between C and Python, and positional-only is a major difference since it has a very visible effect on the API. After having used PHP for years, I really enjoyed Python keyword arguments and default values. I was very happy to not have to "reimplement" the "keyword arguments with default values" feature in each function (*). Basically, I would like the same thing for positional-only arguments :-) (*) Example (found on the Internet) of PHP code pattern for keywords, enjoy ;-) function doSomething($arguments = array()) { // set defaults $arguments = array_merge(array( "argument" => "default value", ), $arguments); var_dump($arguments); } Victor

On 03/01/2017 11:53 AM, Guido van Rossum wrote:
FWIW in typeshed we've started using double leading underscore as a convention for positional-only parameters, e.g. here:
https://github.com/python/typeshed/blob/master/stdlib/3/builtins.pyi#L936
I would much rather have a single '/' to denote where positional-only ends, than have multiple leading '__'. Newest-member-of-the-society-for-the-preservation-of-underscores-ly yrs, -- ~Ethan~

On Wed, Mar 1, 2017 at 9:26 PM Serhiy Storchaka <storchaka@gmail.com> wrote:
I like the idea, but I wanted to note that since it has no meaning from the point of view of the defined function, it can be done with a magic decorator, so new syntax is not required: @positional_only[:4] def replace(self, old, new, count=-1): ... It may ease googling and backporting, by defining positional_only[slice] to be the identity function. Elazar

On Wed, Mar 1, 2017 at 2:16 PM, אלעזר <elazarg@gmail.com> wrote:
I"m confused, what does the [:4] mean? if you want old and new to be positional only, wouldn't it be something like: @positional_only(3) def replace(self, old, new, count=-1): ... i.e. the first three parameters are positional only. and why indexing/slice syntax??? +! on the idea -- still on the fence about syntax. -CHB -- Christopher Barker, Ph.D. Oceanographer Emergency Response Division NOAA/NOS/OR&R (206) 526-6959 voice 7600 Sand Point Way NE (206) 526-6329 fax Seattle, WA 98115 (206) 526-6317 main reception Chris.Barker@noaa.gov

Hi all, I have a slight variant of the decorator proposal. Rather than specify a count, let the decorator implement the typeshed dunder convention: @positional_only def replace(self, __old, __new, count=-1): (I imagine this decorator would also treat "self" as position_only, so no need for __self.) Pros: 1. Consistent with the typeshed convention. 2. Avoids a count. 3. Strictly opt-in, so hopefully keeps those @#?! underscore preservationists from picketing my lawn (again!). Stephan 2017-03-02 4:16 GMT+01:00 Chris Barker <chris.barker@noaa.gov>:

On 03/01/2017 11:41 PM, Stephan Houben wrote:
Only a pro if you like that convention. ;)
2. Avoids a count. 3. Strictly opt-in, so hopefully keeps those @#?! underscore preservationists from picketing my lawn (again!).
Using a decorator is also strictly opt-in. Oh, and did someone say it was time for the protest? [----------------------] [----------------------] [ ] [ ] [ NO MORE UNDERSCORES! ] [ NO MORE UNDERSCORES! ] [ ] [ ] [----------------------] [----------------------] | | | | | | | | | | | | | | | | |-| |-| -- ~Ethan~

Here's a proof-of-concept for the decorator. It does not address the issue of passing aliases to positional arguments to **kwargs - I guess this requires changes in the CPython's core. (Sorry about the coloring, that's how it's pasted) from inspect import signature, Parameter from functools import wraps def positional_only(n): def wrap(f): s = signature(f) params = list(s.parameters.values()) for i in range(n): if params[i].kind != Parameter.POSITIONAL_OR_KEYWORD: raise TypeError('{} has less than {} positional arguments'.format(f.__name__, n)) params[i] = params[i].replace(kind=Parameter.POSITIONAL_ONLY) f.__signature__ = s.replace(parameters=params) @wraps(f) def inner(*args, **kwargs): if len(args) < n: raise TypeError('{} takes at least {} positional arguments'.format(f.__name__, n)) return f(*args, **kwargs) return inner return wrap @positional_only(2) def f(a, b, c): print(a, b, c) help(f) # f(a, b, /, c, **kwargs) f(1, 2, c=2) # f(1, b=2, c=3) # TypeError: f takes at least 2 positional arguments @positional_only(3) def g(a, b, *, c): print(a, b, c) # TypeError: g has less than 3 positional arguments Elazar

That looks great to me! I also think the '/' syntax looks fine and the pun works. If part of the motivation for position-only arguments was better performance and that motivation still holds water, then it makes sense to allow Python to support that optimization, but I would be happy with just a decorator too. I definitely DON'T like the double-underscore. On top of all the other complaints, I think it's more prone to break code. It's also more ugly than '/' IMHO. On Thu, Mar 2, 2017 at 5:10 AM אלעזר <elazarg@gmail.com> wrote:

On 01Mar2017 21:25, Serhiy Storchaka <storchaka@gmail.com> wrote:
I was a mild +0.1 on this until I saw this argument; now I am +1 (unless there's some horrible unforseen performance penalty). I've been writing quite a few functions lately where it is reasonable for a caller to want to pass arbitrary keyword arguments, but where I also want some additional parameters for control purposes. The most recent example was database related: functions accepting arbitrary keyword arguments indicating column values. As a specific example, what I _want_ to write includes this method: def update(self, where, **column_values): Now, because "where" happens to be an SQL keyword it is unlikely that there will be a column of that name, _if_ the database is human designed by an SQL person. I have other examples where picking a "safe" name is harder. I can even describe scenarios where "where" is plausible: supposing the the database is generated from some input data, perhaps supplied by a CSV file (worse, a CSV file that is an export of a human written spreadsheet with a "Where" column header). That isn't really even made up: I've got functions whose purpose is to import such spreadsheet exports, making namedtuple subclasses automatically from the column headers. In many of these situations I've had recently positional-only arguments would have been very helpful. I even had to bugfix a function recently where a positional argument was being trouced by a keyword argument by a caller. Cheers, Cameron Simpson <cs@cskk.id.au>

[Cameron Simpson]
I've run into this before and use the trailing '_' convention for names: def update(self, where_, **column_vals): ... Because such names are **probably** never going to show up. But, of course; if such a name were actually used for a column, it would be a fantastically hard bug to find! On Thu, Sep 6, 2018 at 11:01 PM Cameron Simpson <cs@cskk.id.au> wrote:

On Tue, Feb 28, 2017 at 10:17:31PM +0100, Victor Stinner wrote:
+0 on positional-only parameters.
If yes, what should be the syntax? Use "/" marker?
I think that / makes a nice pun with * and is easy to remember. I dislike the proposed double-leading-only underscore convention, as that makes the names look like *private* parameters the caller shouldn't provide at all. And it leads to confusion: def function(__a, b, _c, * __d): ... So __a is positional-only, b could be positional or keyword, _c is private, and __d is keyword-only but just happens to start with two underscores. Yuck. I think that [...] is completely unacceptable. It is a very common convention to use square brackets to flag optional arguments when writing function signatures in documentation, e.g.: Help on class zip in module builtins: class zip(object) | zip(iter1 [,iter2 [...]]) --> zip object It would be confusing to have [...] have syntactic meaning different to that convention.
Use the @positional() decorator?
I suppose a positional() decorator would be useful for backporting, but I wouldn't want it to be the One Obvious Way to get positional arguments.
By the way, I read that "/" marker is unknown by almost all Python developers,
Of course it is not well known -- it is only new, and not legal syntax yet! Unless they are heavily using Argument Clinic they probably won't recognise it.
-1
That isn't right. It would have to be: replace([self, old, new, count=-1]) if all of the arguments are positional-only. But that makes it look like they are all optional! A very strong -1 to this.
Python 3.5 help (docstring) uses "S.replace(old, new[, count])".
Should be: S.replace(old, new[, count], /) which shows that all three arguments are positional only, but only count is optional. -- Steve

2017-03-02 14:23 GMT+01:00 Steven D'Aprano <steve@pearwood.info>:
Oh, I didn't notice the weird count parameter: positional-only, but no default value? I would prefer to avoid weird parameters and use a syntax which can be written in Python, like: def replace(self, old, new, /, count=-1): ... When a function has more than 3 parameters, I like the ability to pass arguments by keyword for readability: "xxx".replace("x", "y", count=2) It's more explicit than: "xxx".replace("x", "y", 2) By the way, I proposed once to convert open() parameters after filename and mode to keyword-only arguments, but Guido didn't want to break the backward compatibility ;-) open(filename, mode, *, buffering=-1, ...) Victor

It seems all the core devs who have commented on this are in the positive (Victor, Yury, Ethan, Yury, Guido, Terry, and Steven; MAL didn't explicitly vote). So to me that suggests there's enough support to warrant writing a PEP. Are you up for writing it, Victor, or is someone else going to write it? On Tue, 28 Feb 2017 at 13:18 Victor Stinner <victor.stinner@gmail.com> wrote:

Isn't https://www.python.org/dev/peps/pep-0457/ the PEP you are looking for? On 02.03.2017 19:16, Brett Cannon wrote:

I am thinking at writing a PEP, yes. I need time to think about it, find all corner cases. Maybe also include something for "optional parameter without default value". Don't expect it soon, I have some pending work to finish before :-) Victor Le 2 mars 2017 7:16 PM, "Brett Cannon" <brett@python.org> a écrit :

I think it makes more sense to remove the concept of positional only parameters by slowly fixing the standard library. I've discussed the existence of positional only with a few people and their response falls in to some basic categories: - disgust - disbelief - bargaining (it's not very common right?! in fact yes it is) I don't think that's a good look for Python :P

On Thu, Sep 6, 2018 at 10:57 PM Anders Hovmöller <boxed@killingar.net> wrote: [..]
I don't think that's a good look for Python :P
Anders, Discussing something privately with "a few people", posting snarky conclusions, and giving baseless recommendations isn't how we strive to make decisions in Python. Please refrain from posting in this manner to python-ideas and python-dev, as emails written this way are simply distracting and borderline disturbing. Thanks, Yury

I'm +0.5 to add positional-only parameters. Pros: * A lot of people don't know what '/' currently means in functions signatures rendered by `help` and docs. Because it's not a real syntax, it's really hard to find what it means. * Some APIs do look better with positional-only parameters, especially functions with one argument. * We already have this feature half-implemented: some builtin methods have positional-only arguments, inspect.signature API supports it already. Cons: * Function declarations will become a bit more complex, making a bump in Python learning curve. * Performance? I'm not sure if adding another set of checks will make a huge impact, but we'll need to benchmark this. Yury On 2017-02-28 4:17 PM, Victor Stinner wrote:

I think that was started as pep 457: https://www.python.org/dev/peps/pep-0457/ (Syntax For Positional-Only Parameters) Still informal. +1, it's likely possible to backport it to previous version using a decorator and faking __signature__. -- M On Tue, Feb 28, 2017 at 2:03 PM, Yury Selivanov <yselivanov.ml@gmail.com> wrote:

On 28 Feb 2017, at 22:17, Victor Stinner wrote:
Tangential to the main topic, but this module doesn’t enforce positional-only arguments. It allows you enforce keyword-only arguments like on Python 3: >>> from positional import positional >>> @positional(1) ... def replace(old, new): ... ... ... >>> replace(old='a', new='b') >>> replace('a', 'b') Traceback (most recent call last): File "<stdin>", line 1, in <module> File “…/site-packages/positional/__init__.py", line 97, in inner raise TypeError(message) TypeError: replace takes at most 1 positional argument (2 given) Frazer

On 28.02.2017 22:17, Victor Stinner wrote:
Keyword argument handling is comparatively slow and rarely needed for non-optional positional parameters of built-ins. You might make a case for optional ones, but then: how often are these used in practice to warrant the drop in performance ? Note: All this is different for Python methods/functions. The overhead of keyword parsing is small compared to what Python has to do to setup new frames for execution.
... and that's another reason why to avoid them: the naming of positional parameters was never really taken into account when writing the C functions and it does even change sometimes without warning.
+1
Using "count=-1" looks confusing when the method doesn't allow keyword arguments, so it's probably better to use the 3.5 style and document the defaults in the doc-string. AFAIR, some of these don't even have a (documented) default value. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Experts (#1, Feb 28 2017)
::: We implement business ideas - efficiently in both time and costs ::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/ http://www.malemburg.com/

I just noticed a module on PyPI to implement this behaviour on Python functions:
This library seem to do the opposite of what you ask and force kwarg only. In [11]: @positional.positional(1) ...: def foo(a=1,b=2): ...: print(a,b) ...: In [12]: foo(a=1,b=2) # should have raised. 1 2 In [13]: foo(1,b=2) 1 2 This seem to be the right thing: https://pypi.python.org/pypi/positionalonly In [1]: @positionalonly(1) ...: def foo(a=1,b=2): ...: print(a,b) In [2]: foo(1, 2) 1 2 In [3]: foo(a=1,b=2) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-3-78db88131ebc> in <module>() ----> 1 foo(a=1,b=2) /Users/bussonniermatthias/anaconda/lib/python3.5/site-packages/positionalonly/__init__.py in fun(*args, **kwargs) 208 ]) 209 raise TypeError("The following parameters of `{}` are positional only.\n" --> 210 "They were used as keywords arguments:\n{}".format(function.__qualname__, lst)) 211 if insertn and len(args) >= insertn: 212 args = args[:insertn] + (None,) + args[insertn:] TypeError: The following parameters of `foo` are positional only. They were used as keywords arguments: - 'a' (1) should be in 0th position -- M On Tue, Feb 28, 2017 at 2:17 PM, M.-A. Lemburg <mal@egenix.com> wrote:

On 28.02.17 23:17, Victor Stinner wrote:
I'm strongly +1 for supporting positional-only parameters. The main benefit to me is that this allows to declare functions that takes arbitrary keyword arguments like Formatter.format() or MutableMapping.update(). Now we can't use even the "self" parameter and need to use a trick with parsing *args manually. This harms clearness and performance. The problem with the "/" marker is that it looks ugly. There was an excuse for the "*" marker -- it came from omitting the name in "*args". The "*" prefix itself means an iterable unpacking, but "/" is not used neither as prefix nor suffix.
Do you see concrete cases where it's a deliberate choice to deny passing arguments as keywords?
dict.__init__(), dict.update(), partial.__new__() and partial.__call__() are obvious examples. There are others. And there was performance reason. Just making the function supporting keyword arguments added an overhead even to calls with only positional arguments. This was changed recently, but I didn't checked whether some overhead is left.
I believe weird names like "x" was added when the support of "base" keyword was added due to the limitation of PyArg_ParseTupleAndKeywords(). All or nothing, either builtin function didn't support keyword arguments, or it supported passing by keyword for all arguments. But now it is possible to support passing by keyword only the part of parameters. I want to propose to deprecate badly designed keyword names of builtins.
[...] is not Python syntax too. And it is orthogonal to positional-only parameters. [...] doesn't mean that parameters are positional-only. They can be passed by keyword, but just don't have default value. On other side, mandatory parameters can be positional-only.

On Wed, Mar 1, 2017 at 11:25 AM, Serhiy Storchaka <storchaka@gmail.com> wrote:
Agreed.
It's in a sense a pun -- * and / are "opposites" in mathematics, and so are the usages here.
+1
FWIW in typeshed we've started using double leading underscore as a convention for positional-only parameters, e.g. here: https://github.com/python/typeshed/blob/master/stdlib/3/builtins.pyi#L936 FWIW I think 'self' should also be special-cased as positional-only. Nobody wants to write 'C.foo(self=C())'. :-) -- --Guido van Rossum (python.org/~guido <http://python.org/%7Eguido>)

On 3/1/2017 2:53 PM, Guido van Rossum wrote:
Agreed.
+ 1 also. When people write a Python equivalent of a built-in function for documentation or teaching purposes, they should be able to exactly mimic the API.
Besides which, '/' has traditionally be used in non-numeric contexts as a separator, and in unix paths. -- Terry Jan Reedy

2017-03-01 21:52 GMT+01:00 Terry Reedy <tjreedy@udel.edu>:
Yeah, Serhiy said basically the same thing: it's doable, but complex without builtin support for positional-only arguments. I dislike subtle differences between C and Python, and positional-only is a major difference since it has a very visible effect on the API. After having used PHP for years, I really enjoyed Python keyword arguments and default values. I was very happy to not have to "reimplement" the "keyword arguments with default values" feature in each function (*). Basically, I would like the same thing for positional-only arguments :-) (*) Example (found on the Internet) of PHP code pattern for keywords, enjoy ;-) function doSomething($arguments = array()) { // set defaults $arguments = array_merge(array( "argument" => "default value", ), $arguments); var_dump($arguments); } Victor

On 03/01/2017 11:53 AM, Guido van Rossum wrote:
FWIW in typeshed we've started using double leading underscore as a convention for positional-only parameters, e.g. here:
https://github.com/python/typeshed/blob/master/stdlib/3/builtins.pyi#L936
I would much rather have a single '/' to denote where positional-only ends, than have multiple leading '__'. Newest-member-of-the-society-for-the-preservation-of-underscores-ly yrs, -- ~Ethan~

On Wed, Mar 1, 2017 at 9:26 PM Serhiy Storchaka <storchaka@gmail.com> wrote:
I like the idea, but I wanted to note that since it has no meaning from the point of view of the defined function, it can be done with a magic decorator, so new syntax is not required: @positional_only[:4] def replace(self, old, new, count=-1): ... It may ease googling and backporting, by defining positional_only[slice] to be the identity function. Elazar

On Wed, Mar 1, 2017 at 2:16 PM, אלעזר <elazarg@gmail.com> wrote:
I"m confused, what does the [:4] mean? if you want old and new to be positional only, wouldn't it be something like: @positional_only(3) def replace(self, old, new, count=-1): ... i.e. the first three parameters are positional only. and why indexing/slice syntax??? +! on the idea -- still on the fence about syntax. -CHB -- Christopher Barker, Ph.D. Oceanographer Emergency Response Division NOAA/NOS/OR&R (206) 526-6959 voice 7600 Sand Point Way NE (206) 526-6329 fax Seattle, WA 98115 (206) 526-6317 main reception Chris.Barker@noaa.gov

Hi all, I have a slight variant of the decorator proposal. Rather than specify a count, let the decorator implement the typeshed dunder convention: @positional_only def replace(self, __old, __new, count=-1): (I imagine this decorator would also treat "self" as position_only, so no need for __self.) Pros: 1. Consistent with the typeshed convention. 2. Avoids a count. 3. Strictly opt-in, so hopefully keeps those @#?! underscore preservationists from picketing my lawn (again!). Stephan 2017-03-02 4:16 GMT+01:00 Chris Barker <chris.barker@noaa.gov>:

On 03/01/2017 11:41 PM, Stephan Houben wrote:
Only a pro if you like that convention. ;)
2. Avoids a count. 3. Strictly opt-in, so hopefully keeps those @#?! underscore preservationists from picketing my lawn (again!).
Using a decorator is also strictly opt-in. Oh, and did someone say it was time for the protest? [----------------------] [----------------------] [ ] [ ] [ NO MORE UNDERSCORES! ] [ NO MORE UNDERSCORES! ] [ ] [ ] [----------------------] [----------------------] | | | | | | | | | | | | | | | | |-| |-| -- ~Ethan~

Here's a proof-of-concept for the decorator. It does not address the issue of passing aliases to positional arguments to **kwargs - I guess this requires changes in the CPython's core. (Sorry about the coloring, that's how it's pasted) from inspect import signature, Parameter from functools import wraps def positional_only(n): def wrap(f): s = signature(f) params = list(s.parameters.values()) for i in range(n): if params[i].kind != Parameter.POSITIONAL_OR_KEYWORD: raise TypeError('{} has less than {} positional arguments'.format(f.__name__, n)) params[i] = params[i].replace(kind=Parameter.POSITIONAL_ONLY) f.__signature__ = s.replace(parameters=params) @wraps(f) def inner(*args, **kwargs): if len(args) < n: raise TypeError('{} takes at least {} positional arguments'.format(f.__name__, n)) return f(*args, **kwargs) return inner return wrap @positional_only(2) def f(a, b, c): print(a, b, c) help(f) # f(a, b, /, c, **kwargs) f(1, 2, c=2) # f(1, b=2, c=3) # TypeError: f takes at least 2 positional arguments @positional_only(3) def g(a, b, *, c): print(a, b, c) # TypeError: g has less than 3 positional arguments Elazar

That looks great to me! I also think the '/' syntax looks fine and the pun works. If part of the motivation for position-only arguments was better performance and that motivation still holds water, then it makes sense to allow Python to support that optimization, but I would be happy with just a decorator too. I definitely DON'T like the double-underscore. On top of all the other complaints, I think it's more prone to break code. It's also more ugly than '/' IMHO. On Thu, Mar 2, 2017 at 5:10 AM אלעזר <elazarg@gmail.com> wrote:

On 01Mar2017 21:25, Serhiy Storchaka <storchaka@gmail.com> wrote:
I was a mild +0.1 on this until I saw this argument; now I am +1 (unless there's some horrible unforseen performance penalty). I've been writing quite a few functions lately where it is reasonable for a caller to want to pass arbitrary keyword arguments, but where I also want some additional parameters for control purposes. The most recent example was database related: functions accepting arbitrary keyword arguments indicating column values. As a specific example, what I _want_ to write includes this method: def update(self, where, **column_values): Now, because "where" happens to be an SQL keyword it is unlikely that there will be a column of that name, _if_ the database is human designed by an SQL person. I have other examples where picking a "safe" name is harder. I can even describe scenarios where "where" is plausible: supposing the the database is generated from some input data, perhaps supplied by a CSV file (worse, a CSV file that is an export of a human written spreadsheet with a "Where" column header). That isn't really even made up: I've got functions whose purpose is to import such spreadsheet exports, making namedtuple subclasses automatically from the column headers. In many of these situations I've had recently positional-only arguments would have been very helpful. I even had to bugfix a function recently where a positional argument was being trouced by a keyword argument by a caller. Cheers, Cameron Simpson <cs@cskk.id.au>

[Cameron Simpson]
I've run into this before and use the trailing '_' convention for names: def update(self, where_, **column_vals): ... Because such names are **probably** never going to show up. But, of course; if such a name were actually used for a column, it would be a fantastically hard bug to find! On Thu, Sep 6, 2018 at 11:01 PM Cameron Simpson <cs@cskk.id.au> wrote:

On Tue, Feb 28, 2017 at 10:17:31PM +0100, Victor Stinner wrote:
+0 on positional-only parameters.
If yes, what should be the syntax? Use "/" marker?
I think that / makes a nice pun with * and is easy to remember. I dislike the proposed double-leading-only underscore convention, as that makes the names look like *private* parameters the caller shouldn't provide at all. And it leads to confusion: def function(__a, b, _c, * __d): ... So __a is positional-only, b could be positional or keyword, _c is private, and __d is keyword-only but just happens to start with two underscores. Yuck. I think that [...] is completely unacceptable. It is a very common convention to use square brackets to flag optional arguments when writing function signatures in documentation, e.g.: Help on class zip in module builtins: class zip(object) | zip(iter1 [,iter2 [...]]) --> zip object It would be confusing to have [...] have syntactic meaning different to that convention.
Use the @positional() decorator?
I suppose a positional() decorator would be useful for backporting, but I wouldn't want it to be the One Obvious Way to get positional arguments.
By the way, I read that "/" marker is unknown by almost all Python developers,
Of course it is not well known -- it is only new, and not legal syntax yet! Unless they are heavily using Argument Clinic they probably won't recognise it.
-1
That isn't right. It would have to be: replace([self, old, new, count=-1]) if all of the arguments are positional-only. But that makes it look like they are all optional! A very strong -1 to this.
Python 3.5 help (docstring) uses "S.replace(old, new[, count])".
Should be: S.replace(old, new[, count], /) which shows that all three arguments are positional only, but only count is optional. -- Steve

2017-03-02 14:23 GMT+01:00 Steven D'Aprano <steve@pearwood.info>:
Oh, I didn't notice the weird count parameter: positional-only, but no default value? I would prefer to avoid weird parameters and use a syntax which can be written in Python, like: def replace(self, old, new, /, count=-1): ... When a function has more than 3 parameters, I like the ability to pass arguments by keyword for readability: "xxx".replace("x", "y", count=2) It's more explicit than: "xxx".replace("x", "y", 2) By the way, I proposed once to convert open() parameters after filename and mode to keyword-only arguments, but Guido didn't want to break the backward compatibility ;-) open(filename, mode, *, buffering=-1, ...) Victor

It seems all the core devs who have commented on this are in the positive (Victor, Yury, Ethan, Yury, Guido, Terry, and Steven; MAL didn't explicitly vote). So to me that suggests there's enough support to warrant writing a PEP. Are you up for writing it, Victor, or is someone else going to write it? On Tue, 28 Feb 2017 at 13:18 Victor Stinner <victor.stinner@gmail.com> wrote:

Isn't https://www.python.org/dev/peps/pep-0457/ the PEP you are looking for? On 02.03.2017 19:16, Brett Cannon wrote:

I am thinking at writing a PEP, yes. I need time to think about it, find all corner cases. Maybe also include something for "optional parameter without default value". Don't expect it soon, I have some pending work to finish before :-) Victor Le 2 mars 2017 7:16 PM, "Brett Cannon" <brett@python.org> a écrit :

I think it makes more sense to remove the concept of positional only parameters by slowly fixing the standard library. I've discussed the existence of positional only with a few people and their response falls in to some basic categories: - disgust - disbelief - bargaining (it's not very common right?! in fact yes it is) I don't think that's a good look for Python :P

On Thu, Sep 6, 2018 at 10:57 PM Anders Hovmöller <boxed@killingar.net> wrote: [..]
I don't think that's a good look for Python :P
Anders, Discussing something privately with "a few people", posting snarky conclusions, and giving baseless recommendations isn't how we strive to make decisions in Python. Please refrain from posting in this manner to python-ideas and python-dev, as emails written this way are simply distracting and borderline disturbing. Thanks, Yury
participants (19)
-
Abe Dillon
-
Anders Hovmöller
-
Brett Cannon
-
Cameron Simpson
-
Chris Barker
-
Ethan Furman
-
Frazer McLean
-
Guido van Rossum
-
M.-A. Lemburg
-
Matthias Bussonnier
-
MRAB
-
Serhiy Storchaka
-
Stephan Houben
-
Steven D'Aprano
-
Sven R. Kunze
-
Terry Reedy
-
Victor Stinner
-
Yury Selivanov
-
אלעזר