Подтвердить что ты не робот

Почему repr (int) быстрее, чем str (int)?

Мне интересно, почему repr(int) быстрее, чем str(int). С помощью следующего фрагмента кода:

ROUNDS = 10000

def concat_strings_str():
    return ''.join(map(str, range(ROUNDS)))

def concat_strings_repr():
    return ''.join(map(repr, range(ROUNDS)))

%timeit concat_strings_str()
%timeit concat_strings_repr()

Я получаю эти тайминги (python 3.5.2, но очень похожие результаты с 2.7.12):

 1.9 ms ± 17.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
 1.38 ms ± 9.07 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Если я нахожусь на правильном пути, ту же самую функцию long_to_decimal_string вызывается под капотом.

Я получил что-то не так, или что еще происходит, что мне не хватает?


Обновление: Вероятно, это не имеет ничего общего с методами int __repr__ или __str__, но с различиями между repr() и str(), поскольку int.__str__ и int.__repr__ на самом деле сравнительно быстро:

def concat_strings_str():
    return ''.join([one.__str__() for one in range(ROUNDS)])

def concat_strings_repr():
    return ''.join([one.__repr__() for one in range(ROUNDS)])

%timeit concat_strings_str()
%timeit concat_strings_repr()

приводит к:

2.02 ms ± 24.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.05 ms ± 7.07 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
4b9b3361

Ответ 1

Поскольку использование str(obj) должно сначала пройти через type.__call__, затем str.__new__ (создать новую строку), затем PyObject_Str (сделать строку из объекта), которая вызывает int.__str__ и, наконец, использует связанную вами функцию.

repr(obj), что соответствует builtin_repr, прямо вызывает PyObject_Repr (получить объект rep), который затем вызывает int.__repr__, который использует ту же функцию, что и int.__str__.

Кроме того, путь, который они принимают через call_function (функция, которая обрабатывает call_function код операции, сгенерированный для вызовов) немного отличается.

От ведущей ветки на GitHub (CPython 3.7):

  • str проходит через _PyObject_FastCallKeywords (это тот, который вызывает type.__call__). Помимо выполнения дополнительных проверок, также необходимо создать кортеж для хранения позиционных аргументов (см. _PyStack_AsTuple).
  • repr проходит через _PyCFunction_FastCallKeywords, который вызывает _PyMethodDef_RawFastCallKeywords. repr также повезло, поскольку, поскольку он принимает только один аргумент (коммутатор приводит его к событию METH_0 в _PyMethodDef_RawFastCallKeywords), нет необходимости создавать кортеж, просто индексирование аргументов.

Как говорится в вашем обновлении, это не о int.__repr__ vs int.__str__, они все равно являются одной и той же функцией; все это о том, как достичь repr и str. str просто нужно работать немного сложнее.

Ответ 2

Я просто сравнил реализации str и repr в ветки 3.5. См. здесь.

Кажется, что в str больше проверок: введите описание изображения здесь

Ответ 3

Существует несколько возможностей, потому что функции CPython отвечают за str и repr несколько отличается.

Но я предполагаю, что основная причина заключается в том, что str - это type (класс), а метод str.__new__ должен вызовите __str__, а repr может перейти непосредственно к __repr__.