Python and in an If Statement: The && Alternative

Python stops on the line the moment it meets && inside an if statement, and the caret lands under the second ampersand. Every C-family language accepts those two characters for logical conjunction, so a parser error reads like a bug rather than a language rule. The replacement is the and keyword, and it behaves differently from && in two ways that change your code: it returns one of its operands, and it sometimes skips the second one entirely.

Why && is a syntax error in Python

The characters & and && both exist in Python, and neither one means logical conjunction. A single & is the bitwise AND operator, and a doubled && is not a token the grammar recognises at all.

name1 = "Kundan"
name2 = "Rohan"

if name1 == "Kundan" && name2 == "Rohan":
    print("Hello Kundan and Rohan")
Terminal traceback showing SyntaxError: invalid syntax on a line that uses && in a Python if statement, with the caret pointing at the && characters. Command: python3 and_syntax.py.
Python rejects && while it reads the line, and the caret marks the exact characters it cannot parse.

The traceback points at line 4 and puts the caret under the ampersands rather than the comparison. That position is the useful part, because it says the grammar never reached the operands.

A single & parses, which makes the mistake harder to spot.

print("2 & 3      ->", 2 & 3)
print("True & False ->", True & False)
2 & 3      -> 2
True & False -> False

The bitwise version returns 2 for the same input, because it compares the bits of 2 and 3 instead of deciding anything about truth. Reaching for & when you meant logical conjunction is how a condition ends up true in cases you never tested.

Checking the exact error text is worth the habit, because a missing and looks like a missing bracket at a glance and the two need different fixes.

Using and in an if statement

The and keyword joins two conditions, and the block runs only when both of them are truthy.

name1 = "Kundan"
name2 = "Rohan"

if name1 == "Kundan" and name2 == "Rohan":
    print("Hello Kundan and Rohan")
Hello Kundan and Rohan

Nesting two if statements produces the same outcome, at the cost of an extra indent level.

value = 7

if value > 0:
    if value % 2 == 1:
        print("positive odd number")

if value > 0 and value % 2 == 1:
    print("positive odd number")
positive odd number
positive odd number

Both forms print the same line. The single-line version reads as one decision instead of two, and it keeps the two conditions visible next to each other rather than pushing the second one down an indent level.

Nesting also grows badly, because three conditions mean three indent levels and a block that is mostly whitespace.

user = "kundan"
active = True
attempts = 2

if user == "kundan" and active and attempts < 3:
    print("sign-in allowed")
else:
    print("sign-in blocked")
sign-in allowed

What and actually returns

The operator does not convert its operands to booleans and hand one back. It returns the operand that decided the outcome, whatever type that operand happens to be.

print("1 and 2     ->", repr(1 and 2))
print("0 and 2     ->", repr(0 and 2))
print("'a' and 'b' ->", repr("a" and "b"))
print("'' and 'b'  ->", repr("" and "b"))
print("type        ->", type(1 and 2).__name__)
Terminal output showing 1 and 2 returning 2, 0 and 2 returning 0, 'a' and 'b' returning 'b', an empty string and 'b' returning the empty string, and the type of 1 and 2 being int. Command: python3 and_returns.py.
The result carries the type of whichever operand decided the outcome. It is not a boolean.

When the left operand is truthy, the expression hands back the right one, so 1 and 2 is 2 rather than True. When the left operand is falsy, the right one never gets a say and the left one comes back unchanged.

That is why the printed type is int. Code that assumes a boolean from and will still work inside an if statement, because the result is evaluated for truthiness anyway, and it breaks the moment you store the value and compare it to True.

result = 1 and 2
print("result is True :", result is True)
print("result == True :", result == True)
print("bool(result)   :", bool(result))
result is True : False
result == True : False
bool(result)   : True

Both comparisons return False, and only the last line reports what the branch would have seen. Storing the raw result and testing it against True is the mistake the snippet exposes.

The truth table for and

Two operands give four combinations, and and returns the first falsy one it meets. When neither is falsy, it returns the last operand.

def truth(a, b):
    return a and b

print(f"{'A':<6}{'B':<6}{'A and B'}")
for a in (True, False):
    for b in (True, False):
        print(f"{str(a):<6}{str(b):<6}{truth(a, b)}")
A     B     A and B
True  True  True
True  False False
False True  False
False False False
ABA and BOperand returned
TrueTrueTrueB, because A was truthy
TrueFalseFalseB, because A was truthy
FalseTrueFalseA, because A was falsy
FalseFalseFalseA, because A was falsy

Read the last column first. The block inside an if statement runs only on the first row, and the value that came back identifies which operand ended the evaluation.

Every row below the first returns a falsy value, so the branch is skipped even though one of the two operands was true.

Short-circuit evaluation

Python stops evaluating as soon as the answer is settled, which means a falsy left operand prevents the right one from running at all. A function call on the right side is the clearest way to see it.

def side_effect(label):
    print(f"   evaluated {label}")
    return True

print("case 1: left operand False")
result = False and side_effect("right")
print("   result:", result)

print("case 2: left operand True")
result = True and side_effect("right")
print("   result:", result)
Terminal output showing that False and check('right') prints only result: False, while True and check('right') prints evaluated right before result: True. Command: python3 short_circuit.py.
The check function runs in the second case and never runs in the first, which is short-circuit evaluation made visible.

The second case prints the evaluated line and the first does not. Nothing was skipped by accident, and the same rule that saves a wasted call can hide a call you were counting on.

Left operandRight operand evaluated