Выполнение выбора против randint

Я хочу выбрать случайное целое число между a и b, включительно.

Я знаю 3 способа сделать это. Однако их производительность кажется очень противоречивой:

import timeit

t1 = timeit.timeit("n=random.randint(0, 2)", setup="import random", number=100000)
t2 = timeit.timeit("n=random.choice([0, 1, 2])", setup="import random", number=100000)
t3 = timeit.timeit("n=random.choice(ar)", setup="import random; ar = [0, 1, 2]", number=100000)

[print(t) for t in [t1, t2, t3]]

На моей машине это дает:

0.29744589625620965
0.19716156798482648
0.17500512311108346

Используя онлайн-интерпретатор , это дает:

0.23830216699570883
0.16536146598809864
0.15081614299560897

Обратите внимание, что самая прямая версия (# 1), использующая выделенную функцию для выполнения того, что я делаю, на 50% хуже, чем самая странная версия (# 3), которая предварительно определяет массив и затем выбирает случайным образом из него.

Что происходит?

Ответ 1

Это просто детали реализации. randint делегирует randrange, поэтому он имеет другой слой служебных вызовов функции, а randrange проходит множество проверок аргументов и другой crud. Напротив, choice - действительно простой однострочный.

Здесь для этого вызова проходит путь кода randint с комментариями и неиспользованным кодом:

def randint(self, a, b):
    return self.randrange(a, b+1)

def randrange(self, start, stop=None, step=1, _int=int, _maxwidth=1L<<BPF):
    istart = _int(start)
    if istart != start:
        # not executed
    if stop is None:
        # not executed

    istop = _int(stop)
    if istop != stop:
        # not executed
    width = istop - istart
    if step == 1 and width > 0:
        if width >= _maxwidth:
            # not executed
        return _int(istart + _int(self.random()*width))

И здесь проходит путь кода choice:

def choice(self, seq):
    return seq[int(self.random() * len(seq))]