Cooper
Cooper

Reputation: 45

"if not item in list" not functioning properly in Python?

I have a simple program that checks the user's 'passcode' that either allows them access or denies them access.

If the user types in the correct passcode, the print statement "Login successful! Passcode used:", passcode" is printed, however, if the passcode is wrong, it simply asks for their passcode again without printing the statement "Login unsuccessful."...

Why is this? Thankyou.

LoginCorrect = 0
lst = ['1234', '2345', '3456', '4567', '5678', '6789']
while LoginCorrect == 0:
    passcode = input("Please enter your passcode: ")
    for item in lst:
        if item == passcode:
            print("Login successful! Passcode used:", passcode)
            LoginCorrect = 1
        if not item in lst == False:
            print("Login unsuccessful.")

Upvotes: 0

Views: 157

Answers (5)

Matthias Fripp
Matthias Fripp

Reputation: 18625

To answer your specific question: not item in lst works fine, but not item in lst == False is evaluated differently than you probably expect. Python converts chains of logical tests like (a == b == c) or (a in b in c) to (a == b) and (b == c) or (a in b) and (b in c). It also does this When it sees in and == in the same expression. So not item in lst == False is evaluated as not ((item in lst) and (lst == False)), which is always True. (Without the not, it would always be False, which may be what prompted your original question).

You could fix this by putting parentheses around your first test: (not item in lst) == False (or maybe you mean True?). But a better way to write this test would be if item not in lst:. There are also a couple of other problems with the organization of your code, which I've pointed out in comments below:

LoginCorrect = 0   # probably better to use True/False here
lst = ['1234', '2345', '3456', '4567', '5678', '6789']
while LoginCorrect == 0:
    # The next line will not work right on Python 2.x, which has a different
    # input() function; if your code might run on 2.x you should adjust for that.
    passcode = input("Please enter your passcode: ")
    for item in lst:
        if item == passcode:
            print("Login successful! Passcode used:", passcode)
            LoginCorrect = 1
            # you could use a 'break' here to avoid comparing to more items

        # As noted above, `if item not in lst:` would work better for the next line.
        # This test should also probably occur after the `for` loop instead of 
        # inside it, and act based on `LoginCorrect` instead of `item`. As it is, 
        # you are testing whether `item` is in `lst`, which it always is, and you  
        # are doing this test once for each `item`, which is more checks than needed.

        if not item in lst == False:
            print("Login unsuccessful.")

You mentioned in a comment on a different answer that you have to use a for loop to test the passcode against each item. If that's true, then the revised code below could work well:

try:
    # Python 2 compatibility
    input = raw_input
except NameError:
    pass

LoginCorrect = False
lst = ['1234', '2345', '3456', '4567', '5678', '6789']
while not LoginCorrect:
    passcode = input("Please enter your passcode: ")
    for item in lst:
        if item == passcode:
            print("Login successful! Passcode used:", passcode)
            LoginCorrect = True
            break
    if not LoginCorrect:
        print("Login unsuccessful.")

Or, if you are willing to drop the for loop, you can make your code much simpler:

try:
    # Python 2 compatibility
    input = raw_input
except NameError:
    pass

lst = ['1234', '2345', '3456', '4567', '5678', '6789']
while True:
    passcode = input("Please enter your passcode: ")
    if passcode in lst:
        print("Login successful! Passcode used:", passcode)
        break
    else:
        print("Login unsuccessful.")

Upvotes: 1

PM 2Ring
PM 2Ring

Reputation: 55469

The logic of your code is somewhat unclear. I think you meant to do something like this:

LoginCorrect = 0
lst = ['1234', '2345', '3456', '4567', '5678', '6789']
while LoginCorrect == 0:
    passcode = input("Please enter your passcode: ")
    for item in lst:
        if item == passcode:
            print("Login successful! Passcode used:", passcode)
            LoginCorrect = 1
            break
    if passcode not in lst:
        print("Login unsuccessful.")

BTW, it would be more usual in Python to use the booleans False and True for your LoginCorrect flag, eg

lst = ['1234', '2345', '3456', '4567', '5678', '6789']
LoginCorrect = False
while not LoginCorrect:
    passcode = input("Please enter your passcode: ")
    for item in lst:
        if item == passcode:
            print("Login successful! Passcode used:", passcode)
            LoginCorrect = True
            break
    if passcode not in lst:
        print("Login unsuccessful.")

However, as others have said, it's inefficient to use a Python loop to test if passcode is in lst. An in test does that more efficiently, and it would be even more efficient if lst were instead a set of valid passcodes.

lst = {'1234', '2345', '3456', '4567', '5678', '6789'}
while True:
    passcode = input("Please enter your passcode: ")
    if passcode in lst:
        print("Login successful! Passcode used:", passcode)
        break
    print("Login unsuccessful.")

I have to mention that one of the if statements in your code does not do what you think it does:

if not item in lst == False:

To explain why, I need to take a slight detour. :)

Python syntax supports the chaining of relational operators. This allows you to do things like

if 0 <= a < 5:

or even

if 0 <= a < 5 <= b < 10:

The chained test 0 <= a < 5 is equivalent to (0 <= a) and (a < 5),
and 0 <= a < 5 <= b < 10 is equivalent to

(0 <= a) and (a < 5) and (5 <= b) and (b < 10)

The and operator short-circuits, which means that in

left_expression and right_expression

it only evaluates right_expression if bool(left_expression) is True. So in those chained tests, Python works from left to right, breaking out of the chain if it detects a falsey result at any stage.

Also, in chained tests any of the intermediate expressions are guaranteed to be evaluated at most once. Eg, in f() < g() < h() the function g is called once at most, and it will only be called if f() returns a non-falsey result.

Now, to get back to your code. :)

The in operator is a relational operator, so it can be used in chained tests. So

if item in lst == True:

is equivalent to

if (item in lst) and (lst == True):

and

if not item in lst == False:

is equivalent to

if not ((item in lst) and (lst == False)):

I think you'll agree that's not what you meant to say! :)

And now you should be able to understand why this weird piece of code prints True

ab = ('a', 'b')
print('b' in ab in [('a', 'b'), 'c'])

Upvotes: 1

Rolf of Saxony
Rolf of Saxony

Reputation: 22443

How about a while statement with continue and break?

lst = ['1234', '2345', '3456', '4567', '5678', '6789']

while True:
    passcode = input('Please enter your passcode: ')
    if passcode not in lst:
       continue
    else:
        print("Login successful! Passcode used:", passcode)
        break

You state "if the passcode is wrong, it simply asks for their passcode again without printing the statement "Login unsuccessful."
That being the case, why are you coding for "Login Unsuccessful" ?

Upvotes: 0

Mureinik
Mureinik

Reputation: 311163

You're over complicating things. Instead of iterating over the list yourself, you could just check if the passcode is there:

LoginCorrect = False
lst = ['1234', '2345', '3456', '4567', '5678', '6789']
while LoginCorrect:
    passcode = input("Please enter your passcode: ")
    if passcode in lst:
        print("Login successful! Passcode used:", passcode)
        LoginCorrect = True
    else:
        print("Login unsuccessful.")

Upvotes: 4

Matan Lachmish
Matan Lachmish

Reputation: 1263

I hope that this is just an excersise and not a production code to perform login, because it is super not secured.

Here is a working version of your snippet:

lst = ['1234', '2345', '3456', '4567', '5678', '6789']
LoginCorrect = False
while not LoginCorrect:
    passcode = input("Please enter your passcode: ")
    if passcode in lst:
        print("Login successful! Passcode used:", passcode)
        LoginCorrect = True
    else:
        print("Login unsuccessful.")

Upvotes: 0

Related Questions