Implicit line continuation for method chaining
Hi, It's becoming more popular in Python to have interfaces which are built around method chaining as a way of applying operations to data. For example in pandas is moving more towards a default model where methods do not change the object in-place but instead return an altered version (or at least an altered view) of it. The major downside to method chaining is that it ends up creating long lines of code which can become hard to read. The two main solutions to this are 1) break down the chain and give the steps their own variable names and 2) split it over multiple lines using `\` as a line continuation. e.g.: y = x.rstrip("\n").split(":")[0].lower() would become y = x.rstrip("\n") \ .split(":")[0] \ .lower() I find the continuation character visually distracting, easy to forget and does not allow for line-by-line commenting (causing `SyntaxError: unexpected character after line continuation character`): y = x.rstrip("\n") \ .split(":")[0] \ # grab the key name .lower() My idea is to alter the syntax of Python to allow doing: y = x.rstrip("\n") .split(":")[0] .lower() i.e., not requiring an explicit line continuation character in the case where the next line starts with a period. I've had a search through the archives and I couldn't see this discussion before. Feel free to point me to it if I've missed anything. Cheers, Matt
My inclination would be to cede code formatting to a tool like Black and focus on function: https://black.readthedocs.io/en/stable/ On Fri, 2021-03-12 at 15:32 +0000, Matt Williams wrote:
Hi,
It's becoming more popular in Python to have interfaces which are built around method chaining as a way of applying operations to data. For example in pandas is moving more towards a default model where methods do not change the object in-place but instead return an altered version (or at least an altered view) of it.
The major downside to method chaining is that it ends up creating long lines of code which can become hard to read. The two main solutions to this are 1) break down the chain and give the steps their own variable names and 2) split it over multiple lines using `\` as a line continuation.
e.g.:
y = x.rstrip("\n").split(":")[0].lower()
would become
y = x.rstrip("\n") \ .split(":")[0] \ .lower()
I find the continuation character visually distracting, easy to forget and does not allow for line-by-line commenting (causing `SyntaxError: unexpected character after line continuation character`):
y = x.rstrip("\n") \ .split(":")[0] \ # grab the key name .lower()
My idea is to alter the syntax of Python to allow doing:
y = x.rstrip("\n") .split(":")[0] .lower()
i.e., not requiring an explicit line continuation character in the case where the next line starts with a period.
I've had a search through the archives and I couldn't see this discussion before. Feel free to point me to it if I've missed anything.
Cheers, Matt _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/LWB3U5... Code of Conduct: http://python.org/psf/codeofconduct/
On Fri, 12 Mar 2021 at 15:53, Paul Bryan <pbryan@anode.ca> wrote:
My inclination would be to cede code formatting to a tool like Black and focus on function: https://black.readthedocs.io/en/stable/
... and if you try that out, what you'll find is that black adds parentheses: y = ( x.rstrip("\n") .split(":")[0] .lower() ) Which is a reasonable solution even if you don't like black's formatting choices. Black only wraps when the line length is exceeded, but I can see arguments for wrapping sooner than that in some cases, and personally I prefer the following indentation scheme: y = ( x.rstrip("\n") .split(":")[0] .lower() ) But the principle is the same - I think adding parentheses is an acceptable compromise, in the spirit of "Special cases aren't special enough to break the rules". I don't like needing to use backslashes to continue lines, but I find having to think about how to structure my code to avoid continuations that need backslashes tends to force me to come up with better/more readable ways of writing the expression. Chained method calls are right at the limit here. Sometimes (and only sometimes!) they are a nice way of expressing a computation, but there's no immediately natural way to line-wrap them. I can sympathise with the request here, but parentheses seem adequate to me. (And yes, I spotted the irony of suggesting paren-delimiters for extended expressions in a language that avoids using braces as delimiters for statement blocks :-)) Paul
On 3/12/21 7:50 AM, Paul Bryan wrote:
My inclination would be to cede code formatting to a tool like Black and focus on function: https://black.readthedocs.io/en/stable/
We still have to read it after Black munges it. Like Paul said, add parentheses -- it works for method chaining, string concatenation, etc. -- ~Ethan~
Can we skip ahead to considering how to implement this? I can think of two approaches: either hack the lexer to special-case a newline followed by a period (which currently can never start a line), or redesign the syntax to allow NEWLINE INDENT ‘.’ ......... NEWLINE ‘.’ ............ DEDENT at the end of an expression. Both have pros and cons. Discuss how this allows for certain typos to pass as valid syntax. On Fri, Mar 12, 2021 at 07:42 Matt Williams <matt@milliams.com> wrote:
Hi,
It's becoming more popular in Python to have interfaces which are built around method chaining as a way of applying operations to data. For example in pandas is moving more towards a default model where methods do not change the object in-place but instead return an altered version (or at least an altered view) of it.
The major downside to method chaining is that it ends up creating long lines of code which can become hard to read. The two main solutions to this are 1) break down the chain and give the steps their own variable names and 2) split it over multiple lines using `\` as a line continuation.
e.g.:
y = x.rstrip("\n").split(":")[0].lower()
would become
y = x.rstrip("\n") \ .split(":")[0] \ .lower()
I find the continuation character visually distracting, easy to forget and does not allow for line-by-line commenting (causing `SyntaxError: unexpected character after line continuation character`):
y = x.rstrip("\n") \ .split(":")[0] \ # grab the key name .lower()
My idea is to alter the syntax of Python to allow doing:
y = x.rstrip("\n") .split(":")[0] .lower()
i.e., not requiring an explicit line continuation character in the case where the next line starts with a period.
I've had a search through the archives and I couldn't see this discussion before. Feel free to point me to it if I've missed anything.
Cheers, Matt _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/LWB3U5... Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido (mobile)
Guido van Rossum wrote:
Can we skip ahead to considering how to implement this? I can think of two approaches: either hack the lexer to special-case a newline followed by a period (which currently can never start a line), or redesign the syntax to allow NEWLINE INDENT ‘.’ ......... NEWLINE ‘.’ ............ DEDENT at the end of an expression. Both have pros and cons. Discuss how this allows for certain typos to pass as valid syntax.
IIRC, BCPL's compiler would continue and expression on the next line if a line ended with an operator. E.g.: x = a + b would parse like x = (a + b); (I think this was a bit more general - if a line didn't end with ";", the parser would try adding a ";" and if that failed to parse, it would continue to the next line.) This leads result in a different style for method-chaining: y = x.rstrip("\n"). split(":")[0]. lower() ... and if you don't like ending lines with a ".", you could always add a parenthesis: y = (x.rstrip("\n") .split(":")[0] .lower()) [I wonder why C didn't adopt BCPL's convention for eliding semi-colons? ...]
On Fri, Mar 12, 2021 at 6:23 PM Peter Ludemann <peter.ludemann@gmail.com> wrote:
[I wonder why C didn't adopt BCPL's convention for eliding semi-colons? ...]
[Presumably because it caused too many surprising behaviors...] -- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-c...>
On Fri, 12 Mar 2021 at 18:27, Guido van Rossum <guido@python.org> wrote:
On Fri, Mar 12, 2021 at 6:23 PM Peter Ludemann <peter.ludemann@gmail.com> wrote:
[I wonder why C didn't adopt BCPL's convention for eliding semi-colons? ...]
[Presumably because it caused too many surprising behaviors...]
My guess is that it would have been more work in the parser, and therefore didn't fit with Unix "minimalism". ;) It's not clear to me what surprising behaviors there would be. Javascript seems to do OK with optional semicolons - presumably its algorithm is similar to what BCPL used. (Or perhaps the surprising behaviors are trivial compared to the other surprises that Javascript springs on people.)
-- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-c...>
On Sat, Mar 13, 2021 at 1:46 PM Peter Ludemann <peter.ludemann@gmail.com> wrote:
It's not clear to me what surprising behaviors there would be. Javascript seems to do OK with optional semicolons - presumably its algorithm is similar to what BCPL used. (Or perhaps the surprising behaviors are trivial compared to the other surprises that Javascript springs on people.)
Yes, right up until you try to do something like: function foo() { return thing.goes.here() } which becomes extremely common with frameworks like React.js. There's an implicit semicolon and a big block of dead code. ChrisA
Chris Angelico wrote:
On Sat, Mar 13, 2021 at 1:46 PM Peter Ludemann peter.ludemann@gmail.com wrote:
It's not clear to me what surprising behaviors there would be. Javascript seems to do OK with optional semicolons - presumably its algorithm is similar to what BCPL used. (Or perhaps the surprising behaviors are trivial compared to the other surprises that Javascript springs on people.) Yes, right up until you try to do something like: function foo() { return thing.goes.here() } which becomes extremely common with frameworks like React.js. There's an implicit semicolon and a big block of dead code. ChrisA
def foo(): return thing.goes.here() is illegal Python; and wouldn't result in a line-join by my proposal. However: def foo(): return "A long piece of text (Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.)" + thing.goes.here() would work by my proposal and avoid the extra parentheses that are currently needed.
On Fri, Mar 12, 2021 at 10:41 AM Matt Williams <matt@milliams.com> wrote:
Hi,
It's becoming more popular in Python to have interfaces which are built around method chaining as a way of applying operations to data. For example in pandas is moving more towards a default model where methods do not change the object in-place but instead return an altered version (or at least an altered view) of it.
The major downside to method chaining is that it ends up creating long lines of code which can become hard to read. The two main solutions to this are 1) break down the chain and give the steps their own variable names and 2) split it over multiple lines using `\` as a line continuation.
e.g.:
y = x.rstrip("\n").split(":")[0].lower()
would become
y = x.rstrip("\n") \ .split(":")[0] \ .lower()
I find the continuation character visually distracting, easy to forget and does not allow for line-by-line commenting (causing `SyntaxError: unexpected character after line continuation character`):
y = x.rstrip("\n") \ .split(":")[0] \ # grab the key name .lower()
My idea is to alter the syntax of Python to allow doing:
y = x.rstrip("\n") .split(":")[0] .lower()
i.e., not requiring an explicit line continuation character in the case where the next line starts with a period.
I've had a search through the archives and I couldn't see this discussion before. Feel free to point me to it if I've missed anything.
Why isn't the implicit line continuation inside parentheses sufficient? From my 3.9 REPL:
x 'TEST:TESTING:TEST3\n' y = x.rstrip("\n").split(":")[0].lower() y 'test' z = (x.rstrip("\n") ... .split(":")[0] ... .lower() ... ) z 'test'
This already works today, and every style guide with an opinion that I'm aware of prefers this implicit line continuation over the explicit backslash you demonstrate anyway.
To my mind there are too many differences between running code as a script and running it in the REPL. This would, presumably, add another one: the initial line would be executed without waiting to see if there is a dot continuation line. And it just feels wrong to have a complete syntactically valid line of code ... not be a complete line of code. You can always add outer brackets, as in other situations where lines of code become over-long: y = (x.rstrip("\n") .split(":")[0] .lower()) -1 Rob Cliffe On 12/03/2021 15:32, Matt Williams wrote:
Hi,
It's becoming more popular in Python to have interfaces which are built around method chaining as a way of applying operations to data. For example in pandas is moving more towards a default model where methods do not change the object in-place but instead return an altered version (or at least an altered view) of it.
The major downside to method chaining is that it ends up creating long lines of code which can become hard to read. The two main solutions to this are 1) break down the chain and give the steps their own variable names and 2) split it over multiple lines using `\` as a line continuation.
e.g.:
y = x.rstrip("\n").split(":")[0].lower()
would become
y = x.rstrip("\n") \ .split(":")[0] \ .lower()
I find the continuation character visually distracting, easy to forget and does not allow for line-by-line commenting (causing `SyntaxError: unexpected character after line continuation character`):
y = x.rstrip("\n") \ .split(":")[0] \ # grab the key name .lower()
My idea is to alter the syntax of Python to allow doing:
y = x.rstrip("\n") .split(":")[0] .lower()
i.e., not requiring an explicit line continuation character in the case where the next line starts with a period.
I've had a search through the archives and I couldn't see this discussion before. Feel free to point me to it if I've missed anything.
Cheers, Matt _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/LWB3U5... Code of Conduct: http://python.org/psf/codeofconduct/
This argument pretty much kills the proposal. On Fri, Mar 12, 2021 at 9:01 AM Rob Cliffe via Python-ideas < python-ideas@python.org> wrote:
To my mind there are too many differences between running code as a script and running it in the REPL. This would, presumably, add another one: the initial line would be executed without waiting to see if there is a dot continuation line. And it just feels wrong to have a complete syntactically valid line of code ... not be a complete line of code. You can always add outer brackets, as in other situations where lines of code become over-long:
y = (x.rstrip("\n") .split(":")[0] .lower())
-1 Rob Cliffe
Hi,
It's becoming more popular in Python to have interfaces which are built around method chaining as a way of applying operations to data. For example in pandas is moving more towards a default model where methods do not change the object in-place but instead return an altered version (or at least an altered view) of it.
The major downside to method chaining is that it ends up creating long
On 12/03/2021 15:32, Matt Williams wrote: lines of code which can become hard to read. The two main solutions to this are 1) break down the chain and give the steps their own variable names and 2) split it over multiple lines using `\` as a line continuation.
e.g.:
y = x.rstrip("\n").split(":")[0].lower()
would become
y = x.rstrip("\n") \ .split(":")[0] \ .lower()
I find the continuation character visually distracting, easy to forget
and does not allow for line-by-line commenting (causing `SyntaxError: unexpected character after line continuation character`):
y = x.rstrip("\n") \ .split(":")[0] \ # grab the key name .lower()
My idea is to alter the syntax of Python to allow doing:
y = x.rstrip("\n") .split(":")[0] .lower()
i.e., not requiring an explicit line continuation character in the case
where the next line starts with a period.
I've had a search through the archives and I couldn't see this
discussion before. Feel free to point me to it if I've missed anything.
Cheers, Matt _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at
https://mail.python.org/archives/list/python-ideas@python.org/message/LWB3U5...
Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/GVVLTS... Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-c...>
I have written tens of thousands of lines of Pandas code, and I've taught thousands of people to use Pandas. I've worked with the core developers of Pandas as well. This is the first time I've ever seen `\` line continuation used for fluent programming style rather than just using parentheses as a way of expressing a logical line. On Fri, Mar 12, 2021, 10:41 AM Matt Williams <matt@milliams.com> wrote:
Hi,
It's becoming more popular in Python to have interfaces which are built around method chaining as a way of applying operations to data. For example in pandas is moving more towards a default model where methods do not change the object in-place but instead return an altered version (or at least an altered view) of it.
The major downside to method chaining is that it ends up creating long lines of code which can become hard to read. The two main solutions to this are 1) break down the chain and give the steps their own variable names and 2) split it over multiple lines using `\` as a line continuation.
e.g.:
y = x.rstrip("\n").split(":")[0].lower()
would become
y = x.rstrip("\n") \ .split(":")[0] \ .lower()
I find the continuation character visually distracting, easy to forget and does not allow for line-by-line commenting (causing `SyntaxError: unexpected character after line continuation character`):
y = x.rstrip("\n") \ .split(":")[0] \ # grab the key name .lower()
My idea is to alter the syntax of Python to allow doing:
y = x.rstrip("\n") .split(":")[0] .lower()
i.e., not requiring an explicit line continuation character in the case where the next line starts with a period.
I've had a search through the archives and I couldn't see this discussion before. Feel free to point me to it if I've missed anything.
Cheers, Matt _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/LWB3U5... Code of Conduct: http://python.org/psf/codeofconduct/
participants (10)
-
Chris Angelico
-
David Mertz
-
Ethan Furman
-
Guido van Rossum
-
Jonathan Goble
-
Matt Williams
-
Paul Bryan
-
Paul Moore
-
Peter Ludemann
-
Rob Cliffe