not — Intermediate Examples
Logical NOT operator; inverts a boolean value
not in boolean contexts
Using not for clean conditional logic.
python
# Double negation and bool() x = 42 print(not not x) # Same as bool(x) print(bool(x)) # Clearer way # not vs != items = [1, 2, 3] # These are different: print(not items == []) # not (items == []) = not False = True print(items != []) # True (preferred) # De Morgan's laws a, b = True, False print(f"not (a or b) = {not (a or b)}") print(f"(not a) and (not b) = {(not a) and (not b)}") print(f"not (a and b) = {not (a and b)}") print(f"(not a) or (not b) = {(not a) or (not b)}")
Expected Output
True True True True not (a or b) = False (not a) and (not b) = False not (a and b) = True (not a) or (not b) = True
'not' always returns a boolean. Use it for readability, but prefer 'is not', 'not in', and '!=' over 'not ... is', 'not ... in', and 'not ... =='.
not with custom objects
How not interacts with __bool__ and __len__.
python
class Box: def __init__(self, items): self.items = items def __bool__(self): return len(self.items) > 0 def __repr__(self): return f"Box({self.items})" empty_box = Box([]) full_box = Box([1, 2, 3]) print(f"not {empty_box}: {not empty_box}") print(f"not {full_box}: {not full_box}") # Without __bool__, not uses __len__ class Stack: def __init__(self): self.data = [] def push(self, x): self.data.append(x) def __len__(self): return len(self.data) s = Stack() print(f"Empty stack: not s = {not s}") s.push(1) print(f"Non-empty stack: not s = {not s}")
Expected Output
not Box([]): True not Box([1, 2, 3]): False Empty stack: not s = True Non-empty stack: not s = False
'not' calls __bool__() on the object. If __bool__ isn't defined, it falls back to __len__. If neither exists, the object is always truthy (not returns False).
Want to try these examples interactively?
Open Intermediate Playground