2

EDIT: The suggested duplicate is incredibly helpful in regards to basic input validation. While it does cover a lot, my specific problem (failing to assign int(evaluation) to a variable) is only explicitly addressed here. I'm marking this separately in case anyone else has made a similarly silly mistake :)

I've spent the last few weeks playing with Python 2.7 and having a lot of fun. To learn more about while loops, I've created a small script which asks the user for an integer between 1 and 10.

My goal is to then be able to respond to cases in which the user responds with unexpected input, like a non-integer, or an integer outside the specified range. I've been able to fix a lot of my issues with help from other StackOverflow threads, but now I'm stumped.

First, I created a variable, idiocy, to keep track of exceptions. (The script is supposed to be sassy, but until I get it working, I'm the one it's making fun of.)

idiocy = 0

while 1:
    evaluation = raw_input("> ")
    try:
        int(evaluation)
        if evaluation < 1 or evaluation > 10:
            raise AssertionError
    except ValueError:
        idiocy += 1
        print "\nEnter an INTEGER, dirtbag.\n"
    except AssertionError:
        idiocy += 1
        print "\nI said between 1 and 10, moron.\n"
    else:
        if idiocy == 0:
            print "\nOkay, processing..."
        else:
            print "\nDid we finally figure out how to follow instructions?"
            print "Okay, processing..."
        break

As you can see, I'm trying to handle two different errors -- a ValueError for the input type, and an AssertionError for the integer range -- and keep track of how many times they're raised. (Really, I only care about knowing whether or not they've been raised at least once; that's all I need to insult the user.)

Anyways, when I run the script in its current form, the error response works just fine ('dirtbag' for non-integers, 'moron' for out-of-range). The problem is that even when I input a valid integer, I still get an out-of-range AssertionError.

I suspect that my issue has to do with my while logic, but I'm not sure what to do. I've added a break here or there but that doesn't seem to help. Any suggestions or blatant errors? Again, total Python beginner here, so I'm half winging it.

//If anyone has simpler, cleaner, or prettier ways to do this, feel free to let me know too. I'm here to learn!

platov
  • 35
  • 8
  • I would stop working with python 2.7 and focus on python 3, preferably version 3.6.2 if you can. The end of life for 2.7 is only a couple of years away and it would be good if you started out on the right foot without having to relearn some of the finer points. – Matthew Purdon Aug 17 '17 at 02:27
  • Possible duplicate of [Asking the user for input until they give a valid response](https://stackoverflow.com/questions/23294658/asking-the-user-for-input-until-they-give-a-valid-response) – wwii Aug 17 '17 at 02:33
  • @MatthewPurdon I've been learning with the help of Zed Shaw's infamously divisive book, which only addresses 2.7, since I like its format. That being said, I've thought about moving to 3.x before. I'll look around, but are there any [updated] e-books or websites you'd recommend I get started with? – platov Aug 17 '17 at 04:17

4 Answers4

2

Your problem is you're not saving the int version of evaluation to evaluation like this:

idiocy = 0

while 1:
    evaluation = raw_input("> ")
    try:
        evaluation = int(evaluation) <--- here
        if evaluation < 1 or evaluation > 10:
            raise AssertionError
    except ValueError:
        idiocy += 1
        print "\nEnter an INTEGER, dirtbag.\n"
    except AssertionError:
        idiocy += 1
        print "\nI said between 1 and 10, moron.\n"
    else:
        if idiocy == 0:
            print "\nDid we finally figure out how to follow instructions?"
            print "Okay, processing..."
        else:
            print "\nOkay, processing..."

If you wanted to track the types of exceptions raised, you could use collections.Counter for idiocy and change the code like this:

from collections import Counter

idiocy = Counter()

while 1:
    evaluation = raw_input("> ")
    try:
        evaluation = int(evaluation)
        if evaluation < 1 or evaluation > 10:
            raise AssertionError
    except ValueError as e:
        idiocy[e.__class__] += 1 
        print "\nEnter an INTEGER, dirtbag.\n"
    except AssertionError as e:
        idiocy[e.__class__] += 1
        print "\nI said between 1 and 10, moron.\n"
    else:
        if idiocy == 0:
            print "\nDid we finally figure out how to follow instructions?"
            print "Okay, processing..."
        else:
            print "\nOkay, processing..."

>>> idiocy
Counter({AssertionError: 2, ValueError: 3})

And you can access the error counts by key like idiocy[AssertionError]

Cory Madden
  • 5,026
  • 24
  • 37
  • I hadn't seen the `Counter` module before, thank you for sharing. I think I'm better off without it in this script but it'll definitely be useful in the future. – platov Aug 17 '17 at 04:08
1

You have int(evalutation), but you're not assigning it to anything.

Try

try:
    evaluation = int(evaluation)
    assert 0 < evaluation < 10
except ValueError:
    ...
except AssertionError:
MacMcIrish
  • 193
  • 1
  • 7
  • That's the ticket. Seems obvious in retrospect! Thank you. – platov Aug 17 '17 at 04:06
  • 1
    One more thing -- is it bad form to assign `int(evaluation)` to `evaluation`, instead of using a unique variable name? Seems like it could be confusing, unless I've missed something. – platov Aug 17 '17 at 04:27
  • It's a little more verbose, but there's nothing actually wrong with it. If you'd like to cut down on the renaming, you could also have `try: evaluation = int(raw_input('> ') except ValueError: ...` but in my opinion, this gets a little busy for one line :) – MacMcIrish Aug 17 '17 at 04:30
  • Ah, my issue was more along the lines of, might it be confusing to write `evaluation = int(evaluation)` instead of, say, `fixed_evaluation = int(evaluation)`? Or does doing it your way overwrite the `raw_input` that `evaluation` was assigned to earlier? – platov Aug 17 '17 at 04:39
  • I see! There's no issue with re-assigning the `evaluation` variable if it's always your intention to convert it to `int`. – MacMcIrish Aug 17 '17 at 04:48
0

Your range test can be refactored as

assert 1 <= evaluation <= 10

You could keep your insults in a dictionary

insults = {AssertionError : "\nI said between 1 and 10, moron.\n",
           ValueError : "\nEnter an INTEGER, dirtbag.\n"
           }

And write the try/except like this

try:
    ...
except (AssertionError, ValueError) as e:
    print(insults[type(e)])

When you change the user input to an int, you need to assign it to something

evaluation = int(evaluation)

assert was meant for debugging - you are using it incorrectly.

  • You should use TypeError for non-integer repsonses - the type is wrong.
  • You use ValueError for responses outside of a range - the value is wrong
wwii
  • 23,232
  • 7
  • 37
  • 77
  • Your last point was especially helpful, thank you. Changing the error types makes the logic much cleaner -- I'll do that now. – platov Aug 17 '17 at 04:05
0

In your code: int(evaluation) is not typecasting evaluation variable to int type. The output is:

> 2
<type 'str'>
I said between 1 and 10, moron.

Try this:

idiocy = 0

while 1:
    try:
        evaluation = int(raw_input("> "))
        if evaluation < 1 or evaluation > 10:
            raise AssertionError
    except ValueError:
        idiocy += 1
        print "\nEnter an INTEGER, dirtbag.\n"
    except AssertionError:
        idiocy += 1
        print "\nI said between 1 and 10, moron.\n"
    else:
        if idiocy == 0:
            print "\nOkay, processing..."
        else:
            print "\nDid we finally figure out how to follow instructions?"
            print "Okay, processing..."
        break

By the way you can use tuple to store all your exceptions. Example:

idiocy = 0

all_exceptions = (ValueError, AssertionError)
while 1:
    try:
        evaluation = int(raw_input("> "))
        if evaluation < 1 or evaluation > 10:
            raise AssertionError("\nI said between 1 and 10, moron.\n")
    except all_exceptions as e:
        idiocy += 1
        print str(e)
    else:
        if idiocy == 0:
            print "\nOkay, processing..."
        else:
            print "\nDid we finally figure out how to follow instructions?"
            print "Okay, processing..."
        break

Hope it helps.

Prashant Shubham
  • 456
  • 8
  • 20