
Python logical operators are the glue that holds complex conditions together. Whether you are checking if a user is logged in and has the right permissions, or deciding whether a number falls within a certain range, python logical operators make it possible to combine multiple conditions into a single expression. In this tutorial you will learn how the three logical operators — and, or, and not — work in Python, when to use each one, and how Python evaluates them under the hood.
Logical operators are special keywords in Python that let you combine or modify boolean expressions. Python has exactly three logical operators:
| Operator | Meaning |
|---|---|
| and | True if both conditions are True |
| or | True if at least one condition is True |
| not | Reverses the boolean value |
These operators are used constantly in if statements, while loops, list comprehensions, and any place you need to make decisions based on more than one condition. Unlike comparison operators (such as ==, >, <) which compare two values, logical operators combine the results of comparisons into a final True or False answer.
The and operator returns True only when both the left-hand side and the right-hand side conditions are True. If either one is False, the whole expression evaluates to False.
Think of it like a two-key lock — both keys must turn at the same time for the door to open. If even one key is missing, nothing happens.
age = 25
has_id = True
if age >= 18 and has_id:
print("Entry allowed")
else:
print("Entry denied")
Entry allowed
Here, both conditions must pass. The variable age is 25, which is greater than or equal to 18, and has_id is True — so both sides are True and the block runs.
Here is the full truth table for the and operator:
| Left | Right | Result |
|---|---|---|
| True | True | True |
| True | False | False |
| False | True | False |
| False | False | False |
print(True and True) # True
print(True and False) # False
print(False and True) # False
print(False and False) # False
True
False
False
False
The or operator returns True if at least one of the conditions is True. It only returns False when both conditions are False.
Think of it like a light switch with two controls in different rooms — turning on either one lights up the room.
is_weekend = False
is_holiday = True
if is_weekend or is_holiday:
print("No work today!")
else:
print("Time to work.")
No work today!
Even though is_weekend is False, is_holiday is True. Since at least one condition is True, the or expression evaluates to True.
Truth table for or:
| Left | Right | Result |
|---|---|---|
| True | True | True |
| True | False | True |
| False | True | True |
| False | False | False |
print(True or True) # True
print(True or False) # True
print(False or True) # True
print(False or False) # False
True
True
True
False
The not operator is a unary operator — it takes only one operand and flips its boolean value. If something is True, not makes it False. If it is False, not makes it True.
It is like a toggle switch — pressing it once changes the state.
logged_in = False
if not logged_in:
print("Please log in to continue.")
else:
print("Welcome back!")
Please log in to continue.
The variable logged_in is False, and not False becomes True, so the if block runs.
Truth table for not:
| Value | Result |
|---|---|
| True | False |
| False | True |
print(not True) # False
print(not False) # True
False
True
In real applications you rarely stop at just one logical operator. Python lets you chain and, or, and not together in a single expression. When you do this, Python follows a specific order of precedence: not is evaluated first, then and, then or. This mirrors how most programming languages handle logical expressions.
score = 72
passed_exam = True
has_attendance = False
if (score >= 60 and passed_exam) or has_attendance:
print("Student passes the course")
else:
print("Student does not pass the course")
Student passes the course
Here the parentheses ensure score >= 60 and passed_exam is evaluated together first. That inner expression is True, so the or sees True or False, which is True.
Without parentheses Python would follow natural precedence, but adding them makes intent clear and prevents bugs. Always use parentheses when mixing and with or to make your code easy to read.
Python uses short-circuit evaluation when processing logical operators. This means Python stops evaluating as soon as the final result is certain, without checking the remaining conditions.
For and: if the left side is False, Python skips the right side entirely because the result can only be False. For or: if the left side is True, Python skips the right side because the result is already True.
This behavior is not just an optimization — it has practical uses. You can guard against errors by putting a safety check on the left side.
data = []
if data and data[0] > 10:
print("First element is greater than 10")
else:
print("List is empty or condition not met")
List is empty or condition not met
Because data is an empty list (falsy), Python short-circuits and never tries to access data[0]. Without short-circuiting this would raise an IndexError.
You can learn more about how Python evaluates expressions in the official Python documentation on boolean operations.
Python logical operators do not always return True or False — they return one of the actual operand values depending on which one determined the result. This is a useful and sometimes surprising feature of Python.
For and: returns the first falsy value it encounters, or the last value if all are truthy. For or: returns the first truthy value it encounters, or the last value if all are falsy.
print(0 and "hello") # 0 — first falsy value
print(5 and "hello") # "hello" — all truthy, returns last
print(0 or "hello") # "hello" — first truthy value
print("" or 0) # 0 — all falsy, returns last
print(not "") # True — empty string is falsy, not flips to True
0
hello
hello
0
True
This behavior is used in Python idioms like providing default values:
username = ""
display_name = username or "Guest"
print(display_name)
Guest
If username is an empty string (falsy), Python uses the fallback value "Guest". This is a clean and common pattern in Python code.
Python logical operators work equally well inside while loops to keep looping as long as multiple conditions are satisfied.
attempts = 0
max_attempts = 3
success = False
while attempts < max_attempts and not success:
attempts += 1
print(f"Attempt {attempts}")
if attempts == 2:
success = True
print("Done" if success else "All attempts exhausted")
Attempt 1
Attempt 2
Done
The loop continues only while both conditions hold: attempts is still under the limit and success has not yet become True. Once success flips to True, not success becomes False, and the loop exits.
You can use python logical operators inside list comprehensions to filter values based on multiple conditions at once.
numbers = [1, 4, 7, 12, 15, 18, 21, 30]
divisible = [n for n in numbers if n % 3 == 0 and n % 2 == 0]
print(divisible)
[12, 18, 30]
This collects only numbers that are divisible by both 3 and 2 — meaning divisible by 6 — using the and operator inline.
filtered = [n for n in numbers if n < 5 or n > 20]
print(filtered)
[1, 4, 21, 30]
The or operator here keeps numbers that are either very small or very large, skipping the middle range.
When you mix python logical operators in one expression without parentheses, Python evaluates them in this order:
result = True or False and not True
# Step 1: not True → False
# Step 2: False and False → False
# Step 3: True or False → True
print(result)
True
Understanding this order helps you predict what Python will do. When in doubt, use parentheses to make the grouping explicit. The Python operator precedence table in the official docs is a useful reference.
This example builds a simple access control checker that uses all three python logical operators together. It evaluates whether a user can access a restricted area based on their role, age, and verification status.
def check_access(role, age, verified):
is_admin = role == "admin"
is_adult = age >= 18
is_staff = role == "staff"
if is_admin and verified:
print(f"Full admin access granted (age: {age})")
elif (is_staff or is_adult) and verified:
print(f"Standard access granted for role: {role}")
elif not verified:
print("Access denied: account not verified")
else:
print("Access denied: insufficient permissions")
check_access("admin", 30, True)
check_access("staff", 16, True)
check_access("guest", 25, False)
check_access("guest", 15, True)
Full admin access granted (age: 30)
Standard access granted for role: staff
Access denied: account not verified
Access denied: insufficient permissions
The first call passes because both is_admin and verified are True. The second call goes to the elif because is_staff is True and verified is True, even though is_adult is False — or only needs one side to be True. The third call is caught by not verified. The fourth call fails all conditions because neither is_staff nor is_adult is True, and the role is guest.