I am writing a security system that denies access to unauthorized users.
name = input("Hello. Please enter your name: ")
if name == "Kevin" or "Jon" or "Inbar":
print("Access granted.")
else:
print("Access denied.")
It grants access to authorized users as expected, but it also lets in unauthorized users!
Hello. Please enter your name: Bob
Access granted.
Why does this occur? I've plainly stated to only grant access when name equals Kevin, Jon, or Inbar. I have also tried the opposite logic, if "Kevin" or "Jon" or "Inbar" == name, but the result is the same.
This question is intended as the canonical duplicate target of this very common problem. There is another popular question How to test multiple variables for equality against a single value? that has the same fundamental problem, but the comparison targets are reversed. This question should not be closed as a duplicate of that one as this problem is encountered by newcomers to Python who might have difficulties applying the knowledge from the reversed question to their problem.
In many cases, Python looks and behaves like natural English, but this is one case where that abstraction fails. People can use context clues to determine that "Jon" and "Inbar" are objects joined to the verb "equals", but the Python interpreter is more literal minded.
if name == "Kevin" or "Jon" or "Inbar":
is logically equivalent to:
if (name == "Kevin") or ("Jon") or ("Inbar"):
Which, for user Bob, is equivalent to:
if (False) or ("Jon") or ("Inbar"):
The or operator chooses the first operand that is "truthy", i.e. which would satisfy an if condition (or the last one, if none of them are "truthy"):
if "Jon":
Since "Jon" is truthy, the if block executes. That is what causes "Access granted" to be printed regardless of the name given.
All of this reasoning also applies to the expression if "Kevin" or "Jon" or "Inbar" == name. the first value, "Kevin", is true, so the if block executes.
There are two common ways to properly construct this conditional.
Use multiple == operators to explicitly check against each value:
if name == "Kevin" or name == "Jon" or name == "Inbar":
Compose a collection of valid values (a set, a list or a tuple for example), and use the in operator to test for membership:
if name in {"Kevin", "Jon", "Inbar"}:
In general of the two the second should be preferred as it's easier to read and also faster:
>>> import timeit
>>> timeit.timeit('name == "Kevin" or name == "Jon" or name == "Inbar"',
setup="name='Inbar'")
0.4247764749999945
>>> timeit.timeit('name in {"Kevin", "Jon", "Inbar"}', setup="name='Inbar'")
0.18493307199999265
For those who may want proof that if a == b or c or d or e: ... is indeed parsed like this. The built-in ast module provides an answer:
>>> import ast
>>> ast.parse("a == b or c or d or e", "<string>", "eval")
<ast.Expression object at 0x7f929c898220>
>>> print(ast.dump(_, indent=4))
Expression(
body=BoolOp(
op=Or(),
values=[
Compare(
left=Name(id='a', ctx=Load()),
ops=[
Eq()],
comparators=[
Name(id='b', ctx=Load())]),
Name(id='c', ctx=Load()),
Name(id='d', ctx=Load()),
Name(id='e', ctx=Load())]))
As one can see, it's the boolean operator or applied to four sub-expressions: comparison a == b; and simple expressions c, d, and e.
Summarising all existing answers
(And adding a few of my points)
Explanation :
if name == "Kevin" or "Jon" or "Inbar":
is logically equivalent to:
if (name == "Kevin") or ("Jon") or ("Inbar"):
Which, for user Bob, is equivalent to:
if (False) or ("Jon") or ("Inbar"):
NOTE : Python evaluates the logical value of any non-zero integer as True. Therefore, all Non-empty lists, sets, strings, etc. are evaluable and return True
The or operator chooses the first argument with a positive truth value.
Therefore, "Jon" has a positive truth value and the if block executes, since it is now equivalent to
if (False) or (True) or (True):
That is what causes "Access granted" to be printed regardless of the name input.
Solutions :
Solution 1 : Use multiple == operators to explicitly check against each value
if name == "Kevin" or name == "Jon" or name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
Solution 2 : Compose a collection of valid values (a set, a list or a tuple for example), and use the in operator to test for membership (faster, preferred method)
if name in {"Kevin", "Jon", "Inbar"}:
print("Access granted.")
else:
print("Access denied.")
OR
if name in ["Kevin", "Jon", "Inbar"]:
print("Access granted.")
else:
print("Access denied.")
Solution 3 : Use the basic (and not very efficient) if-elif-else structure
if name == "Kevin":
print("Access granted.")
elif name == "Jon":
print("Access granted.")
elif name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
There are 3 condition checks in if name == "Kevin" or "Jon" or "Inbar":
name == "Kevin"
"Jon"
"Inbar"
and this if statement is equivalent to
if name == "Kevin":
print("Access granted.")
elif "Jon":
print("Access granted.")
elif "Inbar":
print("Access granted.")
else:
print("Access denied.")
Since elif "Jon" will always be true so access to any user is granted
Solution
You can use any one method below
Fast
if name in ["Kevin", "Jon", "Inbar"]:
print("Access granted.")
else:
print("Access denied.")
Slow
if name == "Kevin" or name == "Jon" or name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
Slow + Unnecessary code
if name == "Kevin":
print("Access granted.")
elif name == "Jon":
print("Access granted.")
elif name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
Not-empty lists, sets, strings, etc. are evaluable and, therefore, return True.
Therefore, when you say:
a = "Raul"
if a == "Kevin" or "John" or "Inbar":
pass
You are actually saying:
if "Raul" == "Kevin" or "John" != "" or "Inbar" != "":
pass
Since at least one of "John" and "Inbar" is not an empty string, the whole expression always returns True!
The solution:
a = "Raul"
if a == "Kevin" or a == "John" or a == "Inbar":
pass
or:
a = "Raul"
if a in {"Kevin", "John", "Inbar"}:
pass
Simple engineering problem, let's simply it a bit further.
In [1]: a,b,c,d=1,2,3,4
In [2]: a==b
Out[2]: False
But, inherited from the language C, Python evaluates the logical value of a non zero integer as True.
In [11]: if 3:
...: print ("yey")
...:
yey
Now, Python builds on that logic and let you use logic literals such as or on integers, and so
In [9]: False or 3
Out[9]: 3
Finally
In [4]: a==b or c or d
Out[4]: 3
The proper way to write it would be:
In [13]: if a in (b,c,d):
...: print('Access granted')
For safety I'd also suggest you don't hard code passwords.
Using match/case in Python 3.10 and above
Python 3.10 adds a new syntax to the language. It's officially described as "structural pattern matching", but most people call it according to the syntax: "match/case".
Technical specification
Motivation and rationale (i.e., why it was added, and what inspired the design)
Official tutorial
We can use this special syntax for an example like in the question, by making one "case" that matches all the accepted usernames, and using the "wildcard" case _ in place of the else. Thus:
name = input("Hello. Please enter your name: ")
match name:
case "Kevin" | "Jon" | "Inbar":
print("Access granted.")
case _:
print("Access denied.")
Note that cases are "combined" using |, not or. This is a special syntax: Python does not try to compute "Kevin" | "Jon" | "Inbar" first (| doesn't work with strings), but instead interprets the entire line differently because it starts with case.
Besides some other rather rarer useful cases for the walrus operator already mentioned. This also tend to be a useful case as well.
def calc_value():
return 43
if (v := calc_value()) == 43 and v > 42:
print('happy short, efficient and readable code')
This works because each part of the if-statement is read separately. So (v := calc_value()) is executed and a value is assigned to v and if the first fails, you still have v in the namespace for different conditions or calculations.
Approaches
How a data scientist approaches this problem
The simplest way possible is to eliminate the need for comparison operators and use a list. This looks impressive on security systems because you learn to access ORMs.
user = input("Enter name: ")
if user in {"Bob", "Kevin", "Joe"}:
print("Access granted, " + str(user) + ".")
else:
print("Access denied.")
Or, you can resemble the exact same code above, just put the list of registered users in their own list:
user = input("Enter name: ")
users = {"Bob", "Kevin", "Joe", "a million more users if you like"}
if user in users:
print("Access granted, " + str(user) + ".")
else:
print("Access denied.")
If you wanted to complete this protocol safely without the risk of attack, set up double parameters. This would check your mini-ORM for first and last name fields, as well as a password or secret question key. Objects can be sorted like this if you want to efficiently lazy-load user credentials without hashing:
def lazy(i):
j = 0 # For example
while j < i:
yield j
j += 1
The loop will consume only the yielded values to save time and energy on your system:
You can then do something with the iterated list:
for j in lazy_range(10):
do_something_here(j)
This problem can be approached from any angle: memory management, security, or simply by an organic list or packaged ORM.
Related
I am writing a security system that denies access to unauthorized users.
name = input("Hello. Please enter your name: ")
if name == "Kevin" or "Jon" or "Inbar":
print("Access granted.")
else:
print("Access denied.")
It grants access to authorized users as expected, but it also lets in unauthorized users!
Hello. Please enter your name: Bob
Access granted.
Why does this occur? I've plainly stated to only grant access when name equals Kevin, Jon, or Inbar. I have also tried the opposite logic, if "Kevin" or "Jon" or "Inbar" == name, but the result is the same.
This question is intended as the canonical duplicate target of this very common problem. There is another popular question How to test multiple variables for equality against a single value? that has the same fundamental problem, but the comparison targets are reversed. This question should not be closed as a duplicate of that one as this problem is encountered by newcomers to Python who might have difficulties applying the knowledge from the reversed question to their problem.
In many cases, Python looks and behaves like natural English, but this is one case where that abstraction fails. People can use context clues to determine that "Jon" and "Inbar" are objects joined to the verb "equals", but the Python interpreter is more literal minded.
if name == "Kevin" or "Jon" or "Inbar":
is logically equivalent to:
if (name == "Kevin") or ("Jon") or ("Inbar"):
Which, for user Bob, is equivalent to:
if (False) or ("Jon") or ("Inbar"):
The or operator chooses the first operand that is "truthy", i.e. which would satisfy an if condition (or the last one, if none of them are "truthy"):
if "Jon":
Since "Jon" is truthy, the if block executes. That is what causes "Access granted" to be printed regardless of the name given.
All of this reasoning also applies to the expression if "Kevin" or "Jon" or "Inbar" == name. the first value, "Kevin", is true, so the if block executes.
There are two common ways to properly construct this conditional.
Use multiple == operators to explicitly check against each value:
if name == "Kevin" or name == "Jon" or name == "Inbar":
Compose a collection of valid values (a set, a list or a tuple for example), and use the in operator to test for membership:
if name in {"Kevin", "Jon", "Inbar"}:
In general of the two the second should be preferred as it's easier to read and also faster:
>>> import timeit
>>> timeit.timeit('name == "Kevin" or name == "Jon" or name == "Inbar"',
setup="name='Inbar'")
0.4247764749999945
>>> timeit.timeit('name in {"Kevin", "Jon", "Inbar"}', setup="name='Inbar'")
0.18493307199999265
For those who may want proof that if a == b or c or d or e: ... is indeed parsed like this. The built-in ast module provides an answer:
>>> import ast
>>> ast.parse("a == b or c or d or e", "<string>", "eval")
<ast.Expression object at 0x7f929c898220>
>>> print(ast.dump(_, indent=4))
Expression(
body=BoolOp(
op=Or(),
values=[
Compare(
left=Name(id='a', ctx=Load()),
ops=[
Eq()],
comparators=[
Name(id='b', ctx=Load())]),
Name(id='c', ctx=Load()),
Name(id='d', ctx=Load()),
Name(id='e', ctx=Load())]))
As one can see, it's the boolean operator or applied to four sub-expressions: comparison a == b; and simple expressions c, d, and e.
Summarising all existing answers
(And adding a few of my points)
Explanation :
if name == "Kevin" or "Jon" or "Inbar":
is logically equivalent to:
if (name == "Kevin") or ("Jon") or ("Inbar"):
Which, for user Bob, is equivalent to:
if (False) or ("Jon") or ("Inbar"):
NOTE : Python evaluates the logical value of any non-zero integer as True. Therefore, all Non-empty lists, sets, strings, etc. are evaluable and return True
The or operator chooses the first argument with a positive truth value.
Therefore, "Jon" has a positive truth value and the if block executes, since it is now equivalent to
if (False) or (True) or (True):
That is what causes "Access granted" to be printed regardless of the name input.
Solutions :
Solution 1 : Use multiple == operators to explicitly check against each value
if name == "Kevin" or name == "Jon" or name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
Solution 2 : Compose a collection of valid values (a set, a list or a tuple for example), and use the in operator to test for membership (faster, preferred method)
if name in {"Kevin", "Jon", "Inbar"}:
print("Access granted.")
else:
print("Access denied.")
OR
if name in ["Kevin", "Jon", "Inbar"]:
print("Access granted.")
else:
print("Access denied.")
Solution 3 : Use the basic (and not very efficient) if-elif-else structure
if name == "Kevin":
print("Access granted.")
elif name == "Jon":
print("Access granted.")
elif name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
There are 3 condition checks in if name == "Kevin" or "Jon" or "Inbar":
name == "Kevin"
"Jon"
"Inbar"
and this if statement is equivalent to
if name == "Kevin":
print("Access granted.")
elif "Jon":
print("Access granted.")
elif "Inbar":
print("Access granted.")
else:
print("Access denied.")
Since elif "Jon" will always be true so access to any user is granted
Solution
You can use any one method below
Fast
if name in ["Kevin", "Jon", "Inbar"]:
print("Access granted.")
else:
print("Access denied.")
Slow
if name == "Kevin" or name == "Jon" or name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
Slow + Unnecessary code
if name == "Kevin":
print("Access granted.")
elif name == "Jon":
print("Access granted.")
elif name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
Not-empty lists, sets, strings, etc. are evaluable and, therefore, return True.
Therefore, when you say:
a = "Raul"
if a == "Kevin" or "John" or "Inbar":
pass
You are actually saying:
if "Raul" == "Kevin" or "John" != "" or "Inbar" != "":
pass
Since at least one of "John" and "Inbar" is not an empty string, the whole expression always returns True!
The solution:
a = "Raul"
if a == "Kevin" or a == "John" or a == "Inbar":
pass
or:
a = "Raul"
if a in {"Kevin", "John", "Inbar"}:
pass
Simple engineering problem, let's simply it a bit further.
In [1]: a,b,c,d=1,2,3,4
In [2]: a==b
Out[2]: False
But, inherited from the language C, Python evaluates the logical value of a non zero integer as True.
In [11]: if 3:
...: print ("yey")
...:
yey
Now, Python builds on that logic and let you use logic literals such as or on integers, and so
In [9]: False or 3
Out[9]: 3
Finally
In [4]: a==b or c or d
Out[4]: 3
The proper way to write it would be:
In [13]: if a in (b,c,d):
...: print('Access granted')
For safety I'd also suggest you don't hard code passwords.
Using match/case in Python 3.10 and above
Python 3.10 adds a new syntax to the language. It's officially described as "structural pattern matching", but most people call it according to the syntax: "match/case".
Technical specification
Motivation and rationale (i.e., why it was added, and what inspired the design)
Official tutorial
We can use this special syntax for an example like in the question, by making one "case" that matches all the accepted usernames, and using the "wildcard" case _ in place of the else. Thus:
name = input("Hello. Please enter your name: ")
match name:
case "Kevin" | "Jon" | "Inbar":
print("Access granted.")
case _:
print("Access denied.")
Note that cases are "combined" using |, not or. This is a special syntax: Python does not try to compute "Kevin" | "Jon" | "Inbar" first (| doesn't work with strings), but instead interprets the entire line differently because it starts with case.
Besides some other rather rarer useful cases for the walrus operator already mentioned. This also tend to be a useful case as well.
def calc_value():
return 43
if (v := calc_value()) == 43 and v > 42:
print('happy short, efficient and readable code')
This works because each part of the if-statement is read separately. So (v := calc_value()) is executed and a value is assigned to v and if the first fails, you still have v in the namespace for different conditions or calculations.
Approaches
How a data scientist approaches this problem
The simplest way possible is to eliminate the need for comparison operators and use a list. This looks impressive on security systems because you learn to access ORMs.
user = input("Enter name: ")
if user in {"Bob", "Kevin", "Joe"}:
print("Access granted, " + str(user) + ".")
else:
print("Access denied.")
Or, you can resemble the exact same code above, just put the list of registered users in their own list:
user = input("Enter name: ")
users = {"Bob", "Kevin", "Joe", "a million more users if you like"}
if user in users:
print("Access granted, " + str(user) + ".")
else:
print("Access denied.")
If you wanted to complete this protocol safely without the risk of attack, set up double parameters. This would check your mini-ORM for first and last name fields, as well as a password or secret question key. Objects can be sorted like this if you want to efficiently lazy-load user credentials without hashing:
def lazy(i):
j = 0 # For example
while j < i:
yield j
j += 1
The loop will consume only the yielded values to save time and energy on your system:
You can then do something with the iterated list:
for j in lazy_range(10):
do_something_here(j)
This problem can be approached from any angle: memory management, security, or simply by an organic list or packaged ORM.
I am writing a security system that denies access to unauthorized users.
name = input("Hello. Please enter your name: ")
if name == "Kevin" or "Jon" or "Inbar":
print("Access granted.")
else:
print("Access denied.")
It grants access to authorized users as expected, but it also lets in unauthorized users!
Hello. Please enter your name: Bob
Access granted.
Why does this occur? I've plainly stated to only grant access when name equals Kevin, Jon, or Inbar. I have also tried the opposite logic, if "Kevin" or "Jon" or "Inbar" == name, but the result is the same.
This question is intended as the canonical duplicate target of this very common problem. There is another popular question How to test multiple variables for equality against a single value? that has the same fundamental problem, but the comparison targets are reversed. This question should not be closed as a duplicate of that one as this problem is encountered by newcomers to Python who might have difficulties applying the knowledge from the reversed question to their problem.
In many cases, Python looks and behaves like natural English, but this is one case where that abstraction fails. People can use context clues to determine that "Jon" and "Inbar" are objects joined to the verb "equals", but the Python interpreter is more literal minded.
if name == "Kevin" or "Jon" or "Inbar":
is logically equivalent to:
if (name == "Kevin") or ("Jon") or ("Inbar"):
Which, for user Bob, is equivalent to:
if (False) or ("Jon") or ("Inbar"):
The or operator chooses the first operand that is "truthy", i.e. which would satisfy an if condition (or the last one, if none of them are "truthy"):
if "Jon":
Since "Jon" is truthy, the if block executes. That is what causes "Access granted" to be printed regardless of the name given.
All of this reasoning also applies to the expression if "Kevin" or "Jon" or "Inbar" == name. the first value, "Kevin", is true, so the if block executes.
There are two common ways to properly construct this conditional.
Use multiple == operators to explicitly check against each value:
if name == "Kevin" or name == "Jon" or name == "Inbar":
Compose a collection of valid values (a set, a list or a tuple for example), and use the in operator to test for membership:
if name in {"Kevin", "Jon", "Inbar"}:
In general of the two the second should be preferred as it's easier to read and also faster:
>>> import timeit
>>> timeit.timeit('name == "Kevin" or name == "Jon" or name == "Inbar"',
setup="name='Inbar'")
0.4247764749999945
>>> timeit.timeit('name in {"Kevin", "Jon", "Inbar"}', setup="name='Inbar'")
0.18493307199999265
For those who may want proof that if a == b or c or d or e: ... is indeed parsed like this. The built-in ast module provides an answer:
>>> import ast
>>> ast.parse("a == b or c or d or e", "<string>", "eval")
<ast.Expression object at 0x7f929c898220>
>>> print(ast.dump(_, indent=4))
Expression(
body=BoolOp(
op=Or(),
values=[
Compare(
left=Name(id='a', ctx=Load()),
ops=[
Eq()],
comparators=[
Name(id='b', ctx=Load())]),
Name(id='c', ctx=Load()),
Name(id='d', ctx=Load()),
Name(id='e', ctx=Load())]))
As one can see, it's the boolean operator or applied to four sub-expressions: comparison a == b; and simple expressions c, d, and e.
Summarising all existing answers
(And adding a few of my points)
Explanation :
if name == "Kevin" or "Jon" or "Inbar":
is logically equivalent to:
if (name == "Kevin") or ("Jon") or ("Inbar"):
Which, for user Bob, is equivalent to:
if (False) or ("Jon") or ("Inbar"):
NOTE : Python evaluates the logical value of any non-zero integer as True. Therefore, all Non-empty lists, sets, strings, etc. are evaluable and return True
The or operator chooses the first argument with a positive truth value.
Therefore, "Jon" has a positive truth value and the if block executes, since it is now equivalent to
if (False) or (True) or (True):
That is what causes "Access granted" to be printed regardless of the name input.
Solutions :
Solution 1 : Use multiple == operators to explicitly check against each value
if name == "Kevin" or name == "Jon" or name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
Solution 2 : Compose a collection of valid values (a set, a list or a tuple for example), and use the in operator to test for membership (faster, preferred method)
if name in {"Kevin", "Jon", "Inbar"}:
print("Access granted.")
else:
print("Access denied.")
OR
if name in ["Kevin", "Jon", "Inbar"]:
print("Access granted.")
else:
print("Access denied.")
Solution 3 : Use the basic (and not very efficient) if-elif-else structure
if name == "Kevin":
print("Access granted.")
elif name == "Jon":
print("Access granted.")
elif name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
There are 3 condition checks in if name == "Kevin" or "Jon" or "Inbar":
name == "Kevin"
"Jon"
"Inbar"
and this if statement is equivalent to
if name == "Kevin":
print("Access granted.")
elif "Jon":
print("Access granted.")
elif "Inbar":
print("Access granted.")
else:
print("Access denied.")
Since elif "Jon" will always be true so access to any user is granted
Solution
You can use any one method below
Fast
if name in ["Kevin", "Jon", "Inbar"]:
print("Access granted.")
else:
print("Access denied.")
Slow
if name == "Kevin" or name == "Jon" or name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
Slow + Unnecessary code
if name == "Kevin":
print("Access granted.")
elif name == "Jon":
print("Access granted.")
elif name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
Not-empty lists, sets, strings, etc. are evaluable and, therefore, return True.
Therefore, when you say:
a = "Raul"
if a == "Kevin" or "John" or "Inbar":
pass
You are actually saying:
if "Raul" == "Kevin" or "John" != "" or "Inbar" != "":
pass
Since at least one of "John" and "Inbar" is not an empty string, the whole expression always returns True!
The solution:
a = "Raul"
if a == "Kevin" or a == "John" or a == "Inbar":
pass
or:
a = "Raul"
if a in {"Kevin", "John", "Inbar"}:
pass
Simple engineering problem, let's simply it a bit further.
In [1]: a,b,c,d=1,2,3,4
In [2]: a==b
Out[2]: False
But, inherited from the language C, Python evaluates the logical value of a non zero integer as True.
In [11]: if 3:
...: print ("yey")
...:
yey
Now, Python builds on that logic and let you use logic literals such as or on integers, and so
In [9]: False or 3
Out[9]: 3
Finally
In [4]: a==b or c or d
Out[4]: 3
The proper way to write it would be:
In [13]: if a in (b,c,d):
...: print('Access granted')
For safety I'd also suggest you don't hard code passwords.
Using match/case in Python 3.10 and above
Python 3.10 adds a new syntax to the language. It's officially described as "structural pattern matching", but most people call it according to the syntax: "match/case".
Technical specification
Motivation and rationale (i.e., why it was added, and what inspired the design)
Official tutorial
We can use this special syntax for an example like in the question, by making one "case" that matches all the accepted usernames, and using the "wildcard" case _ in place of the else. Thus:
name = input("Hello. Please enter your name: ")
match name:
case "Kevin" | "Jon" | "Inbar":
print("Access granted.")
case _:
print("Access denied.")
Note that cases are "combined" using |, not or. This is a special syntax: Python does not try to compute "Kevin" | "Jon" | "Inbar" first (| doesn't work with strings), but instead interprets the entire line differently because it starts with case.
Besides some other rather rarer useful cases for the walrus operator already mentioned. This also tend to be a useful case as well.
def calc_value():
return 43
if (v := calc_value()) == 43 and v > 42:
print('happy short, efficient and readable code')
This works because each part of the if-statement is read separately. So (v := calc_value()) is executed and a value is assigned to v and if the first fails, you still have v in the namespace for different conditions or calculations.
Approaches
How a data scientist approaches this problem
The simplest way possible is to eliminate the need for comparison operators and use a list. This looks impressive on security systems because you learn to access ORMs.
user = input("Enter name: ")
if user in {"Bob", "Kevin", "Joe"}:
print("Access granted, " + str(user) + ".")
else:
print("Access denied.")
Or, you can resemble the exact same code above, just put the list of registered users in their own list:
user = input("Enter name: ")
users = {"Bob", "Kevin", "Joe", "a million more users if you like"}
if user in users:
print("Access granted, " + str(user) + ".")
else:
print("Access denied.")
If you wanted to complete this protocol safely without the risk of attack, set up double parameters. This would check your mini-ORM for first and last name fields, as well as a password or secret question key. Objects can be sorted like this if you want to efficiently lazy-load user credentials without hashing:
def lazy(i):
j = 0 # For example
while j < i:
yield j
j += 1
The loop will consume only the yielded values to save time and energy on your system:
You can then do something with the iterated list:
for j in lazy_range(10):
do_something_here(j)
This problem can be approached from any angle: memory management, security, or simply by an organic list or packaged ORM.
I am writing a security system that denies access to unauthorized users.
name = input("Hello. Please enter your name: ")
if name == "Kevin" or "Jon" or "Inbar":
print("Access granted.")
else:
print("Access denied.")
It grants access to authorized users as expected, but it also lets in unauthorized users!
Hello. Please enter your name: Bob
Access granted.
Why does this occur? I've plainly stated to only grant access when name equals Kevin, Jon, or Inbar. I have also tried the opposite logic, if "Kevin" or "Jon" or "Inbar" == name, but the result is the same.
This question is intended as the canonical duplicate target of this very common problem. There is another popular question How to test multiple variables for equality against a single value? that has the same fundamental problem, but the comparison targets are reversed. This question should not be closed as a duplicate of that one as this problem is encountered by newcomers to Python who might have difficulties applying the knowledge from the reversed question to their problem.
In many cases, Python looks and behaves like natural English, but this is one case where that abstraction fails. People can use context clues to determine that "Jon" and "Inbar" are objects joined to the verb "equals", but the Python interpreter is more literal minded.
if name == "Kevin" or "Jon" or "Inbar":
is logically equivalent to:
if (name == "Kevin") or ("Jon") or ("Inbar"):
Which, for user Bob, is equivalent to:
if (False) or ("Jon") or ("Inbar"):
The or operator chooses the first operand that is "truthy", i.e. which would satisfy an if condition (or the last one, if none of them are "truthy"):
if "Jon":
Since "Jon" is truthy, the if block executes. That is what causes "Access granted" to be printed regardless of the name given.
All of this reasoning also applies to the expression if "Kevin" or "Jon" or "Inbar" == name. the first value, "Kevin", is true, so the if block executes.
There are two common ways to properly construct this conditional.
Use multiple == operators to explicitly check against each value:
if name == "Kevin" or name == "Jon" or name == "Inbar":
Compose a collection of valid values (a set, a list or a tuple for example), and use the in operator to test for membership:
if name in {"Kevin", "Jon", "Inbar"}:
In general of the two the second should be preferred as it's easier to read and also faster:
>>> import timeit
>>> timeit.timeit('name == "Kevin" or name == "Jon" or name == "Inbar"',
setup="name='Inbar'")
0.4247764749999945
>>> timeit.timeit('name in {"Kevin", "Jon", "Inbar"}', setup="name='Inbar'")
0.18493307199999265
For those who may want proof that if a == b or c or d or e: ... is indeed parsed like this. The built-in ast module provides an answer:
>>> import ast
>>> ast.parse("a == b or c or d or e", "<string>", "eval")
<ast.Expression object at 0x7f929c898220>
>>> print(ast.dump(_, indent=4))
Expression(
body=BoolOp(
op=Or(),
values=[
Compare(
left=Name(id='a', ctx=Load()),
ops=[
Eq()],
comparators=[
Name(id='b', ctx=Load())]),
Name(id='c', ctx=Load()),
Name(id='d', ctx=Load()),
Name(id='e', ctx=Load())]))
As one can see, it's the boolean operator or applied to four sub-expressions: comparison a == b; and simple expressions c, d, and e.
Summarising all existing answers
(And adding a few of my points)
Explanation :
if name == "Kevin" or "Jon" or "Inbar":
is logically equivalent to:
if (name == "Kevin") or ("Jon") or ("Inbar"):
Which, for user Bob, is equivalent to:
if (False) or ("Jon") or ("Inbar"):
NOTE : Python evaluates the logical value of any non-zero integer as True. Therefore, all Non-empty lists, sets, strings, etc. are evaluable and return True
The or operator chooses the first argument with a positive truth value.
Therefore, "Jon" has a positive truth value and the if block executes, since it is now equivalent to
if (False) or (True) or (True):
That is what causes "Access granted" to be printed regardless of the name input.
Solutions :
Solution 1 : Use multiple == operators to explicitly check against each value
if name == "Kevin" or name == "Jon" or name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
Solution 2 : Compose a collection of valid values (a set, a list or a tuple for example), and use the in operator to test for membership (faster, preferred method)
if name in {"Kevin", "Jon", "Inbar"}:
print("Access granted.")
else:
print("Access denied.")
OR
if name in ["Kevin", "Jon", "Inbar"]:
print("Access granted.")
else:
print("Access denied.")
Solution 3 : Use the basic (and not very efficient) if-elif-else structure
if name == "Kevin":
print("Access granted.")
elif name == "Jon":
print("Access granted.")
elif name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
There are 3 condition checks in if name == "Kevin" or "Jon" or "Inbar":
name == "Kevin"
"Jon"
"Inbar"
and this if statement is equivalent to
if name == "Kevin":
print("Access granted.")
elif "Jon":
print("Access granted.")
elif "Inbar":
print("Access granted.")
else:
print("Access denied.")
Since elif "Jon" will always be true so access to any user is granted
Solution
You can use any one method below
Fast
if name in ["Kevin", "Jon", "Inbar"]:
print("Access granted.")
else:
print("Access denied.")
Slow
if name == "Kevin" or name == "Jon" or name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
Slow + Unnecessary code
if name == "Kevin":
print("Access granted.")
elif name == "Jon":
print("Access granted.")
elif name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
Not-empty lists, sets, strings, etc. are evaluable and, therefore, return True.
Therefore, when you say:
a = "Raul"
if a == "Kevin" or "John" or "Inbar":
pass
You are actually saying:
if "Raul" == "Kevin" or "John" != "" or "Inbar" != "":
pass
Since at least one of "John" and "Inbar" is not an empty string, the whole expression always returns True!
The solution:
a = "Raul"
if a == "Kevin" or a == "John" or a == "Inbar":
pass
or:
a = "Raul"
if a in {"Kevin", "John", "Inbar"}:
pass
Simple engineering problem, let's simply it a bit further.
In [1]: a,b,c,d=1,2,3,4
In [2]: a==b
Out[2]: False
But, inherited from the language C, Python evaluates the logical value of a non zero integer as True.
In [11]: if 3:
...: print ("yey")
...:
yey
Now, Python builds on that logic and let you use logic literals such as or on integers, and so
In [9]: False or 3
Out[9]: 3
Finally
In [4]: a==b or c or d
Out[4]: 3
The proper way to write it would be:
In [13]: if a in (b,c,d):
...: print('Access granted')
For safety I'd also suggest you don't hard code passwords.
Using match/case in Python 3.10 and above
Python 3.10 adds a new syntax to the language. It's officially described as "structural pattern matching", but most people call it according to the syntax: "match/case".
Technical specification
Motivation and rationale (i.e., why it was added, and what inspired the design)
Official tutorial
We can use this special syntax for an example like in the question, by making one "case" that matches all the accepted usernames, and using the "wildcard" case _ in place of the else. Thus:
name = input("Hello. Please enter your name: ")
match name:
case "Kevin" | "Jon" | "Inbar":
print("Access granted.")
case _:
print("Access denied.")
Note that cases are "combined" using |, not or. This is a special syntax: Python does not try to compute "Kevin" | "Jon" | "Inbar" first (| doesn't work with strings), but instead interprets the entire line differently because it starts with case.
Besides some other rather rarer useful cases for the walrus operator already mentioned. This also tend to be a useful case as well.
def calc_value():
return 43
if (v := calc_value()) == 43 and v > 42:
print('happy short, efficient and readable code')
This works because each part of the if-statement is read separately. So (v := calc_value()) is executed and a value is assigned to v and if the first fails, you still have v in the namespace for different conditions or calculations.
Approaches
How a data scientist approaches this problem
The simplest way possible is to eliminate the need for comparison operators and use a list. This looks impressive on security systems because you learn to access ORMs.
user = input("Enter name: ")
if user in {"Bob", "Kevin", "Joe"}:
print("Access granted, " + str(user) + ".")
else:
print("Access denied.")
Or, you can resemble the exact same code above, just put the list of registered users in their own list:
user = input("Enter name: ")
users = {"Bob", "Kevin", "Joe", "a million more users if you like"}
if user in users:
print("Access granted, " + str(user) + ".")
else:
print("Access denied.")
If you wanted to complete this protocol safely without the risk of attack, set up double parameters. This would check your mini-ORM for first and last name fields, as well as a password or secret question key. Objects can be sorted like this if you want to efficiently lazy-load user credentials without hashing:
def lazy(i):
j = 0 # For example
while j < i:
yield j
j += 1
The loop will consume only the yielded values to save time and energy on your system:
You can then do something with the iterated list:
for j in lazy_range(10):
do_something_here(j)
This problem can be approached from any angle: memory management, security, or simply by an organic list or packaged ORM.
I am writing a security system that denies access to unauthorized users.
name = input("Hello. Please enter your name: ")
if name == "Kevin" or "Jon" or "Inbar":
print("Access granted.")
else:
print("Access denied.")
It grants access to authorized users as expected, but it also lets in unauthorized users!
Hello. Please enter your name: Bob
Access granted.
Why does this occur? I've plainly stated to only grant access when name equals Kevin, Jon, or Inbar. I have also tried the opposite logic, if "Kevin" or "Jon" or "Inbar" == name, but the result is the same.
This question is intended as the canonical duplicate target of this very common problem. There is another popular question How to test multiple variables for equality against a single value? that has the same fundamental problem, but the comparison targets are reversed. This question should not be closed as a duplicate of that one as this problem is encountered by newcomers to Python who might have difficulties applying the knowledge from the reversed question to their problem.
In many cases, Python looks and behaves like natural English, but this is one case where that abstraction fails. People can use context clues to determine that "Jon" and "Inbar" are objects joined to the verb "equals", but the Python interpreter is more literal minded.
if name == "Kevin" or "Jon" or "Inbar":
is logically equivalent to:
if (name == "Kevin") or ("Jon") or ("Inbar"):
Which, for user Bob, is equivalent to:
if (False) or ("Jon") or ("Inbar"):
The or operator chooses the first operand that is "truthy", i.e. which would satisfy an if condition (or the last one, if none of them are "truthy"):
if "Jon":
Since "Jon" is truthy, the if block executes. That is what causes "Access granted" to be printed regardless of the name given.
All of this reasoning also applies to the expression if "Kevin" or "Jon" or "Inbar" == name. the first value, "Kevin", is true, so the if block executes.
There are two common ways to properly construct this conditional.
Use multiple == operators to explicitly check against each value:
if name == "Kevin" or name == "Jon" or name == "Inbar":
Compose a collection of valid values (a set, a list or a tuple for example), and use the in operator to test for membership:
if name in {"Kevin", "Jon", "Inbar"}:
In general of the two the second should be preferred as it's easier to read and also faster:
>>> import timeit
>>> timeit.timeit('name == "Kevin" or name == "Jon" or name == "Inbar"',
setup="name='Inbar'")
0.4247764749999945
>>> timeit.timeit('name in {"Kevin", "Jon", "Inbar"}', setup="name='Inbar'")
0.18493307199999265
For those who may want proof that if a == b or c or d or e: ... is indeed parsed like this. The built-in ast module provides an answer:
>>> import ast
>>> ast.parse("a == b or c or d or e", "<string>", "eval")
<ast.Expression object at 0x7f929c898220>
>>> print(ast.dump(_, indent=4))
Expression(
body=BoolOp(
op=Or(),
values=[
Compare(
left=Name(id='a', ctx=Load()),
ops=[
Eq()],
comparators=[
Name(id='b', ctx=Load())]),
Name(id='c', ctx=Load()),
Name(id='d', ctx=Load()),
Name(id='e', ctx=Load())]))
As one can see, it's the boolean operator or applied to four sub-expressions: comparison a == b; and simple expressions c, d, and e.
Summarising all existing answers
(And adding a few of my points)
Explanation :
if name == "Kevin" or "Jon" or "Inbar":
is logically equivalent to:
if (name == "Kevin") or ("Jon") or ("Inbar"):
Which, for user Bob, is equivalent to:
if (False) or ("Jon") or ("Inbar"):
NOTE : Python evaluates the logical value of any non-zero integer as True. Therefore, all Non-empty lists, sets, strings, etc. are evaluable and return True
The or operator chooses the first argument with a positive truth value.
Therefore, "Jon" has a positive truth value and the if block executes, since it is now equivalent to
if (False) or (True) or (True):
That is what causes "Access granted" to be printed regardless of the name input.
Solutions :
Solution 1 : Use multiple == operators to explicitly check against each value
if name == "Kevin" or name == "Jon" or name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
Solution 2 : Compose a collection of valid values (a set, a list or a tuple for example), and use the in operator to test for membership (faster, preferred method)
if name in {"Kevin", "Jon", "Inbar"}:
print("Access granted.")
else:
print("Access denied.")
OR
if name in ["Kevin", "Jon", "Inbar"]:
print("Access granted.")
else:
print("Access denied.")
Solution 3 : Use the basic (and not very efficient) if-elif-else structure
if name == "Kevin":
print("Access granted.")
elif name == "Jon":
print("Access granted.")
elif name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
There are 3 condition checks in if name == "Kevin" or "Jon" or "Inbar":
name == "Kevin"
"Jon"
"Inbar"
and this if statement is equivalent to
if name == "Kevin":
print("Access granted.")
elif "Jon":
print("Access granted.")
elif "Inbar":
print("Access granted.")
else:
print("Access denied.")
Since elif "Jon" will always be true so access to any user is granted
Solution
You can use any one method below
Fast
if name in ["Kevin", "Jon", "Inbar"]:
print("Access granted.")
else:
print("Access denied.")
Slow
if name == "Kevin" or name == "Jon" or name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
Slow + Unnecessary code
if name == "Kevin":
print("Access granted.")
elif name == "Jon":
print("Access granted.")
elif name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
Not-empty lists, sets, strings, etc. are evaluable and, therefore, return True.
Therefore, when you say:
a = "Raul"
if a == "Kevin" or "John" or "Inbar":
pass
You are actually saying:
if "Raul" == "Kevin" or "John" != "" or "Inbar" != "":
pass
Since at least one of "John" and "Inbar" is not an empty string, the whole expression always returns True!
The solution:
a = "Raul"
if a == "Kevin" or a == "John" or a == "Inbar":
pass
or:
a = "Raul"
if a in {"Kevin", "John", "Inbar"}:
pass
Simple engineering problem, let's simply it a bit further.
In [1]: a,b,c,d=1,2,3,4
In [2]: a==b
Out[2]: False
But, inherited from the language C, Python evaluates the logical value of a non zero integer as True.
In [11]: if 3:
...: print ("yey")
...:
yey
Now, Python builds on that logic and let you use logic literals such as or on integers, and so
In [9]: False or 3
Out[9]: 3
Finally
In [4]: a==b or c or d
Out[4]: 3
The proper way to write it would be:
In [13]: if a in (b,c,d):
...: print('Access granted')
For safety I'd also suggest you don't hard code passwords.
Using match/case in Python 3.10 and above
Python 3.10 adds a new syntax to the language. It's officially described as "structural pattern matching", but most people call it according to the syntax: "match/case".
Technical specification
Motivation and rationale (i.e., why it was added, and what inspired the design)
Official tutorial
We can use this special syntax for an example like in the question, by making one "case" that matches all the accepted usernames, and using the "wildcard" case _ in place of the else. Thus:
name = input("Hello. Please enter your name: ")
match name:
case "Kevin" | "Jon" | "Inbar":
print("Access granted.")
case _:
print("Access denied.")
Note that cases are "combined" using |, not or. This is a special syntax: Python does not try to compute "Kevin" | "Jon" | "Inbar" first (| doesn't work with strings), but instead interprets the entire line differently because it starts with case.
Besides some other rather rarer useful cases for the walrus operator already mentioned. This also tend to be a useful case as well.
def calc_value():
return 43
if (v := calc_value()) == 43 and v > 42:
print('happy short, efficient and readable code')
This works because each part of the if-statement is read separately. So (v := calc_value()) is executed and a value is assigned to v and if the first fails, you still have v in the namespace for different conditions or calculations.
Approaches
How a data scientist approaches this problem
The simplest way possible is to eliminate the need for comparison operators and use a list. This looks impressive on security systems because you learn to access ORMs.
user = input("Enter name: ")
if user in {"Bob", "Kevin", "Joe"}:
print("Access granted, " + str(user) + ".")
else:
print("Access denied.")
Or, you can resemble the exact same code above, just put the list of registered users in their own list:
user = input("Enter name: ")
users = {"Bob", "Kevin", "Joe", "a million more users if you like"}
if user in users:
print("Access granted, " + str(user) + ".")
else:
print("Access denied.")
If you wanted to complete this protocol safely without the risk of attack, set up double parameters. This would check your mini-ORM for first and last name fields, as well as a password or secret question key. Objects can be sorted like this if you want to efficiently lazy-load user credentials without hashing:
def lazy(i):
j = 0 # For example
while j < i:
yield j
j += 1
The loop will consume only the yielded values to save time and energy on your system:
You can then do something with the iterated list:
for j in lazy_range(10):
do_something_here(j)
This problem can be approached from any angle: memory management, security, or simply by an organic list or packaged ORM.
I am writing a security system that denies access to unauthorized users.
name = input("Hello. Please enter your name: ")
if name == "Kevin" or "Jon" or "Inbar":
print("Access granted.")
else:
print("Access denied.")
It grants access to authorized users as expected, but it also lets in unauthorized users!
Hello. Please enter your name: Bob
Access granted.
Why does this occur? I've plainly stated to only grant access when name equals Kevin, Jon, or Inbar. I have also tried the opposite logic, if "Kevin" or "Jon" or "Inbar" == name, but the result is the same.
This question is intended as the canonical duplicate target of this very common problem. There is another popular question How to test multiple variables for equality against a single value? that has the same fundamental problem, but the comparison targets are reversed. This question should not be closed as a duplicate of that one as this problem is encountered by newcomers to Python who might have difficulties applying the knowledge from the reversed question to their problem.
In many cases, Python looks and behaves like natural English, but this is one case where that abstraction fails. People can use context clues to determine that "Jon" and "Inbar" are objects joined to the verb "equals", but the Python interpreter is more literal minded.
if name == "Kevin" or "Jon" or "Inbar":
is logically equivalent to:
if (name == "Kevin") or ("Jon") or ("Inbar"):
Which, for user Bob, is equivalent to:
if (False) or ("Jon") or ("Inbar"):
The or operator chooses the first operand that is "truthy", i.e. which would satisfy an if condition (or the last one, if none of them are "truthy"):
if "Jon":
Since "Jon" is truthy, the if block executes. That is what causes "Access granted" to be printed regardless of the name given.
All of this reasoning also applies to the expression if "Kevin" or "Jon" or "Inbar" == name. the first value, "Kevin", is true, so the if block executes.
There are two common ways to properly construct this conditional.
Use multiple == operators to explicitly check against each value:
if name == "Kevin" or name == "Jon" or name == "Inbar":
Compose a collection of valid values (a set, a list or a tuple for example), and use the in operator to test for membership:
if name in {"Kevin", "Jon", "Inbar"}:
In general of the two the second should be preferred as it's easier to read and also faster:
>>> import timeit
>>> timeit.timeit('name == "Kevin" or name == "Jon" or name == "Inbar"',
setup="name='Inbar'")
0.4247764749999945
>>> timeit.timeit('name in {"Kevin", "Jon", "Inbar"}', setup="name='Inbar'")
0.18493307199999265
For those who may want proof that if a == b or c or d or e: ... is indeed parsed like this. The built-in ast module provides an answer:
>>> import ast
>>> ast.parse("a == b or c or d or e", "<string>", "eval")
<ast.Expression object at 0x7f929c898220>
>>> print(ast.dump(_, indent=4))
Expression(
body=BoolOp(
op=Or(),
values=[
Compare(
left=Name(id='a', ctx=Load()),
ops=[
Eq()],
comparators=[
Name(id='b', ctx=Load())]),
Name(id='c', ctx=Load()),
Name(id='d', ctx=Load()),
Name(id='e', ctx=Load())]))
As one can see, it's the boolean operator or applied to four sub-expressions: comparison a == b; and simple expressions c, d, and e.
Summarising all existing answers
(And adding a few of my points)
Explanation :
if name == "Kevin" or "Jon" or "Inbar":
is logically equivalent to:
if (name == "Kevin") or ("Jon") or ("Inbar"):
Which, for user Bob, is equivalent to:
if (False) or ("Jon") or ("Inbar"):
NOTE : Python evaluates the logical value of any non-zero integer as True. Therefore, all Non-empty lists, sets, strings, etc. are evaluable and return True
The or operator chooses the first argument with a positive truth value.
Therefore, "Jon" has a positive truth value and the if block executes, since it is now equivalent to
if (False) or (True) or (True):
That is what causes "Access granted" to be printed regardless of the name input.
Solutions :
Solution 1 : Use multiple == operators to explicitly check against each value
if name == "Kevin" or name == "Jon" or name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
Solution 2 : Compose a collection of valid values (a set, a list or a tuple for example), and use the in operator to test for membership (faster, preferred method)
if name in {"Kevin", "Jon", "Inbar"}:
print("Access granted.")
else:
print("Access denied.")
OR
if name in ["Kevin", "Jon", "Inbar"]:
print("Access granted.")
else:
print("Access denied.")
Solution 3 : Use the basic (and not very efficient) if-elif-else structure
if name == "Kevin":
print("Access granted.")
elif name == "Jon":
print("Access granted.")
elif name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
There are 3 condition checks in if name == "Kevin" or "Jon" or "Inbar":
name == "Kevin"
"Jon"
"Inbar"
and this if statement is equivalent to
if name == "Kevin":
print("Access granted.")
elif "Jon":
print("Access granted.")
elif "Inbar":
print("Access granted.")
else:
print("Access denied.")
Since elif "Jon" will always be true so access to any user is granted
Solution
You can use any one method below
Fast
if name in ["Kevin", "Jon", "Inbar"]:
print("Access granted.")
else:
print("Access denied.")
Slow
if name == "Kevin" or name == "Jon" or name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
Slow + Unnecessary code
if name == "Kevin":
print("Access granted.")
elif name == "Jon":
print("Access granted.")
elif name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
Not-empty lists, sets, strings, etc. are evaluable and, therefore, return True.
Therefore, when you say:
a = "Raul"
if a == "Kevin" or "John" or "Inbar":
pass
You are actually saying:
if "Raul" == "Kevin" or "John" != "" or "Inbar" != "":
pass
Since at least one of "John" and "Inbar" is not an empty string, the whole expression always returns True!
The solution:
a = "Raul"
if a == "Kevin" or a == "John" or a == "Inbar":
pass
or:
a = "Raul"
if a in {"Kevin", "John", "Inbar"}:
pass
Simple engineering problem, let's simply it a bit further.
In [1]: a,b,c,d=1,2,3,4
In [2]: a==b
Out[2]: False
But, inherited from the language C, Python evaluates the logical value of a non zero integer as True.
In [11]: if 3:
...: print ("yey")
...:
yey
Now, Python builds on that logic and let you use logic literals such as or on integers, and so
In [9]: False or 3
Out[9]: 3
Finally
In [4]: a==b or c or d
Out[4]: 3
The proper way to write it would be:
In [13]: if a in (b,c,d):
...: print('Access granted')
For safety I'd also suggest you don't hard code passwords.
Using match/case in Python 3.10 and above
Python 3.10 adds a new syntax to the language. It's officially described as "structural pattern matching", but most people call it according to the syntax: "match/case".
Technical specification
Motivation and rationale (i.e., why it was added, and what inspired the design)
Official tutorial
We can use this special syntax for an example like in the question, by making one "case" that matches all the accepted usernames, and using the "wildcard" case _ in place of the else. Thus:
name = input("Hello. Please enter your name: ")
match name:
case "Kevin" | "Jon" | "Inbar":
print("Access granted.")
case _:
print("Access denied.")
Note that cases are "combined" using |, not or. This is a special syntax: Python does not try to compute "Kevin" | "Jon" | "Inbar" first (| doesn't work with strings), but instead interprets the entire line differently because it starts with case.
Besides some other rather rarer useful cases for the walrus operator already mentioned. This also tend to be a useful case as well.
def calc_value():
return 43
if (v := calc_value()) == 43 and v > 42:
print('happy short, efficient and readable code')
This works because each part of the if-statement is read separately. So (v := calc_value()) is executed and a value is assigned to v and if the first fails, you still have v in the namespace for different conditions or calculations.
Approaches
How a data scientist approaches this problem
The simplest way possible is to eliminate the need for comparison operators and use a list. This looks impressive on security systems because you learn to access ORMs.
user = input("Enter name: ")
if user in {"Bob", "Kevin", "Joe"}:
print("Access granted, " + str(user) + ".")
else:
print("Access denied.")
Or, you can resemble the exact same code above, just put the list of registered users in their own list:
user = input("Enter name: ")
users = {"Bob", "Kevin", "Joe", "a million more users if you like"}
if user in users:
print("Access granted, " + str(user) + ".")
else:
print("Access denied.")
If you wanted to complete this protocol safely without the risk of attack, set up double parameters. This would check your mini-ORM for first and last name fields, as well as a password or secret question key. Objects can be sorted like this if you want to efficiently lazy-load user credentials without hashing:
def lazy(i):
j = 0 # For example
while j < i:
yield j
j += 1
The loop will consume only the yielded values to save time and energy on your system:
You can then do something with the iterated list:
for j in lazy_range(10):
do_something_here(j)
This problem can be approached from any angle: memory management, security, or simply by an organic list or packaged ORM.