When should I prefer random.choice() over random.randint?

Viewed 198

Consider the following two options for printing a random string from a list of messages:

import random

messages = ['It is certain',
    'It is decidedly so',
    'Yes definitely',
    'Reply hazy try again',
    'Ask again later',
    'Concentrate and ask again',
    'My reply is no',
    'Outlook not so good',
    'Very doubtful']


print(messages[random.randint(0, len(messages) - 1)])  # option 1

print(random.choice(messages))                         # option 2

Both option 1 and option 2 produce the same effect. Is there a reason to prefer one over the other? What about performance—is one more efficient than the other?

3 Answers

Some time profiling:

from timeit import timeit

setup = """
import random
import string
s = string.ascii_lowercase
x = [s[:random.randint(1, 26)] for _ in range(10_000)]
"""
stmt1 = """
random.choice(x)
"""

stmt2 = """
x[random.randint(0, len(x) - 1)]
"""

for i in range(0, 100_000, 10_000):
    print(f"Size_seq: {i}")
    print(f"    choice: {timeit(stmt = stmt1, setup = setup, number = i):>.5}s")
    print(f"    randint: {timeit(stmt = stmt2, setup = setup, number = i):>.5}s")

Output:

Size_seq: 0
    choice: 1.426e-06s
    randint: 1.672e-06s
Size_seq: 10000
    choice: 0.010363s
    randint: 0.017393s
Size_seq: 20000
    choice: 0.020168s
    randint: 0.034601s
Size_seq: 30000
    choice: 0.030942s
    randint: 0.050896s
Size_seq: 40000
    choice: 0.041753s
    randint: 0.069362s
Size_seq: 50000
    choice: 0.053554s
    randint: 0.088815s
Size_seq: 60000
    choice: 0.067187s
    randint: 0.10979s
Size_seq: 70000
    choice: 0.080606s
    randint: 0.12793s
Size_seq: 80000
    choice: 0.083252s
    randint: 0.1365s
Size_seq: 90000
    choice: 0.091724s
    randint: 0.14603s

The difference is significant on large scale. So you can use any of them if input is not much bigger, but randint gets significantly slower than random.choice when size grows.

So, random.choice only calls _randbelow internally, which is implementation dependent.

random.randint on the other hand is a wrapper for random.randrange, which is made to find a number between a start and an end number. It checks a lot of stuff, as there are 2 numbers supplied and that can be troublesome if they are switched and stuff, furthermore it also uses _randbelow in the end, so its as good as random.choice in best case

TLDR; Same randomness, but choice should be minimally faster (almost indistinguishable) and is also more readable.

Source for my claims: Python Standard Library

It just comes down to preference. If you like option 1, then go for it, or vice versa.

But when it comes down to other people reading it, I would prefer you to use option 2. It just makes it very easy on me and others... You may also come back to it in a week or two and might just get stuck for a minute to see what you did.

Related