osdir.com


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Python-Dev] Assignment expression and coding style: the while True case


On 05.07.2018 1:51, Victor Stinner wrote:
> Hi,
>
> Let's say that the PEP 572 (assignment expression) is going to be
> approved. Let's move on and see how it can be used in the Python
> stdlib.
>
> I propose to start the discussion about "coding style" (where are
> assignment expressions appropriate or not?) with the "while True"
> case.
>
> I wrote a WIP pull request to use assignment expressions in "while True":
> https://github.com/python/cpython/pull/8095/files
>
> In short, replace:
>
>      while True:
>          x = expr
>          if not x:
>              break
>          ...
> with:
>
>      while (x := expr):
>          ...
>
> My question is now: for which "while True" patterns are the assignment
> expression appropriate? There identified different patterns.
>
>
> == Pattern 1, straighforward ==
>
> while True:
>      line = input.readline()
>      if not line:
>          break
>      ...
>
> IMHO here assingment expression is appropriate here. The code remains
> straighfoward to read.
>
> while (line := input.readline()):
>      ...
>
>
> == Pattern 2, condition ==
>
> Condition more complex than just "not line":
>
> while True:
>      q = c//n
>      if n <= q:
>          break
>      ...
>
> replaced with:
>
> while (q := c//n) < n:
>      ...
>
> IMHO it's still acceptable to use assignement expression... Maybe only
> for basic conditions? (see above)
>
>
> == Pattern 3, double condition ==
>
> while True:
>      s = self.__read(1)
>      if not s or s == NUL:
>          break
>      ....
>
> replaced with:
>
> while (s := self.__read(1)) and s != NUL:
>      ...
>
> Honestly, here, I don't know if it's appropriate...
>
> At the first look, "s != NUL" is surprising, since "s" is not defined
> before the while, it's only defined in the first *test* (defining a
> variable inside a test is *currently* uncommon in Python).
>
>
> == Pattern 4, while (...): pass ==
>
> Sometimes, the loop body is replaced by "pass".
>
> while True:
>      tarinfo = self.next()
>      if tarinfo is None:
>          break
>
> replaced with:
>
> while (tarinfo := self.next()) is not None:
>      pass
>
> It reminds me the *surprising* "while (func());" or "while (func())
> {}" in C (sorry for theorical C example, I'm talking about C loops
> with an empty body).
>
> Maybe it's acceptable here, I'm not sure.
Would be more readable with a more descriptive variable name:

while (chunk := self.next()) is not None:
     pass
tarinfo = chunk


> Note: such loop is rare (see my PR).
>
>
> == Pattern 5, two variables ==
>
> while True:
>      m = match()
>      if not m:
>          break
>      j = m.end()
>      if i == j:
>          break
>      ...
>
> replaced with:
>
> while (m := match()) and (j := m.end()) == i:
>      ...
>
> Maybe we reached here the maximum acceptable complexity of a single
> Python line? :-)
Would be more readable with additional parentheses:

while (m := match()) and ((j := m.end()) == i):

>
>
> == Other cases ==
>
> I chose to not use assignment expressions for the following while loops.
>
> (A)
>
> while True:
>      name, token = _getname(g)
>      if not name:
>          break
>      ...
>
> "x, y := ..." is invalid. It can be tricked using "while (x_y :=
> ...)[0]: x, y = x_y; ...". IMHO it's not worth it.
>
> (B)
>
> while True:
>      coeff = _dlog10(c, e, places)
>      # assert len(str(abs(coeff)))-p >= 1
>      if coeff % (5*10**(len(str(abs(coeff)))-p-1)):
>          break
>      places += 3
>
> NOT replaced with:
>
> while not (coeff := _dlog10(c, e, places)) % (5*10**(len(str(abs(coeff)))-p-1)):
>      places += 3
>
> ^-- Tim Peters, I'm looking at you :-)
>
> coeff is defined and then "immediately" used in "y" expression of
> x%y... Yeah, it's valid code, but it looks too magic to me...
>
> (C)
>
> while True:
>      chunk = self.raw.read()
>      if chunk in empty_values:
>          nodata_val = chunk
>          break
>      ...
>
> "nodata_val = chunk" cannot be put into the "chunk := self.raw.read()"
> assignment expression combined with a test. At least, I don't see how.
> (D)
>
> while 1:
>      u1 = random()
>      if not 1e-7 < u1 < .9999999:
>          continue
>      ...
>
> Again, I don't see how to use assignment expression here.
Here, unlike the previous example, the assignment is a subexpression of 
the conditions, so it _can_ be inlined:

while? (u1:=random()) < 1e-7 or u1 > .9999999:
>
> Victor
> _______________________________________________
> Python-Dev mailing list
> Python-Dev at python.org
> https://mail.python.org/mailman/listinfo/python-dev
> Unsubscribe: https://mail.python.org/mailman/options/python-dev/vano%40mail.mipt.ru

-- 
Regards,
Ivan