Passing positional arguments as keyword arguments (to provide function arguments out of order)
Currently if one wants to provide positional arguments after keyword arguments, it's not possible, one must begin with positional arguments [1] or use keyword arguments [2] : ``` def f(x, *, long_name='foo'): return ... f(2, long_name='bar') # [1] f(long_name='bar', x=2) # [2] ``` The problem is that it's not always possible to do so because some functions cannot have keyword arguments (because they call a C function) like math.hypot : ``` import math math.hypot(y=5, x=2) # TypeError ``` A solution is to use partial. ``` from functools import partial partial(f, long_name='bar')(2) ``` But that begins to be "functional programming style" and that doesn't solve the case where one would like to change the order of (I created funcoperators.elipartial for that reason) : ``` from funcoperators import elipartial elipartial(math.hypot, ..., 5)(2) ``` Of course one could create a tuple and a dict they call f(*a, **k) but cannot be used inside an expression. ``` k = dict(long_name='bar') a = (2, ) f(*a, **k) ``` So what would be a good new syntax for that or workaround I didn't think of ? ``` f(**dict(long_name='bar'), *(2, )) f(long_name='bar', 0=2) math.hypot(0=5, 1=2) f(long_name='bar', args[0]=2) math.hypot(args[0]=5, args[1]=2) ... ``` I don't know if such an idea has been asked before (I don't know how to formulate it). robertvandeneynde.be
On 15 May 2019, at 03:07, Robert Vanden Eynde <robertve92@gmail.com> wrote:
Currently if one wants to provide positional arguments after keyword arguments, it's not possible, one must begin with positional arguments [1] or use keyword arguments [2] :
I'm my opinion the goal should be that there is no such thing as positional only arguments in python. It's just a bad idea. No r al arguments that can be both positional or keyword at the call site, or keyword only. Those are good and make sense. Adding a third option is just added complexity. / Anders
Le mer. 15 mai 2019 à 07:37, Anders Hovmöller <boxed@killingar.net> a écrit :
On 15 May 2019, at 03:07, Robert Vanden Eynde <robertve92@gmail.com> wrote:
Currently if one wants to provide positional arguments after keyword arguments, it's not possible, one must begin with positional arguments [1] or use keyword arguments [2] :
In my opinion the goal should be that there is no such thing as positional
only arguments in python. It's just a bad idea. No r al arguments that can be both positional or keyword at the call site, or keyword only. Those are good and make sense. Adding a third option is just added complexity.
But what if on the call side the user wants to specify y before x but the definition side wanted "positional only args" for performance reason ? On another note, there's no way to specify "positional only arguments" in "pure python" using a syntax used in the documentation like "def f(x, y, /)" (but def f(*args) will do the trick).
On Wed, May 15, 2019 at 1:37 AM Anders Hovmöller <boxed@killingar.net> wrote:
On 15 May 2019, at 03:07, Robert Vanden Eynde <robertve92@gmail.com> wrote:
Currently if one wants to provide positional arguments after keyword arguments, it's not possible, one must begin with positional arguments [1] or use keyword arguments [2] :
I'm my opinion the goal should be that there is no such thing as positional only arguments in python. It's just a bad idea. No r al arguments that can be both positional or keyword at the call site, or keyword only. Those are good and make sense. Adding a third option is just added complexity.
That's not a realistic goal; there are some use cases, including in CPython builtins, that cannot be accomplished without positional-only arguments. For example, the current behavior of the `dict` constructor is to accept both certain iterables as a positional-only argument and/or keyword arguments from which a dict can be created. If the iterable argument was not positional-only, then it would be forced to consume a keyword (even if the caller passes it as a positional argument), meaning that that keyword could never be included in **kwargs and could not become a dict key via keyword arguments. Additionally, PEP 570 [1], which will add syntax for positional-only parameters in Python functions, was accepted by Guido last month. [1] https://www.python.org/dev/peps/pep-0570/
On 15 May 2019, at 07:51, Jonathan Goble <jcgoble3@gmail.com> wrote:
That's not a realistic goal; there are some use cases, including in CPython builtins, that cannot be accomplished without positional-only arguments. For example, the current behavior of the `dict` constructor is to accept both certain iterables as a positional-only argument and/or keyword arguments from which a dict can be created. If the iterable argument was not positional-only, then it would be forced to consume a keyword (even if the caller passes it as a positional argument), meaning that that keyword could never be included in **kwargs and could not become a dict key via keyword arguments.
You lost me. How is this not handled by *args and **kwargs? I think it is. "Positional only" isn't needed in this case.
Additionally, PEP 570 [1], which will add syntax for positional-only parameters in Python functions, was accepted by Guido last month.
I know and in my opinion it's a big mistake. It adapts python to a misfeatures of C extension code instead of making the C extentions play nice end behave like python. But obviously it's too late now. / Anders
On Wed, May 15, 2019 at 12:46 AM Anders Hovmöller <boxed@killingar.net> wrote:
On 15 May 2019, at 07:51, Jonathan Goble <jcgoble3@gmail.com> wrote:
That's not a realistic goal; there are some use cases, including in CPython builtins, that cannot be accomplished without positional-only arguments. For example, the current behavior of the `dict` constructor is to accept both certain iterables as a positional-only argument and/or keyword arguments from which a dict can be created. If the iterable argument was not positional-only, then it would be forced to consume a keyword (even if the caller passes it as a positional argument), meaning that that keyword could never be included in **kwargs and could not become a dict key via keyword arguments.
You lost me. How is this not handled by *args and **kwargs? I think it is. "Positional only" isn't needed in this case.
Additionally, PEP 570 [1], which will add syntax for positional-only parameters in Python functions, was accepted by Guido last month.
It was actually a decision of the Python steering council, of which Guido is a member and so am I.
I know and in my opinion it's a big mistake. It adapts python to a misfeatures of C extension code instead of making the C extentions play nice end behave like python.
There's actually more to it than that (it actually resolves various bugs we had in the stdlib that have nothing to do with C extension modules). The details can be found in the PEP.
On Wed, May 15, 2019 at 3:45 AM Anders Hovmöller <boxed@killingar.net> wrote:
On 15 May 2019, at 07:51, Jonathan Goble <jcgoble3@gmail.com> wrote:
That's not a realistic goal; there are some use cases, including in CPython builtins, that cannot be accomplished without positional-only arguments. For example, the current behavior of the `dict` constructor is to accept both certain iterables as a positional-only argument and/or keyword arguments from which a dict can be created. If the iterable argument was not positional-only, then it would be forced to consume a keyword (even if the caller passes it as a positional argument), meaning that that keyword could never be included in **kwargs and could not become a dict key via keyword arguments.
You lost me. How is this not handled by *args and **kwargs? I think it is. "Positional only" isn't needed in this case.
Because `def func(*args, **kwargs):` obfuscates the true signature, making `help()` and the `inspect` module less useful and forcing one to analyze the implementation code or study the docs to understand what and how many arguments are actually expected.
On 15 May 2019, at 21:55, Jonathan Goble <jcgoble3@gmail.com> wrote:
On Wed, May 15, 2019 at 3:45 AM Anders Hovmöller <boxed@killingar.net> wrote:
On 15 May 2019, at 07:51, Jonathan Goble <jcgoble3@gmail.com> wrote:
That's not a realistic goal; there are some use cases, including in CPython builtins, that cannot be accomplished without positional-only arguments. For example, the current behavior of the `dict` constructor is to accept both certain iterables as a positional-only argument and/or keyword arguments from which a dict can be created. If the iterable argument was not positional-only, then it would be forced to consume a keyword (even if the caller passes it as a positional argument), meaning that that keyword could never be included in **kwargs and could not become a dict key via keyword arguments.
You lost me. How is this not handled by *args and **kwargs? I think it is. "Positional only" isn't needed in this case.
Because `def func(*args, **kwargs):` obfuscates the true signature, making `help()` and the `inspect` module less useful and forcing one to analyze the implementation code or study the docs to understand what and how many arguments are actually expected.
In the general case because of features that until / was introduced was unique to functions defined in C. But in the case of the dict constructor that's just a weird misfeature. If it did in fact take *args and handled it it would be perfectly fine and useful. It would have meant people wouldn't have written all those merge() utility functions over the years. And it would make dict(a, b) valid syntax instead of the current overly verbose dict(**a, **b). For / to be a good idea one needs a strong argument for why a function must exist that tales a bounded amount of arguments (ie not *args) where calling it with the parameters named would be a bad thing. There are certainly cases where the names don't add much, like most functions with one argument but that is a different thing. / Anders
16.05.19 08:46, Anders Hovmöller пише:
In the general case because of features that until / was introduced was unique to functions defined in C. But in the case of the dict constructor that's just a weird misfeature. If it did in fact take *args and handled it it would be perfectly fine and useful. It would have meant people wouldn't have written all those merge() utility functions over the years. And it would make dict(a, b) valid syntax instead of the current overly verbose dict(**a, **b).
For / to be a good idea one needs a strong argument for why a function must exist that tales a bounded amount of arguments (ie not *args) where calling it with the parameters named would be a bad thing. There are certainly cases where the names don't add much, like most functions with one argument but that is a different thing.
The dict constructor is just one example. There are around 60 Python functions in the stdlib which will benefit from adding support of positional-only arguments. Look at https://github.com/python/cpython/pull/12620. Writing the code that parses args is errorprone and inefficient. There are also other rationales of PEP 570.
On 16 May 2019, at 08:50, Serhiy Storchaka <storchaka@gmail.com> wrote:
16.05.19 08:46, Anders Hovmöller пише:
In the general case because of features that until / was introduced was unique to functions defined in C. But in the case of the dict constructor that's just a weird misfeature. If it did in fact take *args and handled it it would be perfectly fine and useful. It would have meant people wouldn't have written all those merge() utility functions over the years. And it would make dict(a, b) valid syntax instead of the current overly verbose dict(**a, **b). For / to be a good idea one needs a strong argument for why a function must exist that tales a bounded amount of arguments (ie not *args) where calling it with the parameters named would be a bad thing. There are certainly cases where the names don't add much, like most functions with one argument but that is a different thing.
The dict constructor is just one example. There are around 60 Python functions in the stdlib which will benefit from adding support of positional-only arguments. Look at https://github.com/python/cpython/pull/12620.
Writing the code that parses args is errorprone and inefficient.
There are also other rationales of PEP 570.
I've read it. It is unconvincing to me. / Anders
On 5/16/2019 8:23 AM, Anders Hovmöller wrote:
On 16 May 2019, at 08:50, Serhiy Storchaka <storchaka@gmail.com> wrote:
Writing the code that parses args is errorprone and inefficient. There are also other rationales of PEP 570.
I've read it. It is unconvincing to me.
That's fine. Coredevs don't always agree 100% on every change either. -- Terry Jan Reedy
participants (6)
-
Anders Hovmöller
-
Brett Cannon
-
Jonathan Goble
-
Robert Vanden Eynde
-
Serhiy Storchaka
-
Terry Reedy