PEP 572: Write vs Read, Understand and Control Flow

Hi, I have been asked to express myself on the PEP 572. I'm not sure that it's useful, but here is my personal opinion on the proposed "assignment expressions". PEP 572 -- Assignment Expressions: https://www.python.org/dev/peps/pep-0572/ First of all, I concur with others: Chris Angelico did a great job to design a good and full PEP, and a working implementation which is also useful to play with it! WARNING! I was (strongly) opposed to PEP 448 Unpacking Generalizations (ex: [1, 2, *list]) and PEP 498 f-string (f"Hello {name}"), whereas I am now a happy user of these new syntaxes. So I'm not sure that I have good tastes :-) Tim Peter gaves the following example. "LONG" version: diff = x - x_base if diff: g = gcd(diff, n) if g > 1: return g versus the "SHORT" version: if (diff := x - x_base) and (g := gcd(diff, n)) > 1: return g == Write == If your job is to write code: the SHORT version can be preferred since it's closer to what you have in mind and the code is shorter. When you read your own code, it seems straightforward and you like to see everything on the same line. The LONG version looks like your expressiveness is limited by the computer. It's like having to use simple words when you talk to a child, because a child is unable to understand more subtle and advanced sentences. You want to write beautiful code for adults, right? == Read and Understand == In my professional experience, I spent most of my time on reading code, rather than writing code. By reading, I mean: try to understand why this specific bug that cannot occur... is always reproduced by the customer, whereas we fail to reproduce it in our test lab :-) This bug is impossible, you know it, right? So let's say that you never read the example before, and it has a bug. By "reading the code", I really mean understanding here. In your opinion, which version is easier to *understand*, without actually running the code? IMHO the LONG version is simpler to understand, since the code is straightforward, it's easy to "guess" the *control flow* (guess in which order instructions will be executed). Print the code on paper and try to draw lines to follow the control flow. It may be easier to understand how SHORT is more complex to understand than LONG. == Debug == Now let's imagine that you can run the code (someone succeeded to reproduce the bug in the test lab!). Since it has a bug, you now likely want to try to understand why the bug occurs using a debugger. Sadly, most debugger are designed as if a single line of code can only execute a single instruction. I tried pdb: you cannot only run (diff := x - x_base) and then get "diff" value, before running the second assingment, you can only execute the *full line* at once. I would say that the LONG version is easier to debug, at least using pdb. I'm using regularly gdb which implements the "step" command as I expect (don't execute the full line, execute sub expressions one by one), but it's still harder to follow the control flow when a single line contains multiple instructions, than debugging lines with a single instruction. You can see it as a limitation of pdb, but many tools only have the granularity of whole line. Think about tracebacks. If you get an exception at "line 1" in the SHORT example (the long "if" expression), what can you deduce from the line number? What happened? If you get an exception in the LONG example, the line number gives you a little bit more information... maybe just enough to understand the bug? Example showing the pdb limitation:
(Pdb) p x *** NameError: name 'x' is not defined (Pdb) p y *** NameError: name 'y' is not defined (Pdb) step --Return--
<stdin>(3)f()->None
(Pdb) p x 1 (Pdb) p y 2 ... oh, pdb gone too far. I expected a break after "x := 1" and before "y := 2" :-( == Write code for babies! == Please don't write code for yourself, but write code for babies! :-) These babies are going to maintain your code for the next 5 years, while you moved to a different team or project in the meanwhile. Be kind with your coworkers and juniors! I'm trying to write a single instruction per line whenever possible, even if the used language allows me much more complex expressions. Even if the C language allows assignments in if, I avoid them, because I regularly have to debug my own code in gdb ;-) Now the question is which Python are allowed for babies. I recall that a colleague was surprised and confused by context managers. Does it mean that try/finally should be preferred? What about f'Hello {name.title()}' which calls a method into a "string" (formatting)? Or metaclasses? I guess that the limit should depend on your team, and may be explained in the coding style designed by your whole team? Victor

On Tue, Apr 24, 2018 at 2:21 AM, Victor Stinner <vstinner@redhat.com> wrote:
I don't like the idea of assignment expressions in Python. “Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.” - Brian Kernighan Of the proposed syntaxes, I dislike identifer := expression less, but I'd still rather not see it added.

was going to tell instead of := maybe => better := too close to other langs Abdur-Rahmaan Janhangeer https://github.com/Abdur-rahmaanJ Of the proposed syntaxes, I dislike identifer := expression less, but
I'd still rather not see it added.

On Wed, Jul 04, 2018 at 03:21:29PM +0400, Abdur-Rahmaan Janhangeer wrote:
The fact that := will be familiar to many people (especially if they know Go, Pascal or Eiffel etc) is a point in its favour. https://en.wikipedia.org/wiki/Assignment_%28computer_science%29#Notation The => arrow puts the arguments around the "wrong" way compared to regular assignment: name = expression expression => name Although personally I like that order, many people did not and I think Guido ruled it out very early in the discussion. Also it may be too easily confused with <= or >= or the -> syntax from annotations. -- Steve

agree for => but how many people use pascal eiffel etc? (go has a chance) that's a reminder of an old, fading epoch, bland IDEs, hard-to-crunch fonts BDL Guido once remarked in a pycon talk that today agencies would've charged you a high price to tell you what the word python might tickle in the subconscious of the common user, we should maybe write on what ":=" tickles in the mind of most programmers Abdur-Rahmaan Janhangeer https://github.com/Abdur-rahmaanJ Mauritius The fact that := will be familiar to many people (especially if they

On 04.07.2018 15:29, Victor Stinner wrote:
The PEP 572 has been approved, it's no longer worth it to discuss it ;-)
Victor
As of now, https://www.python.org/dev/peps/pep-0572/ is marked as "draft".
-- Regards, Ivan

On Wed, Jul 04, 2018 at 03:36:18PM +0300, Ivan Pozdeev via Python-Dev wrote:
https://mail.python.org/pipermail/python-dev/2018-July/154231.html https://mail.python.org/pipermail/python-dev/2018-July/154247.html The state of the published PEP has unfortunately lagged behind the state of the discussion, due to the overwhelming number of posts. -- Steve

On Tue, Apr 24, 2018 at 2:21 AM, Victor Stinner <vstinner@redhat.com> wrote:
Even if the C language allows assignments in if, I avoid them, because I regularly have to debug my own code in gdb ;-)
I personally haven't written a lot of C, so have no personal experience, but if this is at all a common approach among experienced C developers, it tells us a lot. We shouldn't add a feature that people would make a point of avoiding! OT note:
well, no, because try ... finally is even more confusing -- at least that's the impression I get from spending 20 minutes on it with newbies in my class last night :-)
Or metaclasses?
metaclasses are well known to be advanced juju -- they should not (and probably don't) show up in everyday code. Even if they are used in everyday code, the use is usually hidden -- i.e. when a user subclasses from an ORM model class, they are using metaclasses, but they don't have to know that. That is -- they fall into the category of stuff that should only be used by library/framework developers -- and, in fact, the whole point is to make everyday code easier. In fact, the number of developers that need to write/debug metaclasses, context managers, decorators, is far fewer than the number of folks that USE those things. So the standards are very different. -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

On Fri, 27 Apr 2018 09:49:33 -0700 Chris Barker <chris.barker@noaa.gov> wrote:
I think it's a matter of taste and personal habit. Some people will often do it, some less. Note that C also has a tendency to make it more useful, because doesn't have exceptions, so functions need to (ab)use return values when they want to indicate an error. When you're calling such functions (for example I/O functions), you routinely have to check for special values indicating an error, so it's common to see code such as: // Read up to n bytes from file descriptor if ((bytes_read = read(fd, buf, n)) == -1) { // Error occurred while reading, do something } Regards Antoine.

2018-04-28 17:45 GMT+02:00 Antoine Pitrou <solipsis@pitrou.net>:
About C, there is a popular coding style (not used by our PEP 7) for comparison: if (-1 == n) ... The advantage is to prevent typo mistakes because "-1 = n" is a syntax error. Victor

Thanks Antoine, this is an important point that I hope doesn't get lost. In a language with exceptions, assignment expressions are less needful. Also, the pattern of having of having mutating methods return None further limits the utility. Raymond

[Raymond Hettinger <raymond.hettinger@gmail.com>]
It doesn't diminish the utility one whit in cases where binding expressions are helpful ;-) What you're saying is that there are _fewer_ such opportunities in Python than in C. Which may or may not be true (depending on the code you're working with). If you believe it is true, fine, then that also argues against that people will rush to abuse the feature (to the extent that it's even plausibly useful less often, to that extent also will there be less temptation to use it at all). But then I only care about use cases at heart, and have presented real-life examples wherein binding expressions read both better and worse than what they're replacing. I intend to limit myself to the cases where they read better :-) Which are most of the cases I even considered, BTW - in the vast majority of cases in real code I'd use them, they'd be replacing the annoyingly bare-bones yet somehow repetitive anyway: value = f() if value; doing something with value with the still bare-bones but minimally repetitive: if value := f(): doing something with value For example, tons of functions I write and use return None or 0 or False when they want to communicate "I have nothing useful to return in this case - but since you expected that might happen, I'm not going to annoy you with an exception". That pattern isn't solely limited to regexp search and match functions. The "win" above is minor but frequent. It adds up. There are other cases where binding expressions really shine, but they're much rarer in all the code I looked at (e.g., see the uselessly ever-increasing indentation levels near the end of `copy()` in the std library's copy.py). In all, I expect I'd use them significantly more often than ternary `if`, but far less often than augmented assignments. If the PEP is accepted, that's what all Pythoneers will be saying 5 years from now ;-)

On Tue, Apr 24, 2018 at 2:21 AM, Victor Stinner <vstinner@redhat.com> wrote:
I don't like the idea of assignment expressions in Python. “Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.” - Brian Kernighan Of the proposed syntaxes, I dislike identifer := expression less, but I'd still rather not see it added.

was going to tell instead of := maybe => better := too close to other langs Abdur-Rahmaan Janhangeer https://github.com/Abdur-rahmaanJ Of the proposed syntaxes, I dislike identifer := expression less, but
I'd still rather not see it added.

On Wed, Jul 04, 2018 at 03:21:29PM +0400, Abdur-Rahmaan Janhangeer wrote:
The fact that := will be familiar to many people (especially if they know Go, Pascal or Eiffel etc) is a point in its favour. https://en.wikipedia.org/wiki/Assignment_%28computer_science%29#Notation The => arrow puts the arguments around the "wrong" way compared to regular assignment: name = expression expression => name Although personally I like that order, many people did not and I think Guido ruled it out very early in the discussion. Also it may be too easily confused with <= or >= or the -> syntax from annotations. -- Steve

agree for => but how many people use pascal eiffel etc? (go has a chance) that's a reminder of an old, fading epoch, bland IDEs, hard-to-crunch fonts BDL Guido once remarked in a pycon talk that today agencies would've charged you a high price to tell you what the word python might tickle in the subconscious of the common user, we should maybe write on what ":=" tickles in the mind of most programmers Abdur-Rahmaan Janhangeer https://github.com/Abdur-rahmaanJ Mauritius The fact that := will be familiar to many people (especially if they

On 04.07.2018 15:29, Victor Stinner wrote:
The PEP 572 has been approved, it's no longer worth it to discuss it ;-)
Victor
As of now, https://www.python.org/dev/peps/pep-0572/ is marked as "draft".
-- Regards, Ivan

On Wed, Jul 04, 2018 at 03:36:18PM +0300, Ivan Pozdeev via Python-Dev wrote:
https://mail.python.org/pipermail/python-dev/2018-July/154231.html https://mail.python.org/pipermail/python-dev/2018-July/154247.html The state of the published PEP has unfortunately lagged behind the state of the discussion, due to the overwhelming number of posts. -- Steve

On Tue, Apr 24, 2018 at 2:21 AM, Victor Stinner <vstinner@redhat.com> wrote:
Even if the C language allows assignments in if, I avoid them, because I regularly have to debug my own code in gdb ;-)
I personally haven't written a lot of C, so have no personal experience, but if this is at all a common approach among experienced C developers, it tells us a lot. We shouldn't add a feature that people would make a point of avoiding! OT note:
well, no, because try ... finally is even more confusing -- at least that's the impression I get from spending 20 minutes on it with newbies in my class last night :-)
Or metaclasses?
metaclasses are well known to be advanced juju -- they should not (and probably don't) show up in everyday code. Even if they are used in everyday code, the use is usually hidden -- i.e. when a user subclasses from an ORM model class, they are using metaclasses, but they don't have to know that. That is -- they fall into the category of stuff that should only be used by library/framework developers -- and, in fact, the whole point is to make everyday code easier. In fact, the number of developers that need to write/debug metaclasses, context managers, decorators, is far fewer than the number of folks that USE those things. So the standards are very different. -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

On Fri, 27 Apr 2018 09:49:33 -0700 Chris Barker <chris.barker@noaa.gov> wrote:
I think it's a matter of taste and personal habit. Some people will often do it, some less. Note that C also has a tendency to make it more useful, because doesn't have exceptions, so functions need to (ab)use return values when they want to indicate an error. When you're calling such functions (for example I/O functions), you routinely have to check for special values indicating an error, so it's common to see code such as: // Read up to n bytes from file descriptor if ((bytes_read = read(fd, buf, n)) == -1) { // Error occurred while reading, do something } Regards Antoine.

2018-04-28 17:45 GMT+02:00 Antoine Pitrou <solipsis@pitrou.net>:
About C, there is a popular coding style (not used by our PEP 7) for comparison: if (-1 == n) ... The advantage is to prevent typo mistakes because "-1 = n" is a syntax error. Victor

Thanks Antoine, this is an important point that I hope doesn't get lost. In a language with exceptions, assignment expressions are less needful. Also, the pattern of having of having mutating methods return None further limits the utility. Raymond

[Raymond Hettinger <raymond.hettinger@gmail.com>]
It doesn't diminish the utility one whit in cases where binding expressions are helpful ;-) What you're saying is that there are _fewer_ such opportunities in Python than in C. Which may or may not be true (depending on the code you're working with). If you believe it is true, fine, then that also argues against that people will rush to abuse the feature (to the extent that it's even plausibly useful less often, to that extent also will there be less temptation to use it at all). But then I only care about use cases at heart, and have presented real-life examples wherein binding expressions read both better and worse than what they're replacing. I intend to limit myself to the cases where they read better :-) Which are most of the cases I even considered, BTW - in the vast majority of cases in real code I'd use them, they'd be replacing the annoyingly bare-bones yet somehow repetitive anyway: value = f() if value; doing something with value with the still bare-bones but minimally repetitive: if value := f(): doing something with value For example, tons of functions I write and use return None or 0 or False when they want to communicate "I have nothing useful to return in this case - but since you expected that might happen, I'm not going to annoy you with an exception". That pattern isn't solely limited to regexp search and match functions. The "win" above is minor but frequent. It adds up. There are other cases where binding expressions really shine, but they're much rarer in all the code I looked at (e.g., see the uselessly ever-increasing indentation levels near the end of `copy()` in the std library's copy.py). In all, I expect I'd use them significantly more often than ternary `if`, but far less often than augmented assignments. If the PEP is accepted, that's what all Pythoneers will be saying 5 years from now ;-)
participants (12)
-
Abdur-Rahmaan Janhangeer
-
Antoine Pitrou
-
Chris Barker
-
Dan Stromberg
-
Ethan Furman
-
Ivan Pozdeev
-
Mike Miller
-
Raymond Hettinger
-
Steven D'Aprano
-
Sven R. Kunze
-
Tim Peters
-
Victor Stinner