The Python Oracle

"not in" identity operator not working when checking empty string for certain characters

--------------------------------------------------
Rise to the top 3% as a developer or hire one of them at Toptal: https://topt.al/25cXVn
--------------------------------------------------

Music by Eric Matyas
https://www.soundimage.org
Track title: Ancient Construction

--

Chapters
00:00 &Quot;Not In&Quot; Identity Operator Not Working When Checking Empty String For Certain Characters
00:45 Accepted Answer Score 6
01:42 Answer 2 Score 6
01:54 Answer 3 Score 2
02:26 Answer 4 Score 2
03:53 Thank you

--

Full question
https://stackoverflow.com/questions/5620...

--

Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...

--

Tags
#python #python3x

#avk47



ACCEPTED ANSWER

Score 6


An empty string is present in any string. Therefore your condition, difficulty not in 'EMH' will evaluate to False when difficulty equals ''; so the while loop's body won't be executed.

In [24]: '' not in 'EMH'                                                                                                                                  
Out[24]: False

In [33]: '' in 'EMH'                                                                                                                                      
Out[33]: True

A better approach might be to convert the string EMH to a list via list('EMH') so that something like EM or EH, or a empty character doesn't break your loop, or avoid it from starting in the first place

Also as @Blckknght suggested, a better alternative is use a default value of None for difficulty.

In [3]: difficulty = None                                                                                                                                

In [4]: while difficulty not in list('EMH'): 
   ...:     print('Enter difficulty: E - Easy, M - Medium, H - Hard') 
   ...:     difficulty = input().upper() 
   ...:                                                                                                                                                   
Enter difficulty: E - Easy, M - Medium, H - Hard
A
Enter difficulty: E - Easy, M - Medium, H - Hard
B
Enter difficulty: E - Easy, M - Medium, H - Hard
C
Enter difficulty: E - Easy, M - Medium, H - Hard
EM
Enter difficulty: E - Easy, M - Medium, H - Hard
E

In [5]:      



ANSWER 2

Score 6


I think what you need to use is a list, instead of a string:

difficulty = ''

while difficulty not in ['E','M','H']:
    print('Enter difficulty: E - Easy, M - Medium, H - Hard')
    difficulty = input().upper()



ANSWER 3

Score 2


This is a good case for a do-while loop condition. But, python doesn't have it. Please check if the below style suits you:

while True:
    print('Enter difficulty: E - Easy, M - Medium, H - Hard')
    difficulty = input().upper()
    if difficulty not in 'EMH': #or in ['E', 'M', 'H']
        continue
    else:
        #do some logic
        break

I do like @Emmet B's suggestion to use ['E', 'M', 'H'] in a loop rather than as a string. It makes sense also as you are expecting one of the character as input and not set of characters.




ANSWER 4

Score 2


If you're inclined to live on the bleeding edge of Python, you might be interested in trying out Python 3.8, which is currently being developed (but alpha releases are available). A somewhat controversial new feature (introduced in PEP 572) is assignment expressions, which let you use := to do an assignment in the middle of another expression.

The loop you're doing could take advantage of such an assignment to do the input and value checking all in one go, without needing to assign a bogus initial value to difficulty before entering the loop.

Note that you probably still want to use a list rather than using not in with a string on the right side, as the latter does a substring check, not a strict sequence membership test. A substring test like difficulty not in 'EMH' will give unintended results for the empty string or the multi-letter inputs 'EM', 'MH' and 'EMH'. If you might use your different difficulty strings as dictionary keys somewhere (e.g. to look up stats that vary with the difficulty setting), you might test if the input is a key in the dictionary directly, rather than building a separate list.

prompt = 'Enter difficulty: E - Easy, M - Medium, H - Hard\n'
while (difficulty := input(prompt).upper()) not in ['E', 'M', 'H']:
    pass  # or maybe: print("I'm sorry, I didn't understand that.")

# you can use `difficulty` down here

Note that while you can use assignment expressions here, it's not necessarily a good idea to do so, just to avoid initializing a variable. You end up bundling up so many different things into a single line (the while, the input and upper calls, the assignment with :=, and the not in test) that it can be easy to get confused about what is going on. Indeed, in my example above, I ended up creating a different variable (prompt) because the line would have been too long otherwise.