C.W.K.
Stream
Lesson 05 of 07 · published

The Four Basic Types — int, float, bool, and the None You'll Meet

~22 min · int, float, bool, none, types, decimal, ieee754, truthiness

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

int — arbitrary precision, no overflow

Python's int has no maximum. Other languages give you 32-bit or 64-bit integers and overflow when you exceed them. Python expands its integer representation as needed — multiply two ten-digit numbers and you get a twenty-digit answer, no special syntax, no library. CPython implements this behind the scenes; you just get correctness.

This is more useful than it sounds. Cryptography, combinatorics, large-scale counting — domains that fight overflow in C/Java work straightforwardly in Python. The cost is a small per-operation overhead; for normal numbers it's invisible.

Integer literals support multiple bases — 0x for hex, 0o for octal, 0b for binary. Underscores are allowed inside literals for readability — 1_000_000 reads better than 1000000. Bitwise operators (& | ^ ~ << >>) work on ints directly.

float — IEEE 754, and the 0.1 + 0.2 trap

Python's float is a 64-bit IEEE 754 double-precision number — same representation every modern language uses. That representation cannot exactly express most decimal fractions. 0.1 in binary is a repeating fraction (like 1/3 in decimal); the stored value is approximately, not exactly, 0.1.

This means 0.1 + 0.2 == 0.3 is False. The actual sum is 0.30000000000000004. This is not a Python bug — it's how floating-point math works everywhere. Every language gets this wrong the same way.

Warning: Never compare floats with ==. Use math.isclose(a, b), which compares within a tolerance. The default tolerance is reasonable for most cases; tighten it if you need to.

Decimal — when float won't do

For domains where exact decimal representation matters — money, scientific measurements, anything regulated — use decimal.Decimal instead of float. Decimal stores the digits exactly and lets you control rounding behavior precisely.

Performance is slower than float (maybe 100×), so don't reach for Decimal for hot loops. But for an invoice total, a tax calculation, a financial report — Decimal is the right type.

bool — True, False, and the rest

Python's bool has two values — True and False. Capitalized; true is a NameError. Bool is actually a subclass of int — True == 1 and False == 0, and you can do arithmetic with them (sum([True, True, False]) gives 2). This is occasionally useful, occasionally surprising.

None — the absence object

None is Python's null. It's a singleton — there is exactly one None object, and every reference to None points at the same one. That's why if x is None works — identity comparison against the singleton.

Functions that don't explicitly return something return None implicitly. Default arguments are often None. Database NULL values come back as None. You'll see it everywhere; treat it as "no value here."

Truthiness — what counts as "true" in Python

Python's if doesn't only accept booleans. Any value can be evaluated for truthiness. The rules:

  • FalsyFalse, None, 0, 0.0, "" (empty string), [] (empty list), {} (empty dict), () (empty tuple), set() (empty set)
  • Truthy — everything else, including non-empty strings, non-zero numbers, non-empty collections

This enables the Pythonic idiom if items: instead of if len(items) > 0:. Both work; the first is the idiom. The Pythonic track digs into the trade-offs.

Self-reference: cwkPippa's backend uses truthiness everywhere — if response_text: instead of if response_text != "":, if user.session: instead of if user.session is not None:. The shorter form lets the reader focus on the question ("do we have a thing?") instead of the mechanism ("compare to empty/null").

Type conversion — int(), float(), str(), bool()

Each built-in type doubles as a constructor that converts other values. int("42") parses a string. float("3.14") too. str(42) goes the other way. bool(0) gives False; bool([]) gives False; bool("hello") gives True.

Two gotchas worth remembering — int("3.14") raises ValueError because string "3.14" isn't an integer. To go from string to int via float — int(float("3.14")) gives 3. And bool("False") gives True because the string "False" is non-empty (truthy). Use explicit comparison or a JSON-aware parser if you're reading boolean-ish strings.

Bottom line

int has no max. float can't exactly represent 0.1. Decimal exists for when float isn't enough. None is a singleton. Truthiness covers more than booleans. These five facts cover 95% of type-related surprises in Python.

Pythonic Way: Use truthiness for collection emptiness checks (if items:, not if len(items) == 0:). Use is None for None checks (not == None). Use math.isclose for float comparison (not ==). Use Decimal for money. Each of these is a one-line change that future-you will thank present-you for.

Code

int — no overflow, multiple bases, underscores for readability·python
>>> 2 ** 100                        # 100-digit integer, no overflow
1267650600228229401496703205376

>>> 0xff, 0o77, 0b1010              # hex, octal, binary literals
(255, 63, 10)

>>> 1_000_000                        # underscores for readability — same as 1000000
1000000

>>> 0b1100 & 0b1010                  # bitwise AND on ints
8                                    # = 0b1000
The 0.1 + 0.2 trap — float and IEEE 754·python
>>> 0.1 + 0.2
0.30000000000000004                  # not 0.3 exactly

>>> 0.1 + 0.2 == 0.3
False                                # this is correct behavior

>>> import math
>>> math.isclose(0.1 + 0.2, 0.3)
True                                 # the right way to compare floats

>>> # Why this happens — 0.1 in binary is a repeating fraction.
>>> # IEEE 754 double has 53 bits of precision; the rest gets truncated.
Decimal — when float won't do·python
>>> from decimal import Decimal

>>> Decimal("0.1") + Decimal("0.2")
Decimal('0.3')                       # exact!

>>> # Always pass strings to Decimal, not floats:
>>> Decimal(0.1)
Decimal('0.1000000000000000055511151231257827021181583404541015625')
>>> # Because the float 0.1 was already imprecise — Decimal preserves that imprecision.

>>> # Use case — money:
>>> price = Decimal("19.99")
>>> tax_rate = Decimal("0.0825")
>>> total = price * (1 + tax_rate)
>>> total.quantize(Decimal("0.01"))   # round to cents
Decimal('21.64')
Truthiness — the Pythonic check·python
>>> # All falsy values:
>>> for x in [False, None, 0, 0.0, "", [], {}, (), set()]:
...     print(bool(x))
False  False  False  False  False  False  False  False  False

>>> # Idiom — collection emptiness:
>>> items = []
>>> if not items:
...     print("empty")
empty

>>> # Idiom — first non-falsy with `or`:
>>> name = user_input or "default"     # if user_input is empty string, use "default"

>>> # Note — bool is a subclass of int. Surprising but true:
>>> True == 1
True
>>> sum([True, True, False, True])     # counting truthy values
3
Type conversion gotchas·python
>>> int("42")
42
>>> int("3.14")
Traceback (most recent call last):
...
ValueError: invalid literal for int() with base 10: '3.14'
>>> int(float("3.14"))               # via float — works
3

>>> bool("False")                     # SURPRISE — non-empty string is truthy
True
>>> # If you have a config string, check explicitly:
>>> value = "False"
>>> bool_value = value.lower() in ("true", "1", "yes", "on")

External links

Exercise

In the REPL — verify 0.1 + 0.2 != 0.3, then verify math.isclose(0.1 + 0.2, 0.3). Then create a Decimal('0.1') + Decimal('0.2') and notice the difference. Finally — create a list of all the falsy values from this lesson, pass each to bool(), confirm they all return False, and explain to yourself out loud why empty collections are falsy. Three minutes of REPL beats reading this lesson twice.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.