Order of positional and keyword arguments
There is an inconsistence in passing arguments to functions. Explicit positional arguments should precede keyword arguments (both explicit and variable), but variable positional arguments can follow explicit keyword arguments and precede variable keyword arguments. For example, for function def f(a=None, b=None): return a, b the following is valid syntax: f(1, b=2) f(1, **{'b': 2}) f(*[1], b=2) f(*[1], **{'b': 2}) f(b=2, *[1]) but the following is an error: f(b=2, 1) f(**{'b': 2}, 1) f(**{'b': 2}, *[1]) f(b=2, *[1]) is surprised in two ways: 1. Argument values are passed not in order. The first value is assigned to the second parameter, and the second value is assigned to the first parameter. 2. Argument values are evaluated not from left to right. This contradicts the general rule that expressions are evaluated from left to right (with few known exceptions). I never seen the form `f(b=2, *[1])` in practice (though the language reference contains an explicit example for it), and it looks weird to me. I don't see reasons of writing `f(b=2, *[1])` instead of more natural `f(*[1], b=2)`. I propose to disallow it. This will also make the grammar simpler. Current grammar: argument_list: `positional_arguments` ["," `starred_and_keywords`] : ["," `keywords_arguments`] : | `starred_and_keywords` ["," `keywords_arguments`] : | `keywords_arguments` positional_arguments: ["*"] `expression` ("," ["*"] `expression`)* starred_and_keywords: ("*" `expression` | `keyword_item`) : ("," "*" `expression` | "," `keyword_item`)* keywords_arguments: (`keyword_item` | "**" `expression`) : ("," `keyword_item` | "," "**" `expression`)* keyword_item: `identifier` "=" `expression` Proposed grammar: argument_list: `positional_arguments` ["," `keywords_arguments`] : | `keywords_arguments` positional_arguments: ["*"] `expression` ("," ["*"] `expression`)* keywords_arguments: `keyword_argument` ("," `keyword_argument`)* keyword_argument: `identifier` "=" `expression` | "**" `expression`
I see no practical benefit to making such a restriction, and there's a
risk of breaking existing code.
While it's not something I've ever used myself in real code,
def wrapper(*args, **kw):
return wrapped_fn(some_arg=1, *args, **kw)
seems like a perfectly reasonable way to write a wrapper to me. I'm
against breaking this for no better reason than "to simplify the
grammar" and to exclude cases that some people might find confusing (I
actually found your example perfectly comprehensible, if convoluted).
-1 from me.
Paul
On 26 April 2018 at 20:25, Serhiy Storchaka
There is an inconsistence in passing arguments to functions.
Explicit positional arguments should precede keyword arguments (both explicit and variable), but variable positional arguments can follow explicit keyword arguments and precede variable keyword arguments.
For example, for function
def f(a=None, b=None): return a, b
the following is valid syntax:
f(1, b=2) f(1, **{'b': 2}) f(*[1], b=2) f(*[1], **{'b': 2}) f(b=2, *[1])
but the following is an error:
f(b=2, 1) f(**{'b': 2}, 1) f(**{'b': 2}, *[1])
f(b=2, *[1]) is surprised in two ways:
1. Argument values are passed not in order. The first value is assigned to the second parameter, and the second value is assigned to the first parameter.
2. Argument values are evaluated not from left to right. This contradicts the general rule that expressions are evaluated from left to right (with few known exceptions).
I never seen the form `f(b=2, *[1])` in practice (though the language reference contains an explicit example for it), and it looks weird to me. I don't see reasons of writing `f(b=2, *[1])` instead of more natural `f(*[1], b=2)`. I propose to disallow it.
This will also make the grammar simpler. Current grammar:
argument_list: `positional_arguments` ["," `starred_and_keywords`] : ["," `keywords_arguments`] : | `starred_and_keywords` ["," `keywords_arguments`] : | `keywords_arguments` positional_arguments: ["*"] `expression` ("," ["*"] `expression`)* starred_and_keywords: ("*" `expression` | `keyword_item`) : ("," "*" `expression` | "," `keyword_item`)* keywords_arguments: (`keyword_item` | "**" `expression`) : ("," `keyword_item` | "," "**" `expression`)* keyword_item: `identifier` "=" `expression`
Proposed grammar:
argument_list: `positional_arguments` ["," `keywords_arguments`] : | `keywords_arguments` positional_arguments: ["*"] `expression` ("," ["*"] `expression`)* keywords_arguments: `keyword_argument` ("," `keyword_argument`)* keyword_argument: `identifier` "=" `expression` | "**" `expression`
_______________________________________________ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/p.f.moore%40gmail.com
On Thu, Apr 26, 2018 at 12:25 PM, Serhiy Storchaka
f(b=2, *[1]) is surprised in two ways:
1. Argument values are passed not in order. The first value is assigned to the second parameter, and the second value is assigned to the first parameter.
2. Argument values are evaluated not from left to right. This contradicts the general rule that expressions are evaluated from left to right (with few known exceptions).
I never seen the form `f(b=2, *[1])` in practice (though the language reference contains an explicit example for it), and it looks weird to me. I don't see reasons of writing `f(b=2, *[1])` instead of more natural `f(*[1], b=2)`. I propose to disallow it.
Coincidentally, I recently came across and reviewed a PR to Django that proposed exactly this, or at least something very similar. They proposed changing-- def create_cursor(self, name=None): to-- def create_cursor(self, name=None, *args, **kwargs): https://github.com/django/django/pull/9674/files#diff-53fcf3ac0535307033e0cf... --Chris
This will also make the grammar simpler. Current grammar:
argument_list: `positional_arguments` ["," `starred_and_keywords`] : ["," `keywords_arguments`] : | `starred_and_keywords` ["," `keywords_arguments`] : | `keywords_arguments` positional_arguments: ["*"] `expression` ("," ["*"] `expression`)* starred_and_keywords: ("*" `expression` | `keyword_item`) : ("," "*" `expression` | "," `keyword_item`)* keywords_arguments: (`keyword_item` | "**" `expression`) : ("," `keyword_item` | "," "**" `expression`)* keyword_item: `identifier` "=" `expression`
Proposed grammar:
argument_list: `positional_arguments` ["," `keywords_arguments`] : | `keywords_arguments` positional_arguments: ["*"] `expression` ("," ["*"] `expression`)* keywords_arguments: `keyword_argument` ("," `keyword_argument`)* keyword_argument: `identifier` "=" `expression` | "**" `expression`
_______________________________________________ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/chris.jerdonek%40gmail.co...
On Thu, 26 Apr 2018 22:25:09 +0300
Serhiy Storchaka
f(b=2, *[1]) is surprised in two ways:
1. Argument values are passed not in order. The first value is assigned to the second parameter, and the second value is assigned to the first parameter.
I don't find it that surprising. If you write f(b=2, a=1), you are also passing arguments "not in order", but it still looks ok to me.
2. Argument values are evaluated not from left to right. This contradicts the general rule that expressions are evaluated from left to right (with few known exceptions).
Well... If you have code that relies on that rule, I would agree it's brittle code and should be rewritten differently (perhaps by assigning to temporary variables explicitly). Regards Antoine.
participants (4)
-
Antoine Pitrou
-
Chris Jerdonek
-
Paul Moore
-
Serhiy Storchaka