<div dir="ltr"><div class="gmail_quote"><div dir="ltr">[Victor Stinner]Let's say that the PEP 572 (assignment expression) is going to be<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
approved. Let's move on and see how it can be used in the Python stdlib.<br></blockquote><div><br>Ugh - how adult ;-)<br> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">I propose to start the discussion about "coding style" (where are<br>
assignment expressions appropriate or not?) with the "while True"<br>
case.<br>
<br>
I wrote a WIP pull request to use assignment expressions in "while True":<br>
<a href="https://github.com/python/cpython/pull/8095/files" rel="noreferrer" target="_blank">https://github.com/python/cpython/pull/8095/files</a><br>
<br>
In short, replace:<br>
<br>
while True:<br>
x = expr<br>
if not x:<br>
break<br>
...<br>
with:<br>
<br>
while (x := expr):</blockquote><div><br>Better is to translate it to:<br><br> while x := expr:<br><br>That is, ;parentheses aren't needed in this context, and adding them anyway will quickly look as strange here as, e.g.,<br><br> return (result)<br><br>already looks. (Always requiring parens was rejected - see the PEP's "<font face="arial, helvetica, sans-serif" size="2" style="box-sizing:border-box;color:rgb(55,118,171);text-decoration-line:none;border-bottom:1px solid rgb(255,223,118);display:inline"><a class="gmail-toc-backref" href="https://www.python.org/dev/peps/pep-0572/#id24" style="box-sizing:border-box;color:rgb(55,118,171);text-decoration-line:none;border-bottom:1px solid rgb(255,223,118);display:inline">Always requiring parentheses</a>" </font>section). ...</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">...<br>
== Pattern 3, double condition ==<br>
<br>
while True:<br>
s = self.__read(1)<br>
if not s or s == NUL:<br>
break<br>
....<br>
<br>
replaced with:<br>
<br>
while (s := self.__read(1)) and s != NUL:<br> ...<br>
Honestly, here, I don't know if it's appropriate...<br></blockquote><div><br>Then leave it be! My rule was "if it's not obviously better - at least a little - don't use it". This one is a wash (tie) to me, so I'd save the bother of changing it. Or just do the obvious part:<br><br> while s := self.__read(1):</div><div> if s == NUL:</div><div> break<br><br>No matter how the code may change in the future, the loop body surely requires a non-empty `s` to stare at. and now the `while` header makes that abundantly clear at a glance.<br><br>...</div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
== Pattern 4, while (...): pass ==<br>
<br>
Sometimes, the loop body is replaced by "pass".<br>
<br>
while True:<br>
tarinfo = self.next()<br>
if tarinfo is None:<br>
break<br>
<br>
replaced with:<br>
<br>
while (tarinfo := self.next()) is not None:<br>
pass<br>
<br>
It reminds me the *surprising* "while (func());" or "while (func())<br>
{}" in C (sorry for theorical C example, I'm talking about C loops<br>
with an empty body).<br>
<br>
Maybe it's acceptable here, I'm not sure.<br>
<br>
Note: such loop is rare (see my PR).<br></blockquote><div><br>I decided "slight loss - don't bother" for most such in my own code. At least the first spelling above cuts the number of statements in half. Replacing random.py's<br><br> r = getrandbits(k)<br><div> while r >= n:</div><div> r = getrandbits(k)</div><div><br>with</div><div><br></div><div> while (r := getrandbits(k)) >= n:</div><div> pass</div><div><br></div><div>is more attractive, for eliminating a textually identical (except for indentation) line.<br><br></div></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">== Pattern 5, two variables ==<br>
<br>
while True:<br>
m = match()<br>
if not m:<br>
break<br>
j = m.end()<br>
if i == j:<br>
break<br>
...<br>
<br>
replaced with:<br>
<br>
while (m := match()) and (j := m.end()) == i:<br>
...<br>
<br>
Maybe we reached here the maximum acceptable complexity of a single<br>
Python line? :-)<br></blockquote><div><br>It's at my limit. But, as in an earlier example, I'd be tempted to do "the obvious part":<br><br> while m:= match():</div><div> j = m.end()<br> if i == j::</div><div> break</div><div> <br></div><div>Then the start reads like "while there's something _to_ look at::" and the body of the loop is happily guaranteed that there is.<br><br>...</div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">I chose to not use assignment expressions for the following while loops.<br>
<br>
(A)<br>
<br>
while True:<br>
name, token = _getname(g)<br>
if not name:<br>
break<br>
...<br>
<br>
"x, y := ..." is invalid. It can be tricked using "while (x_y :=...)[0]: x, y = x_y; ...". IMHO it's not worth it.</blockquote><div><br>Indeed, it's quite worth _not_ doing it :-)<br> </div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">(B)<br>
<br>
while True:<br>
coeff = _dlog10(c, e, places)<br>
# assert len(str(abs(coeff)))-p >= 1<br>
if coeff % (5*10**(len(str(abs(coeff)))-p-1)):<br>
break<br>
places += 3<br>
<br>
NOT replaced with:<br>
<br>
while not (coeff := _dlog10(c, e, places)) % (5*10**(len(str(abs(coeff)))-p-1)):<br>
places += 3<br>
<br>
^-- Tim Peters, I'm looking at you :-)<br></blockquote><div><br>Not my code ;-) - and it's _already_ too "busy" to be my code. The `5*10**...` part is already crying to be broken into simpler pieces with a comment explaining what the intent is.<br><br> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">coeff is defined and then "immediately" used in "y" expression of<br>
x%y... Yeah, it's valid code, but it looks too magic to me...<br></blockquote><div><br></div><div>And the code was already too dense to follow easily.<br><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
(C)<br>
<br>
while True:<br>
chunk = self.raw.read()<br>
if chunk in empty_values:<br>
nodata_val = chunk<br>
break<br>
...<br>
<br>
"nodata_val = chunk" cannot be put into the "chunk := self.raw.read()"<br>
assignment expression combined with a test. At least, I don't see how.<br>
<br></blockquote><div>No need to strain, either! If it's not obvious, don't bother.<br><br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
(D)<br>
<br>
while 1:<br>
u1 = random()<br>
if not 1e-7 < u1 < .9999999:<br>
continue<br>
...<br>
<br>
Again, I don't see how to use assignment expression here.<br><br></blockquote><div>It could be, in context, but not for the outermost `while 1:`.</div><div><br></div><div>while 1:</div><div> while not 1e-7 < (u1 := random()) < 9999999:</div><div> pass</div><div> # code that uses u1, and possibly returns, else goes</div><div> # around the outer loop again</div><div> </div><div> That one is fine by me either way.<br><br>In all, I'd say our tastes here are pretty similar! So there's hope ;-)</div><div><br><br><br></div></div></div>