random.randint / randrange

randint(a, b) is literally randrange(a, b + 1). Both draw exactly uniform integers with getrandbits() and rejection, work for arbitrarily large ranges, and refuse floats since Python 3.12.

random functionAll Python 3 versions (float arguments rejected since 3.12)Live demo
Common call
random.randint(1, 6)
Returns
int, both ends included (randrange: stop excluded)
Replaces
int(random.random() * n) + 1
Watch out
randint(5, 1) fails with a message about randrange(5, 2)
random.randint(a, b) / random.randrange(start, stopstop — randrange: excluded upper (or lower, for a negative step) bound.type: int · default: None=None, stepstep — randrange: spacing of the candidates; may be negative, never 0. Needs an explicit stop.type: int · default: 1=1)
→ int

Demo

Live evaluation
Eight integers from a to b inclusive. Try a > b to see the error message.
Try:
Inputs
seedint | stran int or a string
aintlowest
binthighest
Code
import random
random.seed(42)
[random.randint(1, 10) for _ in range(8)]
Result
[2, 1, 5, 4, 4, 3, 2, 9]

randint(5, 1) calls randrange(5, 2), and that is the call the error names: "empty range in randrange(5, 2)". A negative step counts down from start, so randrange(10, 0, -2) picks from 10, 8, 6, 4, 2 and never 0. Step 0 and a step pointing away from stop are both ValueErrors.

Parameters

NameTypeRequiredDescription
a, bintyesrandint: the lowest and highest possible result. b must be >= a.
startintyesrandrange: with one argument this is stop and start is 0, like range(stop).
stopintno (None)randrange: excluded upper (or lower, for a negative step) bound.
stepintno (1)randrange: spacing of the candidates; may be negative, never 0. Needs an explicit stop.

Return value

int — randint: a <= N <= b. randrange: an element of range(start, stop, step).

Common patterns

Roll dice
Both ends included.
import random
roll = random.randint(1, 6)
three_d6 = sum(random.randint(1, 6) for _ in range(3))
A random index
randrange(len(seq)) can never be out of range (but choice(seq) is simpler for the item).
import random
i = random.randrange(len(items))
An even number from 0 to 100
Step 2 with stop 101 includes 100.
import random
even = random.randrange(0, 101, 2)
A random big id
Arbitrarily large ranges work (getrandbits rejection sampling) — but use secrets for anything guessable.
import random
row_id = random.randrange(10 ** 30)

Examples

1. randint includes both ends
import random random.seed(42) [random.randint(1, 10) for _ in range(8)]
Returns
[2, 1, 5, 4, 4, 3, 2, 9]
2. randrange(n): 0 to n - 1
import random random.seed(42) [random.randrange(10) for _ in range(8)]
Returns
[1, 0, 4, 3, 3, 2, 1, 8]
3. randrange with a step
import random random.seed(42) [random.randrange(0, 100, 5) for _ in range(6)]
Returns
[15, 0, 40, 35, 35, 20]
4. Same draws: randint(a, b) is randrange(a, b + 1)
import random random.seed(42) [random.randrange(1, 7) for _ in range(5)]
Returns
[6, 1, 1, 6, 3]
5. Huge ranges are exact
import random random.seed(42) random.randint(10 ** 20, 10 ** 20 + 5)
Returns
100000000000000000005
6. a > b
import random random.randint(5, 1)
Returns
ValueError: empty range in randrange(5, 2)
7. Floats are rejected (3.12+)
import random random.randint(1, 6.0)
Returns
TypeError: 'float' object cannot be interpreted as an integer
8. step needs an explicit stop
import random random.randrange(10, step=2)
Returns
TypeError: Missing a non-None stop argument

Pitfalls

1. randrange(1, 6) for a die
randrange excludes stop, like range(), so 6 never comes up. randint(1, 6), or randrange(1, 7), includes it.
randrange(1, 6)
import random
random.seed(42)
rolls = [random.randrange(1, 6) for _ in range(6000)]
(min(rolls), max(rolls))
(1, 5)
randint(1, 6)
import random
random.seed(42)
rolls = [random.randint(1, 6) for _ in range(6000)]
(min(rolls), max(rolls))
(1, 6)
2. Passing a float bound
Since 3.12 there is no silent int() conversion: 6.0 from a division or float() input raises TypeError. Convert explicitly.
n = 12 / 2
import random
n = 12 / 2
random.randint(1, n)
TypeError: 'float' object cannot be interpreted as an integer
n // 2
import random
random.seed(42)
n = 12 // 2
random.randint(1, n)
6
3. randrange(0) or an empty range
There is nothing to pick from range(0) or range(5, 5); randrange raises instead of returning a default.
randrange(0)
import random
random.randrange(0)
ValueError: empty range for randrange()
guard the empty case
import random
n = 0
random.randrange(n) if n > 0 else 'nothing to pick'
'nothing to pick'

When to use

Use it
  • Dice, card indexes, random ids in a range
  • Exactly uniform integers, including huge ranges like randrange(10 ** 30)
  • Every k-th value: randrange(start, stop, k)
Reach for something else
  • Picking an item → choice(seq)
  • Several distinct ints → sample(range(a, b + 1), k)
  • Security codes and PINs → secrets.randbelow(n)

Notes

CPython impl
Lib/random.py: randint returns self.randrange(a, b+1); randrange converts its arguments with operator.index() and draws _randbelow(n), which calls getrandbits(n.bit_length()) until the value is below n — exact integer arithmetic, identical on every platform
Messages
The ValueError shows the arguments as you passed them: empty range in randrange(5, 5) / zero step for randrange() / empty range for randrange() for a single argument <= 0
Keywords
The docs warn against keyword arguments: randrange(start=100) is interpreted as randrange(0, 100, 1)

FAQ

Yes, randint(a, b) can return both a and b. randrange(a, b) excludes b, like range(a, b).