A grammatical oddity: trailing commas in argument lists -- continuation
Dear Python Developers, It is s my first post to python-dev, so let me introduce myself briefly: Jan Kaliszewski, programmer and composer, sometimes also NGO activist. Coming to the matter... The discussion started with remark by Mark Dickinson about such a syntax oddity:
def f(a, b,): ... is fine, but def f(*, a, b,): ... is a SyntaxError
Then some other similar oddities were pointed at (*args/**kwargs-related ones as well as calls like f(*, a=3,) causing SyntaxError too). References: * http://mail.python.org/pipermail/python-dev/2010-July/101636.html * http://bugs.python.org/issue9232 * http://bugs.python.org/issue10682 But yesterday both mentioned issues has been closed as rejected -- with suggestion that it would probably require a PEP to modify Python in this aspect (as there is no clear consensus). So I'd opt for re-opening the discussion -- I suppose that more people could be interested in solving the issue (at least after the end of PEP 3003 moratorium period). I think that seeing that: def f(a, b): ... def f(a, b,): ... def f(a, *, b): ... def f(a, *args, b): ... x(1, 2, 3, 4, z=5) x(1, 2, 3, 4, z=5,) x(1, *(2,3,4), z=5) ...are ok, then -- def f(a, *, b,): ... def f(a, *args, b,): ... x(1, *(2,3,4), z=5,): ... ...should be ok as well, and consequently -- def f(a, *args,): ... def f(a, **kwargs,): ... x(1, *(2,3,4),) x(1, **dict(z=6),) ...should also be ok. Please also note that Py3k's function annotations make one-def-argument- -per-line formattig style the most suitable in some cases, e.g.: def my_func( spam:"Very tasty and nutritious piece of food", ham:"For experts only", *more_spam:"Not less tasty and not less nutritious!", spammish_inquisition:"Nobody expects this!", ) -> "Spam, spam, spam, spam, spam, spam, spam, spam, spam, spam": ... Regards, Jan Kaliszewski
On Mon, Dec 13, 2010 at 9:44 PM, Jan Kaliszewski
I think that seeing that:
def f(a, b): ... def f(a, *, b): ... def f(a, *args, b): ... x(1, 2, 3, 4, z=5) x(1, *(2,3,4), z=5)
As per the closure of the affected tickets, the likely outcome of such a discussion would be the deprecation and subsequent removal of support for the following two options: def f(a, b,): ... x(1, 2, 3, 4, z=5,): ... Function arguments are not lists. Even when separated onto multiple lines, the closing "):" should remain on the final line with other content. That would be a lot of hassle to get rid of something that people probably aren't doing in the first place, though. Regards, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On 13/12/2010 13:25, Nick Coghlan wrote:
On Mon, Dec 13, 2010 at 9:44 PM, Jan Kaliszewski
wrote: I think that seeing that:
def f(a, b): ... def f(a, *, b): ... def f(a, *args, b): ... x(1, 2, 3, 4, z=5) x(1, *(2,3,4), z=5) As per the closure of the affected tickets, the likely outcome of such a discussion would be the deprecation and subsequent removal of support for the following two options:
def f(a, b,): ... x(1, 2, 3, 4, z=5,): ...
Function arguments are not lists. Even when separated onto multiple lines, the closing "):" should remain on the final line with other content.
Why? For very long signatures I still mildly prefer this: def f(self, first, second, third, fourth, foo=None, bar=None, baz=None, spam=None, eggs=None, ham=None ): Over putting the closing paren: on the last line of the def. Of course not having such long signatures is even more preferable, but *sometimes* they are needed. All the best, Michael Foord
That would be a lot of hassle to get rid of something that people probably aren't doing in the first place, though.
Regards, Nick.
-- http://www.voidspace.org.uk/ READ CAREFULLY. By accepting and reading this email you agree, on behalf of your employer, to release me from all obligations and waivers arising from any and all NON-NEGOTIATED agreements, licenses, terms-of-service, shrinkwrap, clickwrap, browsewrap, confidentiality, non-disclosure, non-compete and acceptable use policies (”BOGUS AGREEMENTS”) that I have entered into with your employer, its partners, licensors, agents and assigns, in perpetuity, without prejudice to my ongoing rights and privileges. You further represent that you have the authority to release me from any BOGUS AGREEMENTS on behalf of your employer.
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On 12/13/2010 08:25 AM, Nick Coghlan wrote:
On Mon, Dec 13, 2010 at 9:44 PM, Jan Kaliszewski
wrote: I think that seeing that:
def f(a, b): ... def f(a, *, b): ... def f(a, *args, b): ... x(1, 2, 3, 4, z=5) x(1, *(2,3,4), z=5)
As per the closure of the affected tickets, the likely outcome of such a discussion would be the deprecation and subsequent removal of support for the following two options:
def f(a, b,): ... x(1, 2, 3, 4, z=5,): ...
Function arguments are not lists. Even when separated onto multiple lines, the closing "):" should remain on the final line with other content.
That would be a lot of hassle to get rid of something that people probably aren't doing in the first place, though.
I actually make use of the feature when dealing with APIs which both a) take lots of arguments (more than fit comfortably on two lines at whatever indentation they are called), and b) have optional trailing arguments: I always leave the trailing comma in place in such cases, with the closing paren on the following line, so that adding or removing an argument at the end of the list stays consistent (the diffs are better, too, when I use this pattern). Tres. - -- =================================================================== Tres Seaver +1 540-429-0999 tseaver@palladion.com Palladion Software "Excellence by Design" http://palladion.com -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.10 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/ iEYEARECAAYFAk0GIsoACgkQ+gerLs4ltQ5HLwCfYFSyVrFtt04h6a39hyK6BD2c t8oAoJdXNS7wIsjF34ZiOQCwQGq9Qs2v =ZWqW -----END PGP SIGNATURE-----
On Mon, Dec 13, 2010 at 11:42 PM, Tres Seaver
I actually make use of the feature when dealing with APIs which both a) take lots of arguments (more than fit comfortably on two lines at whatever indentation they are called), and b) have optional trailing arguments: I always leave the trailing comma in place in such cases, with the closing paren on the following line, so that adding or removing an argument at the end of the list stays consistent (the diffs are better, too, when I use this pattern).
My personal preferences aren't strong either way, but the issues were closed because committers voiced opinions against making this consistent in the other direction (i.e. always allowing the trailing comma). I don't know that a full PEP is really needed, but the status quo is that some committers said no and others don't really care about the issue all that much, so the current behaviour is going to remain in place unless those in the first group change their mind (or Guido weighs in and says "change it"). Creating a PEP is one way to carry out such persuasion (probably overkill though). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
Nick Coghlan dixit (2010-12-13, 23:25):
Function arguments are not lists. Even when separated onto multiple lines, the closing "):" should remain on the final line with other content.
Not necessarily, IMHO. 1. What about my example with '-> xxx' return-value annotation? (especially when that annotation is a long expression) 2. There are two argument-list-formatting idioms I apply -- depending on which is more suitable in a particular case: a) when argument specs/expressions are not very long and rather if their number is not very big: def function(argument_spec1, argument_spec2, argument_spec3, argument_spec4, argument_spec5, argument_spec6): function_call(expression1, expression2, expression3, expression4, expression5, expression6) b) for long argument lists and/or argument specs/expressions (e.g. when default values or argument annotations are defined as long expressions): def function( long_argument_spec1, long_argument_spec2, long_argument_spec3, long_argument_spec4, long_argument_spec5, long_argument_spec6, ): function_call( long_expression1, long_expression2, long_expression3, long_expression4, long_expression5, long_expression6, ) Note that option 'b' is more convenient for refactorization, diffs etc. Regards, *j
On Mon, 13 Dec 2010 23:25:58 +1000, Nick Coghlan
On Mon, Dec 13, 2010 at 9:44 PM, Jan Kaliszewski
wrote: I think that seeing that:
=A0 =A0def f(a, b): ... =A0 =A0def f(a, *, b): ... =A0 =A0def f(a, *args, b): ... =A0 =A0x(1, 2, 3, 4, z=3D5) =A0 =A0x(1, *(2,3,4), z=3D5)
As per the closure of the affected tickets, the likely outcome of such a discussion would be the deprecation and subsequent removal of support for the following two options:
def f(a, b,): ... x(1, 2, 3, 4, z=3D5,): ...
Function arguments are not lists. Even when separated onto multiple lines, the closing "):" should remain on the final line with other content.
That would be a lot of hassle to get rid of something that people probably aren't doing in the first place, though.
Counter examples from google code search: http://www.google.com/codesearch/p?hl=en#copo3dCwf5E/django/utils/simplejson/encoder.py&q=^\s*\):&sa=N&cd=5&ct=rc (also appears in json in the stdlib) http://www.google.com/codesearch/p?hl=en#algXCqBNNP0/vendor/python-clientform/ClientForm.py&q=^\ *\):&sa=N&cd=3&ct=rc (class def) http://www.google.com/codesearch/p?hl=en#KT-ZlRkUunU/trunk/code/output/ExprParser.py&q=def\(.*,\s\):&sa=N&cd=2&ct=rc http://www.google.com/codesearch/p?hl=en#XnG7n8Mjf2s/GoogleSearch.py&q=def\(.*,\s\):&sa=N&cd=3&ct=rc http://www.google.com/codesearch/p?hl=en#MokQ50OeeyU/src/python/ndogen/parser/matlab/parser.py&q=def\(.*,\s\):&sa=N&cd=5&ct=rc Not many, granted, but not zero, either, and I'm sure there are lots more out there[*]. I do especially like the fact that there is one in the stdlib :) It seems like the status quo is fine. I wouldn't object to it being made more consistent. I would object to removing the existing cases. -- R. David Murray www.bitdance.com [*] code search's response to various regexes was somewhat surprising; expressions I thought should have been supersets resulted in fewer hits. Nor could I think of a way to search for function invocations ending with a comma. Then again, I usually make lots of mistakes with regexes.
On Mon, Dec 13, 2010 at 5:42 AM, Tres Seaver
I actually make use of the feature when dealing with APIs which both a) take lots of arguments (more than fit comfortably on two lines at whatever indentation they are called), and b) have optional trailing arguments: I always leave the trailing comma in place in such cases, with the closing paren on the following line, so that adding or removing an argument at the end of the list stays consistent (the diffs are better, too, when I use this pattern).
Same here, and it's a soft style rule at Google that trailing commas are good -- they can help produce shorter diffs. I'm at least +0 on allowing trailing commas in the situation the OP mentioned. -- --Guido van Rossum (python.org/~guido)
On Mon, Dec 13, 2010 at 11:54 AM, Guido van Rossum
I'm at least +0 on allowing trailing commas in the situation the OP mentioned.
FWIW, I am also about +0.5 on allowing trailing comma. Note that in a similar situation, the C standardization committee has erred on the side of consistency: """ A new feature of C99: a common extension in many implementations allows a trailing comma after the list of enumeration constants. The Committee decided to adopt this feature as an innocuous extension that mirrors the trailing commas allowed in initializers. """ http://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf Similarly, I find allowing trailing comma in keyword only arguments lists to be an innocuous extension that mirrors the trailing commas allowed in the positional arguments lists. A possible benefit that I have not seen mentioned is that if developer decides to convert some of the trailing arguments in her function to keyword only, she does not have to worry about removing the trailing comma.
On Mon, 13 Dec 2010 14:09:02 -0500
Alexander Belopolsky
On Mon, Dec 13, 2010 at 11:54 AM, Guido van Rossum
wrote: I'm at least +0 on allowing trailing commas in the situation the OP mentioned.
FWIW, I am also about +0.5 on allowing trailing comma. Note that in a similar situation, the C standardization committee has erred on the side of consistency:
""" A new feature of C99: a common extension in many implementations allows a trailing comma after the list of enumeration constants. The Committee decided to adopt this feature as an innocuous extension that mirrors the trailing commas allowed in initializers. """ http://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf
Similarly, I find allowing trailing comma in keyword only arguments lists to be an innocuous extension that mirrors the trailing commas allowed in the positional arguments lists.
+1 from me as well. Special cases are hard to remember. Regards Antoine.
On 12/13/2010 11:17 AM, Antoine Pitrou wrote:
On Mon, 13 Dec 2010 14:09:02 -0500 Alexander Belopolsky
wrote: On Mon, Dec 13, 2010 at 11:54 AM, Guido van Rossum
wrote: I'm at least +0 on allowing trailing commas in the situation the OP mentioned.
FWIW, I am also about +0.5 on allowing trailing comma. Note that in a similar situation, the C standardization committee has erred on the side of consistency:
""" A new feature of C99: a common extension in many implementations allows a trailing comma after the list of enumeration constants. The Committee decided to adopt this feature as an innocuous extension that mirrors the trailing commas allowed in initializers. """ http://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf
Similarly, I find allowing trailing comma in keyword only arguments lists to be an innocuous extension that mirrors the trailing commas allowed in the positional arguments lists. +1 from me as well. Special cases are hard to remember.
+1. I tend to put them in, and then take out the ones that Python won't accept. Then, when I add another item to the list, Python tells me to go back and put the one I took out back in again, and take out the one I put in at the new end of the list. Annoying. (for vertically arranged lists, one per line, primarily, with ) on the last line by itself.
On Mon, Dec 13, 2010 at 3:51 PM, R. David Murray
It seems like the status quo is fine. I wouldn't object to it being made more consistent. I would object to removing the existing cases.
Same here, on all three counts. In one of the projects I'm currently working on, we've settled on a style that does quite a lot of: my_thing = Thing( foo = Foo(arg1, arg2, ...), bar = Bar(arg3, arg4, ...), ... ) and I've found the trailing comma very convenient during refactoring and API experimentation. (There's still good fun to be had arguing about the indentation of that closing parenthesis, though.) Mar
On 12/13/2010 11:39 AM, Mark Dickinson wrote:
my_thing = Thing( foo = Foo(arg1, arg2, ...), bar = Bar(arg3, arg4, ...), ... )
and I've found the trailing comma very convenient during refactoring and API experimentation. (There's still good fun to be had arguing about the indentation of that closing parenthesis, though.)
Clearly it needs to be indented one level, because it is a continuation of the prior line, just like the foo and bar and ... lines are continuations and therefore indented. I'd have argued differently for languages that use {} to delimit blocks. Enjoy!
On 13Dec2010 20:17, Antoine Pitrou
On 12/13/2010 2:17 PM, Antoine Pitrou wrote:
On Mon, 13 Dec 2010 14:09:02 -0500 Alexander Belopolsky
wrote: On Mon, Dec 13, 2010 at 11:54 AM, Guido van Rossum
wrote: I'm at least +0 on allowing trailing commas in the situation the OP mentioned.
FWIW, I am also about +0.5 on allowing trailing comma. Note that in a similar situation, the C standardization committee has erred on the side of consistency:
""" A new feature of C99: a common extension in many implementations allows a trailing comma after the list of enumeration constants. The Committee decided to adopt this feature as an innocuous extension that mirrors the trailing commas allowed in initializers. """ http://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf
Similarly, I find allowing trailing comma in keyword only arguments lists to be an innocuous extension that mirrors the trailing commas allowed in the positional arguments lists.
+1 from me as well. Special cases are hard to remember.
Same here. A strong +1 for a consistent rule (always or never allowed) with a +1 for always given others use case of one param/arg per line. So I think the issues should be reopened. -- Terry Jan Reedy
On Dec 13, 2010, at 1:21 PM, Terry Reedy wrote:
Same here. A strong +1 for a consistent rule (always or never allowed) with a +1 for always given others use case of one param/arg per line.
It seems to me that a trailing comma in an argument list is more likely to be a user error than a deliberate comma-for-the-future. Raymond
On Mon, Dec 13, 2010 at 1:55 PM, Raymond Hettinger
On Dec 13, 2010, at 1:21 PM, Terry Reedy wrote:
Same here. A strong +1 for a consistent rule (always or never allowed) with a +1 for always given others use case of one param/arg per line.
It seems to me that a trailing comma in an argument list is more likely to be a user error than a deliberate comma-for-the-future.
Really? Have you observed this? Even if it was inserted by mistake, it is harmless. Python has a long tradition of allowing redundant trailing commas in comma-separated lists, and it is habit-forming. That's the issue the OP had: he expected it to work in the one context where it doesn't. -- --Guido van Rossum (python.org/~guido)
On 12/13/2010 1:55 PM, Raymond Hettinger wrote:
It seems to me that a trailing comma in an argument list is more likely to be a user error than a deliberate comma-for-the-future.
It seems to me that a trailing comma, especially in the case of one parameter per line, is a deliberate comma-for-the-future. It's a good reminder that you are dealing with a list of some sort, rather than a statement, when you look at just one parameter on the line. Especially if the ) is on the next line, which I prefer. Yes, a parameter list is not a python list, nor a python tuple, but it is still a generic, comma-separated list, and all such are more conveniently dealt with, in the multi-line case, if trailing commas are permitted. And, of course, the one-entry tuple needs the comma to differentiate it from some other expression, forcing the trailing comma into the syntax... so there can be no consistent rule for all commas that doesn't permit trailing commas.
On Tue, Dec 14, 2010 at 5:39 AM, Mark Dickinson
On Mon, Dec 13, 2010 at 3:51 PM, R. David Murray
wrote: It seems like the status quo is fine. I wouldn't object to it being made more consistent. I would object to removing the existing cases.
Same here, on all three counts. In one of the projects I'm currently working on, we've settled on a style that does quite a lot of:
my_thing = Thing( foo = Foo(arg1, arg2, ...), bar = Bar(arg3, arg4, ...), ... )
and I've found the trailing comma very convenient during refactoring and API experimentation. (There's still good fun to be had arguing about the indentation of that closing parenthesis, though.)
Another valid use case that occurred to me is building up a string-keyed dictionary: mapping = dict( x=1, y=2, z=3, ) So, on reflection, removing the existing cases where it is supported is certainly unreasonable, which makes the consistency argument that much stronger. For the record, I reopened issue #9232 (noting the lack of consensus), and (as someone suggested on the tracker) changed the resolution on the other one to be as a duplicate of #9232. Cheers, Nick. P.S. As I noted in the logging discussion, my email access is going to be a bit sketchy for the next couple of weeks. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On Dec 13, 2010, at 2:16 PM, Guido van Rossum wrote:
On Mon, Dec 13, 2010 at 1:55 PM, Raymond Hettinger
wrote: It seems to me that a trailing comma in an argument list is more likely to be a user error than a deliberate comma-for-the-future.
Really? Have you observed this? Even if it was inserted by mistake, it is harmless.
I only have one data point, my own mistakes. The SyntaxError has occasionally been helpful to me when working out a function signature or to detect a copy and paste error. In both cases, it meant that there was supposed to be another argument and it had been either forgotten or mispasted. Also, if I were reviewing someone else's code and saw a trailing comma in a function definition, it would seem weird and I would wonder if the author intended a different signature. FWIW, this isn't important to me at all. Was just noting my own experience. Don't put assign much weight to it, I don't have much of a preference either way.
Python has a long tradition of allowing redundant trailing commas in comma-separated lists, and it is habit-forming.
Right. I see that in the wild quite often and use it myself. Raymond
Terry Reedy wrote:
On 12/13/2010 2:17 PM, Antoine Pitrou wrote:
On Mon, 13 Dec 2010 14:09:02 -0500 Alexander Belopolsky
wrote: On Mon, Dec 13, 2010 at 11:54 AM, Guido van Rossum
wrote: I'm at least +0 on allowing trailing commas in the situation the OP mentioned.
FWIW, I am also about +0.5 on allowing trailing comma. Note that in a similar situation, the C standardization committee has erred on the side of consistency:
""" A new feature of C99: a common extension in many implementations allows a trailing comma after the list of enumeration constants. The Committee decided to adopt this feature as an innocuous extension that mirrors the trailing commas allowed in initializers. """ http://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf
Similarly, I find allowing trailing comma in keyword only arguments lists to be an innocuous extension that mirrors the trailing commas allowed in the positional arguments lists.
+1 from me as well. Special cases are hard to remember.
Same here. A strong +1 for a consistent rule (always or never allowed) with a +1 for always given others use case of one param/arg per line.
+1 on consistency. +1 on allowing the trailing comma. ~Ethan~
Am 13.12.2010 21:08, schrieb Glenn Linderman:
On 12/13/2010 11:39 AM, Mark Dickinson wrote:
my_thing = Thing( foo = Foo(arg1, arg2, ...), bar = Bar(arg3, arg4, ...), ... )
and I've found the trailing comma very convenient during refactoring and API experimentation. (There's still good fun to be had arguing about the indentation of that closing parenthesis, though.)
Clearly it needs to be indented one level, because it is a continuation of the prior line, just like the foo and bar and ... lines are continuations and therefore indented.
Clearly Emacs is superior to Vim because (insert some random fact here). Clearly the only thing that is clear about coding style details (even if we all more or less agree on PEP 8) is that it is a matter of personal taste. Georg
On 12/18/2010 1:04 PM, Georg Brandl wrote:
Am 13.12.2010 21:08, schrieb Glenn Linderman:
On 12/13/2010 11:39 AM, Mark Dickinson wrote:
my_thing = Thing( foo = Foo(arg1, arg2, ...), bar = Bar(arg3, arg4, ...), ... )
and I've found the trailing comma very convenient during refactoring and API experimentation. (There's still good fun to be had arguing about the indentation of that closing parenthesis, though.)
Clearly it needs to be indented one level, because it is a continuation of the prior line, just like the foo and bar and ... lines are continuations and therefore indented. Clearly Emacs is superior to Vim because (insert some random fact here).
Sure you have that right.
Clearly the only thing that is clear about coding style details (even if we all more or less agree on PEP 8) is that it is a matter of personal taste. And this too.
But apparently you missed the fact that Mark wanted some good fun arguing about the indentation of the closing parenthesis... and didn't quote my "Enjoy!" that implied that that was all I was giving him. But then, you are release manager, which would make it very difficult, but hopefully you can still have a Merry Christmas! (or whatever end-of-year holiday suits your fancy)
participants (15)
-
Alexander Belopolsky
-
Antoine Pitrou
-
Cameron Simpson
-
Ethan Furman
-
Georg Brandl
-
Glenn Linderman
-
Guido van Rossum
-
Jan Kaliszewski
-
Mark Dickinson
-
Michael Foord
-
Nick Coghlan
-
R. David Murray
-
Raymond Hettinger
-
Terry Reedy
-
Tres Seaver