DevHireLab
Tutorials
BootcampProblemsCode SimulatorAI InterviewSoonContact
DevHireLab
Tutorials
BootcampProblemsCode SimulatorAI InterviewSoonContact
Getting Started
01
What is Python and Why Learn It in 2026
02
Installing Python on Windows, Mac, and Linux
03
Setting up VS Code / PyCharm for Python
04
Understanding the Python Interpreter and REPL
05
Writing and Running Your First Python Program
06
Python Syntax and Indentation Rules
Variables and Data Types
01
Variables and Naming Conventions in Python
02
Numbers in Python (int, float, complex)
03
Strings and String Methods in Python
04
Booleans in Python
05
Type Conversion / Casting in Python
06
Understanding the None Type in Python
Operators
01
Arithmetic Operators in Python
02
Comparison Operators in Python
03
Logical Operators in Python
04
Assignment Operators in Python
05
Bitwise Operators in Python
06
Identity (is) and Membership (in) Operators
07
Walrus Operator (:=) in Python
Input, Output & Strings
01
input() and print() Functions in Python
02
f-strings and String Formatting in Python
03
String Slicing and Indexing in Python
04
Common String Methods (split, join, strip, replace)
05
Escape Characters in Python Strings
Control Flow
01
if, elif, else Statements in Python
02
Nested Conditionals in Python
03
for Loops in Python
04
while Loops in Python
05
break, continue, and pass Statements
06
Loop with else Clause in Python
07
match-case (Structural Pattern Matching)
Data Structures
01
Python Lists: Creation, Indexing, Slicing, Methods
02
Python Tuples and Immutability
03
Python Sets and Set Operations
04
Python Dictionaries: Keys, Values, and Methods
05
List Comprehensions in Python
06
Dictionary & Set Comprehensions in Python
07
Working with Nested Lists and Dictionaries
Functions
01
Defining and Calling Functions in Python
02
Function Arguments: Positional, Keyword, Default
03
*args and **kwargs in Python
04
Return Statements in Python Functions
05
Lambda Functions in Python
06
Recursion in Python
07
Variable Scope: Local, Global, Nonlocal
08
Docstrings and Function Annotations
Functional Programming Basics
01
map(), filter(), and reduce() in Python
02
Closures in Python Explained
03
Python Decorators: Basic to Advanced
04
Generators and yield in Python
Object-Oriented Programming (OOP)
01
Classes and Objects in Python
02
The init Constructor in Python
03
Instance vs Class Variables in Python
04
Inheritance in Python (Single, Multiple, Multilevel)
05
Polymorphism in Python
06
Encapsulation in Python
07
Abstraction in Python with the abc Module
08
Magic/Dunder Methods in Python
09
Static and Class Methods in Python
10
The @property Decorator in Python
11
Python Dataclasses Explained
Error Handling
01
try, except, finally in Python
02
Handling Multiple Exceptions in Python
03
Raising Custom Exceptions in Python
04
Understanding Python's Exception Hierarchy
05
Assertions in Python
File Handling
01
Reading and Writing Text Files in Python
02
Working with the with Statement in Python
03
Working with CSV Files in Python
04
Working with JSON Files in Python
05
Working with Directories (os, pathlib) in Python
Modules and Packages
01
Importing Built-in Modules in Python
02
Creating Your Own Modules in Python
03
Understanding Packages and init.py
04
Python Standard Library Overview
05
Installing Packages with pip
Advanced Core Concepts
01
Iterators and Iterables in Python
02
Context Managers in Python (with, contextlib)
03
Multithreading Basics in Python
04
Multiprocessing Basics in Python
05
Asyncio Basics: async/await in Python
06
Memory Management and Garbage Collection in Python
07
Type Hints in Python (typing module)

Logical Operators in Python

Real-world decisions rarely come down to a single condition. "Can this person drive?" depends on both their age and whether they have a license. "Is the store open?" might depend on it being a weekday or a holiday. Python logical operators are how you express exactly that kind of multi-condition logic in code.

What Are Logical Operators?

Logical operators combine or invert Boolean expressions to produce a single True/False result. Python has three of them:

and
or
not

If you've used other programming languages, you might expect symbols like && and || here. Python deliberately spells these out as plain English words instead — one more example of the language favoring readability over terse symbols.

These operators are what let you write if statements and loop conditions that check more than one thing at once, which is close to unavoidable in any real program.

The and Operator

and returns True only if both operands are True. If either one is False, the whole expression is False.

Truth table

A

B

A and B

True

True

True

True

False

False

False

True

False

False

False

False

Practical example
age = 20
has_license = True

can_drive = age >= 18 and has_license
print(can_drive)   # True

Both conditions have to hold for can_drive to be True. If either one fails — say, has_license is False — the whole expression becomes False, regardless of the person's age.

age = 20
has_license = False

can_drive = age >= 18 and has_license
print(can_drive)   # False — age is fine, but no license

The or Operator

or returns True if at least one operand is True. It only returns False when both operands are False.

Truth table

A

B

A or B

True

True

True

True

False

True

False

True

True

False

False

False

Practical example
is_weekend = False
is_holiday = True

store_closed = is_weekend or is_holiday
print(store_closed)   # True — at least one condition is met

This pattern is useful for combining conditions python developers often need to check together when any one of several situations qualifies — special pricing that applies on a birthday or during a promotional event, access granted to either an admin or the resource's owner, and so on.

The not Operator

not simply inverts a Boolean value — True becomes False, and vice versa.

print(not True)    # False
print(not False)   # True
Making conditions more readable

not is especially useful for writing conditions that read naturally in plain English, rather than as an awkward equality check:

is_logged_in = False

# Awkward
if is_logged_in == False:
    print("Please log in")

# Cleaner — this is the idiomatic Python style
if not is_logged_in:
    print("Please log in")

The second version is how experienced Python developers write this check. Comparing a Boolean to False with == works, but it's considered unpythonic — not is_logged_in says exactly the same thing more directly.

Combining Operators: Precedence and Short-Circuit Evaluation

Evaluation order

When you combine multiple logical operators in one expression, Python evaluates them in a specific order: not first, then and, then or.

result = not False and True or False

Working through it:

  1. not False → True

  2. True and True → True

  3. True or False → True

print(result)   # True

As expressions get more complex, this precedence order becomes harder to track mentally — which is exactly when parentheses earn their keep:

# Explicit grouping — removes any ambiguity about intent
result = (not False) and (True or False)

Both versions above evaluate identically, but the parenthesized one is unambiguous to read at a glance. When in doubt, add the parentheses — it costs nothing and saves the next reader (often you, later) from having to recompute precedence rules in their head.

Short-circuit evaluation

Here's a genuinely useful behavior worth understanding well: Python doesn't always evaluate both sides of and or or. It stops as soon as the overall result is already determined — this is called short-circuit evaluation.

  • With and: if the first operand is False, the result is guaranteed to be False no matter what the second operand is — so Python never bothers evaluating the second one.

  • With or: if the first operand is True, the result is guaranteed to be True — so again, Python skips the second operand entirely.

def expensive_check():
    print("This ran")
    return True

# Short-circuits — expensive_check() never runs, because False and anything is False
result = False and expensive_check()
print(result)   # False, and "This ran" is never printed
A practical safety pattern

Short-circuit evaluation isn't just an efficiency detail — it enables a genuinely important safety pattern, especially around checking for None before using a value:

user = None

if user is not None and user.is_admin:
    print("Access granted")
else:
    print("Access denied")

If user is not None evaluates to False, Python never even attempts to evaluate user.is_admin — which is exactly what you want, since accessing .is_admin on None would raise an AttributeError. The order here matters: putting the None check first is what makes this pattern safe. Writing it the other way around — user.is_admin and user is not None — would crash before short-circuiting ever had a chance to help.

Truthy/falsy values with logical operators

One more subtlety worth knowing: Python's logical operators don't always return a strict True or False. When operating on non-Boolean values, and and or actually return one of the original operands — whichever one determined the result — rather than converting it to a plain Boolean.

x = "" 
y = "hello"

print(x or y)    # 'hello' — x is falsy, so Python evaluates and returns y
print(x and y)   # ''      — x is falsy, so the result is x itself, without checking y

This is a common idiom for providing a fallback or default value:

name = ""
display_name = name or "Guest"
print(display_name)   # Guest — falls back since name is an empty (falsy) string

It's a compact, very common pattern in real Python code — worth recognizing even if you don't reach for it yourself right away.

PREVIOUSNEXT LESSON