Str в python

При профилировании фрагмента кода python (python 2.6 до 3.2) я обнаружил, что str метод преобразования объекта (в моем случае целое число) в строку почти на порядок медленнее, чем использование форматирования строк.

Вот эталон

>>> from timeit import Timer
>>> Timer('str(100000)').timeit()
0.3145311339386332
>>> Timer('"%s"%100000').timeit()
0.03803517023435887

Кто-нибудь знает, почему это так? Я что-то пропустил?

Ответ 1

'%s' % 100000 оценивается компилятором и эквивалентен константе во время выполнения.

>>> import dis
>>> dis.dis(lambda: str(100000))
  8           0 LOAD_GLOBAL              0 (str)
              3 LOAD_CONST               1 (100000)
              6 CALL_FUNCTION            1
              9 RETURN_VALUE        
>>> dis.dis(lambda: '%s' % 100000)
  9           0 LOAD_CONST               3 ('100000')
              3 RETURN_VALUE        

% с выражением времени выполнения не (значительно) быстрее, чем str:

>>> Timer('str(x)', 'x=100').timeit()
0.25641703605651855
>>> Timer('"%s" % x', 'x=100').timeit()
0.2169809341430664

Заметьте, что str все еще немного медленнее, как сказал @DietrichEpp, это связано с тем, что str включает операции поиска и вызова функций, а % скомпилируется в один немедленный байт-код:

>>> dis.dis(lambda x: str(x))
  9           0 LOAD_GLOBAL              0 (str)
              3 LOAD_FAST                0 (x)
              6 CALL_FUNCTION            1
              9 RETURN_VALUE        
>>> dis.dis(lambda x: '%s' % x)
 10           0 LOAD_CONST               1 ('%s')
              3 LOAD_FAST                0 (x)
              6 BINARY_MODULO       
              7 RETURN_VALUE        

Конечно, вышеизложенное верно для тестируемой системы (CPython 2.7); другие реализации могут отличаться.

Ответ 2

Одной из причин, по которым приходит в голову, является то, что str(100000) включает глобальный поиск, но "%s"%100000 нет. Глобальный масштаб str необходимо искать в глобальной области. Это не объясняет всю разницу:

>>> Timer('str(100000)').timeit()
0.2941889762878418
>>> Timer('x(100000)', 'x=str').timeit()
0.24904918670654297

Как отмечено thg435,

>>> Timer('"%s"%100000',).timeit()
0.034214019775390625
>>> Timer('"%s"%x','x=100000').timeit()
0.2940788269042969