'{0}'.format() работает быстрее, чем str() и '{}'.format() с использованием IPython %timeit и другими способами с использованием чистого Python.

Так что это вещь CPython, не совсем уверен, что она ведет себя так же с другими реализациями.

Но '{0}'.format() быстрее, чем str() и '{}'.format(). Я публикую результаты с Python 3.5.2, но я попробовал это с Python 2.7.12, и тенденция та же.

%timeit q=['{0}'.format(i) for i in range(100, 100000, 100)]
%timeit q=[str(i) for i in range(100, 100000, 100)]
%timeit q=['{}'.format(i) for i in range(100, 100000, 100)]

1000 loops, best of 3: 231 µs per loop
1000 loops, best of 3: 298 µs per loop
1000 loops, best of 3: 434 µs per loop

Из документов на object.__str__(self)

Вызывается str(object) и встроенными функциями format() и print() для вычисления «неформального» или удобного для печати строкового представления объекта.

Итак, str() и format() вызывают один и тот же метод object.__str__(self), но откуда такая разница в скорости?

ОБНОВЛЕНИЕ, как отметили @StefanPochmann и @Leon в комментариях, они получают разные результаты. Я попытался запустить его с помощью python -m timeit "...", и они правы, потому что результаты такие:

$ python3 -m timeit "['{0}'.format(i) for i in range(100, 100000, 100)]"
1000 loops, best of 3: 441 usec per loop

$ python3 -m timeit "[str(i) for i in range(100, 100000, 100)]"
1000 loops, best of 3: 297 usec per loop

$ python3 -m timeit "['{}'.format(i) for i in range(100, 100000, 100)]"
1000 loops, best of 3: 420 usec per loop

Так что кажется, что IPython делает что-то странное...

НОВЫЙ ВОПРОС: какой предпочтительный способ преобразования объекта в str по скорости?


person vishes_shell    schedule 17.11.2016    source источник
comment
Чтобы быть быстрее, вы также можете попробовать использовать i.__str__() напрямую. Хотя str действительно правильный путь. Это недостаточно быстро для вас?   -  person Stefan Pochmann    schedule 17.11.2016


Ответы (1)


Тайминг IPython по какой-то причине просто сбивается (хотя при тестировании с более длинной строкой формата в разных ячейках он вел себя немного лучше). Может быть, выполнение в тех же ячейках неправильно, я действительно не знаю.

В любом случае, "{}" немного быстрее, чем "{pos}", который быстрее, чем "{name}", но все они медленнее, чем str.

str(val) — самый быстрый способ преобразовать объект в str; он напрямую вызывает объект __str__, если он существует, и возвращает результирующую строку. Другие, такие как format (или str.format), включают дополнительные накладные расходы из-за вызова дополнительной функции (самой format); обрабатывает любые аргументы, анализирует строку формата и затем вызывает __str__ их args.

Для str.format методов "{}" используется автоматическая нумерация; из небольшого раздела в документах по синтаксису формата:

Изменено в версии 3.1: спецификаторы позиционных аргументов могут быть опущены, поэтому '{} {}' эквивалентно '{0} {1}'.

то есть, если вы предоставите строку вида:

"{}{}{}".format(1, 2, 3)

CPython сразу знает, что это эквивалентно:

"{0}{1}{2}".format(1, 2, 3)

Со строкой формата, содержащей числа, обозначающие позиции; CPython не может принять строго увеличивающееся число (начинающееся с 0) и должен анализировать каждую скобку, чтобы получить правильное значение, что немного замедляет процесс:

"{1}{2}{0}".format(1, 2, 3)

Вот почему также не разрешается смешивать эти два вместе:

"{1}{}{2}".format(1, 2, 3)

вы получите хороший ответ ValueError, когда попытаетесь это сделать:

ValueError: cannot switch from automatic field numbering to manual field specification

он также захватывает эти позиции с PySequence_GetItem, которые Я почти уверен, что это быстро, по крайней мере, по сравнению с PyObject_GetItem [см. далее].

Для значений "{name}" у CPython всегда есть дополнительная работа из-за того, что мы имеем дело с аргументами ключевого слова, а не с позиционными; это включает в себя такие вещи, как создание словаря для вызовов и создание дополнительных LOAD инструкций байт-кода для загрузки keys и значений. Форма вызова функции с использованием ключевого слова всегда приводит к некоторым накладным расходам. Кроме того, кажется, что захват фактически использует PyObject_GetItem< /a>, что влечет за собой дополнительные накладные расходы из-за его общего характера.

person Dimitris Fasarakis Hilliard    schedule 17.11.2016