Skip to content
Hello Python
2/8

Operators & Expressions

Topic 2 of 8, with 4 concept checks. Logical, bitwise, arithmetic, and overloaded operators

Predict how Python evaluates an expression

Evaluation order

Read an expression in Python's actual precedence order, distinguish logical from bitwise behavior, and decide which special method participates when an operator is overloaded.

Core lesson 01

and/or are logical and short-circuit, returning an operand. &/| are bitwise, always evaluate both sides.

`and`/`or` are control-flow operators: they evaluate left to right and stop as soon as the result is determined, returning the actual operand (not necessarily True/False). `&`/`|` are bitwise operators — they always evaluate both sides and operate bit-by-bit on integers (they're also overloaded by set for union/intersection).

Python example
def noisy(label, val):
    print("eval", label)
    return val

noisy("a", False) and noisy("b", True)   # only prints 'eval a'
noisy("a", False) & noisy("b", True)     # prints both -- always evaluates

What to remember

What's the difference between and/or and &/| for booleans?

Common footguns

  • Using `&`/`|` for boolean logic in an `if` — works for plain booleans but breaks intent and precedence expectations.
  • Forgetting `&`/`|` bind tighter than comparisons: `a & b == c` isn't what it looks like — need `(a & b) == c`.
a() and b()   if a() is False -> b() NEVER called (short-circuit)
a() or  b()   if a() is True  -> b() NEVER called (short-circuit)

Core lesson 02

True — numeric types compare by value, not by exact type.

Python's numeric tower (bool, int, float, complex) defines cross-type equality and arithmetic. `==` between numbers compares mathematical value, so 3 == 3.0 is True even though `type(3) is int` and `type(3.0) is float`. This is different from `is`, which would say False since they're distinct objects.

Python example
print(3 == 3.0)        # True
print(3 is 3.0)        # False
print(type(3) == type(3.0))  # False

What to remember

What does 3 == 3.0 evaluate to, and why?

Common footguns

  • Assuming equal values always means equal types — always true for numbers, but not a general Python rule.

Core lesson 03

Implement __add__(self, other) — Python calls it automatically whenever + is used between instances.

Operator overloading in Python works through 'dunder' (double-underscore) methods. `a + b` is really syntactic sugar for `a.__add__(b)` (falling back to `b.__radd__(a)` if the first returns NotImplemented). This is how built-in types support `+` too — int, str, and list all define their own `__add__`.

Python example
class Vector:
    def __init__(self, x, y):
        self.x, self.y = x, y
    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)
    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

print(Vector(1,2) + Vector(3,4))   # Vector(4, 6)

What to remember

How would you make a custom class support the + operator?

Common footguns

  • Forgetting to return NotImplemented (not raise) when the other operand's type isn't supported, which breaks fallback to __radd__.
  • Mutating self inside __add__ instead of returning a new object — breaks the usual 'a+b doesn't change a' expectation.

Core lesson 04

-10 % 3 = 2, because Python's % always matches the sign of the divisor.

Python defines `%` so that `a % b` always has the same sign as `b` (when b != 0), consistent with floor division: `a == (a // b) * b + (a % b)`. In C-family languages, `%` instead takes the sign of the dividend, so -10 % 3 would be -1 there.

Python example
print(-10 % 3)   # 2
print(10 % -3)   # -2
print(-10 // 3)  # -4
print(-4*3 + 2)  # -10  (checks out)

What to remember

What's the result of -10 % 3 in Python?

Common footguns

  • Porting modulo-based logic (e.g. hashing, wraparound indices) from C/Java and getting different results for negative numbers.

Python lab

Browser Python lab

Runtime · idle

Python loads on your first run. Your code stays in this browser.

Best practices

  • Use and/or for boolean logic; reserve &/| for bitwise ops or pandas/numpy boolean arrays.
  • Avoid unclear chained comparisons unless they genuinely improve readability.
  • Prefer `x += 1` over `x = x + 1` for simple in-place-style updates.
  • Use parentheses to make operator precedence explicit, even when not strictly required.

Apply the concept in Interview practice

Power of TwoeasyLeetCode #231 · O(1) time/space

n is a power of two if n>0 and (n & (n-1)) == 0 — a power of two has exactly one bit set.

Open problem
Divide Two IntegersmediumLeetCode #29 · O(log n) time

Simulate division via repeated doubling/bit-shifting of the divisor instead of * or /; handle sign and 32-bit overflow separately.

Open problem
Mini-Max SumeasyHackerRank · O(n log n) time

Sort the array; the min sum excludes the largest element, the max sum excludes the smallest.

Open problem
Single NumbereasyLeetCode #136 · O(n) time, O(1) space

XOR every number together — a value XORed with itself is 0, and XOR is commutative/associative, so all paired values cancel out, leaving only the single one.

Open problem
Sum of Two IntegersmediumLeetCode #371 · O(1) time (bounded by int width)

Simulate addition with bitwise XOR for the sum-without-carry and AND+shift for the carry, looping until there's no carry left.

Open problem

Concept checks

Q01

What's the difference between and/or and &/| for booleans?

Q02

What does 3 == 3.0 evaluate to, and why?

Q03

How would you make a custom class support the + operator?

Q04

What's the result of -10 % 3 in Python?