?

Log in

No account? Create an account

Форматирование строк в питое. Удивление. - Узором созвездий по мантии ночи

27.03.2013, Среда

21:59:00 - Форматирование строк в питое. Удивление.

Previous Entry Поделиться Next Entry

Задумавшись вдруг, а какой тип подстановки данных в строку использовать, старый (через %) или новый (через str.format) попробовал сравнение для простых случаев.


>>> from timeit import timeit as ti
>>> ti("'%s as `%s`' % ('1','b')") / ti("'{0} as `{1}`'.format('1','b')")
0.52343018570438449
>>> ti("'%d as `%s`' % (1,'b')") / ti("'{0} as `{1}`'.format('1','b')")
1.7450242496291981
>>> ti("'%d as `%s`' % (1,'b')") / ti("'{0} as `{1}`'.format(1,'b')")
1.4912612933641076
>>> ti("'%r as `%s`' % (1,'b')") / ti("'{0} as `{1}`'.format(1,'b')")
0.5603779016127246
>>> ti("'%d as `%s`' % (1,'b')") / ti("'{0} as `{1}`'.format(1,'b')")
1.512150710318646
>>> ti("'%s as `%s`' % (1,'b')") / ti("'{0} as `{1}`'.format(1,'b')")
0.54302277029435786
>>> ti("'{0:d} as `{1}`'.format(1,'b')") / ti("'{0} as `{1}`'.format(1,'b')")
1.0985406776607249
>>> ti("'%d as `%s`' % (1,'b')") / ti("'%s as `%s`' % (1,'b')")
2.745502767603925
>>> ti("'%s as `%s`' % v", "v=('1','b')") / ti("'{0[0]} as `{0[1]}`'.format(v)", "v=('1','b')")
0.29668838868224023

Что получается:

1) Для подставновки строк старое форматирование быстрее нового почти в 2 раза.
1а) А если строки уже собраны в кортеж или список, то более чем в 3 раза.

2) Для обоих способов для чисел явное указание числового формата (без уточнений, просто букву 'd' или 'f') работает дольше, чем указание строкового, вызывающее автоматическое приведение числового аргумента к строке.
2a) Но для старого форматирования разница огромна, а для нового -- неочень.

Была надежда, что как-то новый формат оправдается, когда за значениями для подстановки надо залазить куда-то глубоко, причём часть пути совпадает:

>>> aa = A()
>>> aa.val = {1: ('a', 'b', 'c')}
>>> ti("'`%s`, `%s`, `%s`' % (aa.val[1][1], aa.val[1][0], aa.val[1][2])", "from __main__ import aa") / ti("'`{0[1]}`, `{0[0]}`, `{0[2]}`'.format(aa.val[1])", "from __main__ import aa")
0.6678787220617175

Не вышло. Видимо, в случае старого форматирования питон соптимизировал три подряд обращения к aa.val[1].

>>> def aaf(n):
return aa.val[n]
>>> ti("'`%s`, `%s`, `%s`' % (aaf(1)[1], aaf(1)[0], aaf(1)[2])", "from __main__ import aaf") / ti("'`{0[1]}`, `{0[0]}`, `{0[2]}`'.format(aaf(1))", "from __main__ import aaf")
0.85738243476836973

То ли питон и тут определил, что результат вызова aaf(1) можно закешировать и не звать его трижды, то ли экономия двух вызовов aaf() (внутри которой -- обращение за атрибутом класса, а потом за значением из словаря) не способна полностью окупть потери скорости нового форматирования. В итоге, одно из удобств нового форматирования -- то, что любой из параметров можно использовать неоднократно -- так же не даёт большого выигрыша (хотя проигрыш уже заметно меньше, чем в 2 раза).

Да, я понимаю, что наверняка найдутся случаи, когда старый синтаксис форматирования просто не даст нужного результата. Или, быть может, породит решающим образом более нечитабельный код, чем новое форматирование. Но в целом вывод получается такой, что лучше использовать %-форматирование, пока нет явной потрбности в str.format.

И с выводом чисел, если нет ни одного из условий:
1) требуется явно задать формат вывода числа (десятичные знаки и прочее),
2) требуется получить Exception, если в параметре вдруг оказалось не число,
3) хочется, чтобы причтении кода строка формата поясняла/напоминала, что ожидается от аргументов
-- то лучше ставить %s (ну или не упоминать числовой формат в случае нового форматирования), и питон автоматически приобразует число к строке быстрее, чем по явному указанию.

Ну а теперь, кто тут опытне меня в питоне, скажите, пожалуйста, что я упустил?


P.S.: Критерий "кому как больше по вкусу" сознательно не рассматривался.

This entry was originally posted at http://arilou.dreamwidth.org/904415.html. Please comment there using OpenID.