3
\$\begingroup\$

In the following code I used a while True: loop and implemented break, because I couldn't figure out a cleaner solution than an infinite loop. Is this approach considered bad practice? Here is my code:

while True:
    z = int(input("Y: "))

    if (z >= 0 and z <= 20):
        break

for x in range(z):

    print("-" * (z-x) + "*" * x + " " * x + "-" * (z-x))

... which outputs:

Y: 12
------------------------
-----------* -----------
----------**  ----------
---------***   ---------
--------****    --------
-------*****     -------
------******      ------
-----*******       -----
----********        ----
---*********         ---
--**********          --
-***********           -
\$\endgroup\$

3 Answers 3

3
\$\begingroup\$

I couldn't figure out a cleaner solution than an infinite loop. Is this approach considered bad practice?

You can't do much better, since Python doesn't have the equivalent of a C-style do/while.

This is a trivial program so I doubt it's even worth adding functions (etc.); the one thing that can be improved is

if (z >= 0 and z <= 20):

can be

if 0 <= z <= 20:
\$\endgroup\$
1
\$\begingroup\$

I thought I'd add a useful concept for more complex cases. I fully agree with the previous answers though, they are more adequate for a simple case like this.

The walrus operator := can be useful when looping and simultaneously updating a variable. So in your simple case it might look like this:

while (z := int(input("Y: "))) not in range(0, 21):
    pass

An equivalent way to write it would be

while not (0 <= (z := int(input("Y: "))) <= 20):
    pass

This will assign int(input("Y: ")) to z until it is in range(0, 21), i.e. between 0 and 21. This makes it so you could use z for further calculations in the body of your loop without the need for additional assignment. This will throw an error for non-number inputs though, so be careful. I personally only prefer this approach if there are additional calculations to be done with the value, since I don't like using pass here. I find it actually decreases readability compared to your approach.

\$\endgroup\$
0
\$\begingroup\$

There's nothing wrong with using a while True loop: it often produces the most readable implementation.

Here's an alternative approach. It has one clear benefit: it handles invalid inputs. It also illustrates a useful technique to include in the toolbox: recursion for user-input, validation, and related needs that can fail but tend not to fail a large N of times. Opinions often differ on such matters, but this one reads about as well to me as the while True loop. Finally, when checking whether an integer falls within certain bounds, a range() is sometimes clearer than a pair of comparisons (and it does not raise a TypeError if the value is None).

def get_int(prompt):
    try:
        z = int(input(prompt))
    except ValueError:
        z = None
    return z if z in range(0, 21) else get_int(prompt)
\$\endgroup\$
4
  • \$\begingroup\$ @HTML402 please don't add this technique to your tool box. Recursion adds no benefits when dealing with user input. Use an iterative approach instead. \$\endgroup\$
    – danzel
    Mar 21, 2021 at 0:36
  • \$\begingroup\$ each recursive call unnecessarily adds an execution frame to the call stack. But the real problem is that the number of invalid inputs has no upper bound, whereas the number of recursive calls is limited. Once you hit that limit (IIRC CPython will raise a RecursionError when the recursion depth exceeds 1000 by default), the only reasonable thing to do (at least in the particular case of user input) is call the recursive function again from the same or lower recursion depth, which requires a loop. And then you have recursion and iteration where iteration alone would have been sufficient. \$\endgroup\$
    – danzel
    Mar 23, 2021 at 18:33
  • \$\begingroup\$ I'm only concerned about the "a useful technique to include in the toolbox: recursion for user-input, validation, and related needs that can fail but tend not to fail a large N of times." In real world programming, 'tends to' is not applicable, especially when dealing with user input. If you don't want to introduce a potential exploit or potentially bad user experience, the edge case I mentioned must be handled, either in the function itself or by the caller of the function. In any case, you'll end up with a while True: try: ... around the recursive function. \$\endgroup\$
    – danzel
    Mar 23, 2021 at 21:23
  • \$\begingroup\$ the only difference between the while True loop and the recursive function is that the latter introduces potential problems without any benefit. No time or effort is wasted using the loop instead of the recursive function. Please don't get me wrong, it's fine to demonstrate that one can hammer a nail into a wall using the handle of a screwdriver, but that doesn't make it a useful technique. \$\endgroup\$
    – danzel
    Mar 23, 2021 at 22:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.