Why does `1 == True` return True in Python?
answer
- Not truthiness — look at the type
- Check the method resolution order
- Equal values must hash equally
- One dictionary key, not two
- bool inherits from int
basics
~20 sBecause bool is a subclass of int: True and False are integers with the values 1 and 0. They therefore compare equal to those integers, hash the same, and can be used in arithmetic — but True and 1 are still different objects.
solid answer
~40 s`bool` inherits from `int`, so `True` carries the value 1 and `False` carries 0 — a compatibility decision from when booleans were added to a language that had used 1 and 0. The consequences follow the equality contract: because `True == 1`, `hash(True)` must equal `hash(1)`, so a `dict` cannot hold both as separate keys and `{1, True}` is a one-element set. `True + True` is 2, `sum()` over booleans counts them, and `isinstance(True, int)` is `True`, which means an integer validation guard silently accepts a boolean. Identity is where the coincidence stops: `1` and `True` are equal but not the same object, so an identity check distinguishes them. If a parameter must reject booleans, test `type(x) is int`, or check `isinstance(x, bool)` first.
code
python · 8 linestotals = {1: "int key"}
totals[True] = "bool key"
print(totals) # {1: 'bool key'} - value replaced, key kept
print({True: "a", 1: "b"}) # {True: 'b'}
print({1, True}, hash(True) == hash(1))
print(bool.__mro__)
print(isinstance(True, int), sum([True, True, False]))
print([1] == True) # False: equality is not truthinessgo deeper
Recall the fact and its one-line reason: True and False are integers with the values 1 and 0. You will not be marked down for missing the dictionary consequence, but do not answer with truthiness.
Explain the subclass relationship and derive the container behaviour from the equality-implies-equal-hash rule. Be ready to show that a set of 1 and True has a single element and to say which key object survives.
Point at where it bites in real code: integer validation that accepts booleans, deduplicating caches merging a flag with a count, and serialisation formats that distinguish true from 1 even though Python does not.
Frame it as an API-design constraint: a parameter that means how many and a parameter that means whether should not share a call signature, and validation policy should be explicit about accepting bool subclasses rather than inheriting the ambiguity.
`1 == True` is `True` because `bool` is a **subclass of `int`**. Python has no separate boolean primitive: `True` and `False` are the two instances of `bool`, and they carry the integer values 1 and 0. `bool.__mro__` is `(bool, int, object)`, so a boolean is an integer everywhere the language looks at integers — in arithmetic, in comparisons, in `isinstance` checks, and in hashing. This is a **deliberate compatibility decision** from the introduction of the type: code written before booleans existed used 1 and 0, and making `bool` an `int` subclass kept that code working. The consequences are broader than the equality itself. ## Arithmetic - `True + True` is 2, `True * 5` is 5. - `sum(flags)` counts the true values in an iterable of booleans — a genuinely idiomatic way to count matches. - `[True] * 3` and `range(True, 5)` work for the same reason. ## Hashing, and therefore containers The equality invariant requires that **equal objects hash equally**, so `hash(True) == hash(1)`. A dict cannot hold both as separate keys: `{1: "int"}` followed by `d[True] = "bool"` leaves a one-entry dict. Note precisely what happens — the *value* is replaced and the *original key object* is kept, so the dict still prints with `1` as its key, while a dict built as `{True: "a", 1: "b"}` prints with `True`. Sets collapse the same way: `{1, True}` has one element, and `{0, False, 1, True}` has two. The same rule reaches beyond booleans, because Python's numeric types compare across the tower: `1 == 1.0` is true, `hash(1) == hash(1.0)` is true, and a dict keyed by numbers cannot distinguish `1` from `1.0` either. ## Type checks `isinstance(True, int)` is `True`. Validation code that accepts "an integer" will therefore accept a boolean. Consider a document renderer whose `copies` parameter is an integer count: `render(invoice, copies=True)` passes every `isinstance(copies, int)` guard and renders exactly one copy, which is the sort of defect that survives review because it reads as a plausible flag. - If you genuinely need to exclude booleans, test `type(x) is int`, or reject `isinstance(x, bool)` explicitly before the integer check. - The reverse also happens: a value read as `0`/`1` from a data source flows into a parameter documented as a flag and behaves correctly by accident until someone passes 2. ## Identity is where the coincidence stops `1` and `True` are equal but are not the same object: they have different types and different `id()` values, so an identity comparison between them is false. This is the cleanest demonstration of why the two operators are not interchangeable — **equality crosses the type boundary here, identity does not**. It also means `is` is the tool when you must tell a real boolean from the integer 1: an exact-type check or `x is True` distinguishes them, while `x == 1` cannot. ## Where it shows up in practice - **Serialisation formats care**: JSON writes `true` for a boolean and `1` for the integer, so a value that silently became a boolean changes the wire format. - **Database drivers and column types** make the same distinction. - **Counting APIs** that accept "how many" and **flags** that accept "whether" collide when both are spelled with the same literal. - And any **deduplicating structure** — a set of "seen" keys, a dict of "results by input" — will merge a boolean with the corresponding integer, and with the corresponding float, without a word of warning. ## How to talk about it The wrong answer is "because Python treats anything non-empty as true" — that is **truthiness**, a different mechanism, governed by `__bool__` and `__len__`, and it explains why `if [1]:` runs, not why `1 == True`. Truthiness never makes `==` true: `[1] == True` is `False`, because equality is not truthiness. The right answer: 1. names the subclass relationship, 2. then names the hash consequence, 3. and then, if you want to show depth, notes that the same equal-and-therefore-same-hash rule is what merges `1`, `1.0` and a boolean into a single dictionary key. This is not a question anyone fails an interview over, but it is a fast probe of whether you know Python's object model rather than just its syntax, and the follow-up — "so what does a `set` of mixed numeric types actually contain?" — separates a memorised fact from an understood one.
- How is this different from truthiness?Truthiness is what `if` and `bool()` do: they call `__bool__`, or fall back to `__len__`, so a non-empty list is *truthy*. That never makes it equal to `True` — `[1] == True` is `False`. The `1 == True` result comes from the type hierarchy instead: a boolean *is* an integer, so integer equality applies.
- Does the same key collapse happen with floats?Yes. Python's numeric types compare across the tower, so `1 == 1.0` and their hashes match, which means one dict cannot hold `1`, `1.0` and `True` as three keys — it keeps one entry whose key is whichever object was inserted first. Any deduplicating structure keyed on numbers behaves the same way.
- How do you write a guard that accepts an integer count but rejects a boolean?Reject booleans explicitly before the integer check — `if isinstance(x, bool): raise TypeError(...)` followed by `isinstance(x, int)` — or use an exact-type test, `type(x) is int`, when subclasses are not wanted at all. Relying on `isinstance(x, int)` alone always lets `True` and `False` through.
saying these in an interview costs you the question
- Says it is truthiness rather than subclassing
- Expects a dict to keep 1 and True as separate keys
- Claims isinstance(True, int) is False
- Thinks 1 is True because they compare equal
- Assumes adding two booleans raises TypeError