skip to content

Why does `1 == True` return True in Python?

level: middleimportance: nice to knowfreq 30%

answer

  1. Not truthiness — look at the type
  2. Check the method resolution order
  3. Equal values must hash equally
  4. One dictionary key, not two
  5. bool inherits from int

basics

~20 s

Because 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 lines
python
totals = {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 truthiness

go deeper

for a junior

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.

for a middle

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.

for a senior

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.

for a principal

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

context