3

My program needs an integer from the user, so I'm trying to create a loop that will occur if they enter a non-integer and doesn't end until they enter an integer. I've tried:

PlayerCount = input("How many players?")
while PlayerCount != int:
    try:
        PlayerCount = int(PlayerCount)
    except ValueError:
        print("Please enter a number between 3 and 5")
        PlayerCount = input("How many players?")

However, when a valid input is entered the loop doesn't continue, or end and allow the rest of the program to run. I simply see a line break in IDLE and a blinking cursor. Is there something else I need to do to properly end the while loop? I expected the loop to end automatically once the try block succeeds.

Prags
  • 2,457
  • 2
  • 21
  • 38

6 Answers6

4

Do this:

PlayerCount = input("How many players?")
while True: #loop forever until `break`
    try:
        PlayerCount = int(PlayerCount)
    except ValueError:
        print("Please enter a number between 3 and 5")
        PlayerCount = input("How many players?")
    else: #no error
        break #exit loop

else runs when not error is made in try.

Eb946207
  • 748
  • 8
  • 26
  • Would you mind explaining why this is better than the "not isinstance(PlayerCount, int)" example above? I noticed you called that one a "hack" but to me it seems like they do the same thing. – Matt Johnson Dec 28 '18 at 13:57
  • 1
    never mind, I read the comments farther down where it's explained that this is redundant to the try loop. Thanks for your help! – Matt Johnson Dec 28 '18 at 14:05
  • @MattJohnson Yep, if you want to use `isinstance` you can, but you should replace `int` with `numbers.Integral`. You will have to `import numbers` at the top of your code first to do that though. – Eb946207 Dec 28 '18 at 21:02
3

The error is in the line while PlayerCount != int:.

PlayerCount != int will always be true. What you probably want to do is check if PlayerCount is of an integer type. But you're actually checking if it is equivalent to the class int itself. Note that an actual integer is different from the int class.

To check whether PlayerCount is an instance of the int class, replace the condition with while not isinstance(PlayerCount, int):.

jfhr
  • 687
  • 5
  • 13
  • Don’t check against `int`, check against an `abc`. Currently that is unpythonic. Use `numbers.Integral` (`import numbers`) – Eb946207 Dec 28 '18 at 01:03
2

Try using isinstance in the while loop on the first line, then there is no need to modify the inside of the while loop:

PlayerCount = input("How many players?")
while not isinstance(PlayerCount, int):
    try:
        PlayerCount = int(PlayerCount)
    except ValueError:
        print("Please enter a number between 3 and 5")
        PlayerCount = input("How many players?")
Eb946207
  • 748
  • 8
  • 26
U13-Forward
  • 69,221
  • 14
  • 89
  • 114
0

In your code, you are comparing the user input to the int class. You aren't checking if it's an instance of int. And anyway, you're already checking with your try-except block. The except block executes when the input is not a valid integer.

Just change it to this:

PlayerCount = input("How many players?")
while True:
    try:
        PlayerCount = int(PlayerCount)
        break
    except ValueError:
        print("Please enter a number between 3 and 5")
        PlayerCount = input("How many players?")

As mentioned in the comments, an alternate way would be to use isinstance(PlayerCount, int), which returns a bool value based on if PlayerCount is an instance of int.

Pika Supports Ukraine
  • 3,612
  • 10
  • 26
  • 42
  • Only put “dangerous” code in a `if`. Use `else` (like in my answer) for the `break`. – Eb946207 Dec 28 '18 at 01:01
  • @EthanK if `int` fails with an error, `break` won't be executed anyway. Both ways accomplish the task equally well, and I just prefer this way. – Pika Supports Ukraine Dec 28 '18 at 01:02
  • @Davidthethrid They both *work*, but my one says “do thing that could cause an error, if not error then break”, while yours says “do things that could cause an error **(break could cause an error too)**” `break` can’t cause an error, so that is wrong. Break should be in the `else` because break is not why you need the `try`. – Eb946207 Dec 28 '18 at 01:07
  • @EthanK mine says something more like "execute code until an error occurs, break if `int` doesn't cause error". Also, if you didn't put *anything* that can't cause an error in a `try` block, code would get pretty cluttered up. – Pika Supports Ukraine Dec 28 '18 at 01:08
  • @Davidthethrid ...except there is no way to know that **that line** is the one that might have the error, but in mine it is more seeable which line is the problem. In this case it may not matter (because `break` doesn’t cause errors) but in other code this becomes a proplem and should not be done *ever*. – Eb946207 Dec 28 '18 at 01:11
  • Also, if nothing can cause an error, don’t pit a `try` block at all. – Eb946207 Dec 28 '18 at 01:13
  • @EthanK anyone who knows even just the basics of python can easily infer that `break` is not going to cause the error. – Pika Supports Ukraine Dec 28 '18 at 01:13
  • ...but it is still **bad practice**. – Eb946207 Dec 28 '18 at 01:18
  • @EthanK that's debatable. – Pika Supports Ukraine Dec 28 '18 at 01:26
  • What about [PEP-8](https://www.python.org/dev/peps/pep-0008/)? It says “*Additionally, for all try/except clauses, limit the try clause to the absolute minimum amount of code necessary. Again, this avoids masking bugs.*” It then goes on to tell you that you should use `else` **the way that I have said**. – Eb946207 Dec 28 '18 at 01:28
  • @EthanK technically, that's true, but in this case it makes so little difference it's not worth typing the extra code. When there are larger pieces of code in the `try` statement, *then* it might be a good time to transfer some code to an else clause. In this case, when there's one statement that obviously doesn't cause the error, it's just not worth taking it into an else clause. – Pika Supports Ukraine Dec 28 '18 at 01:34
  • You are right about that, but I like to follow the style guide **at all costs**. I probably get a bit too over the top about it (and edit **everything** to be right), and in this case it doesn’t matter that much. You should still code it right, though, even if it doesn’t matter that much. – Eb946207 Dec 28 '18 at 01:37
  • @EthanK I think it's fine to break the rules every once in a while *only when it brings larger benefits*. I think it's way easier to read the code this way, so I write it this way. – Pika Supports Ukraine Dec 28 '18 at 01:38
  • The Zen of Python (type `import this`) states: “*Special cases aren’t special enough to break the rules.*” – Eb946207 Dec 28 '18 at 01:42
0

Ask Jacob pointed out, comparing to int is incorrect. I suggest to have a loop which will retry until the input in an integer between 3 and 5:

while True:
    player_count = input('How many players (3-5)? ')
    try:
        player_count = int(player_count)
        if player_count in (3, 4, 5):
            break
    except ValueError:
        pass

A few points:

  • This approach includes only 1 input function call
  • The user input is also converted to int
  • It checks for both condition: an int and the range 3..5
Hai Vu
  • 37,849
  • 11
  • 66
  • 93
0
PlayerCount = input("How many players?")
while type(PlayerCount) != int:
    try:
        PlayerCount = int(PlayerCount)
    except ValueError:
        print("Please enter a number between 3 and 5")
        PlayerCount = input("How many players?")
    else:
        pass

else part will be executed when there is no any exception.