[обновление], чтобы ответить на вопрос, необходимо знать, почему, как и когда Python повторно использовать строки.
Давайте начнем с, как: Python использует «интернированы» строки - от wikipedia:
В информатике строка интернирование представляет собой способ хранения только одной копии каждого отдельного значения строки, которые должны быть неизменны , Внутренние строки делают некоторые задачи обработки строк более экономными по времени или пространству за счет того, что требуется больше времени при создании или интернировании строки. Разные значения хранятся в пуле пула.
Почему? Похоже, что сохранение памяти не главная цель здесь, только хороший побочный эффект.
Строка интернирование ускоряет сравнение строк, которые иногда узким местом производительности в приложениях (таких как компиляторы и динамических сред выполнения языка программирования), которые в значительной степени полагаются на хэш-таблицы с строковыми ключами. Без интернирования проверка того, что две разные строки равны, включает в себя изучение каждого символа обеих строк. Это медленно по нескольким причинам: по своей сути это O (n) в длине строк; обычно требуется чтение из нескольких областей памяти, которые требуют времени; и чтение считывает кеш процессора, что означает, что для других нужд меньше кеша. С интернированными строками после первоначальной статической операции достаточно простого теста на идентификацию объекта; это обычно реализуется как тест равенства указателя, как правило, только одна машинная команда без ссылки на память.
String interning также уменьшает использование памяти, если существует множество экземпляров одного и того же строкового значения; например, он считывается из сети или из хранилища. Такие строки могут включать магические числа или информацию о сетевом протоколе.Например, синтаксические анализаторы XML могут ставить имена тегов и атрибутов для сохранения памяти.
Теперь «когда»: CPython «интерны» строка в следующих ситуациях:
- при использовании неосновной встроенной функции
intern()
(который переехал в sys.intern
в Python 3) ,
- небольшие строки (0 или 1 байты) - this very informative article by Laurent Luce объясняют об осуществлении
- имен, используемых в программах Python автоматически интернированы
- словарей, используемых для хранения модуля, атрибуты класса или экземпляра были интернированы ключи
В других ситуациях каждая реализация, похоже, имеет большое значение, когда строки автоматически интернированы.
я не мог поставить его лучше, чем Alex Martinelli сделал в this answer (не удивительно, что парень не имеет 245k репутации):
Каждая реализация языка Python вольна делать свои собственные компромиссы при распределении неизменных объектов (например, строки) - либо создание нового, либо поиск существующего равного, так и использование еще одной ссылки на него - отлично с точки зрения языка. На практике, конечно, реализация в реальном мире требует разумного компрометации: еще одна ссылка на подходящий существующий объект при поиске такого объекта дешева и проста, просто создайте новый объект, если задача поиска подходящего существующего (который может или может не существовать) выглядит так, как будто это может занять много времени.
Так, например, множественные вхождения одного и того же строкового литерала в пределах одной функции будут (во всех реализациях, которые я знаю) использовать стратегию «новая ссылка на тот же объект», потому что при создании константы-пула этой функции это довольно быстро и легко избежать дубликатов; но сделать это через отдельные функции потенциально может быть очень трудоемкой задачей, поэтому реалии реального мира либо не делают этого вообще, либо делают это только в некоторых эвристически определенных подмножествах дел, где можно надеяться на разумный компромисс между время компиляции (замедление путем поиска идентичных существующих констант) и потребление памяти (увеличивается, если сохраняются новые копии констант).
Я не знаю ни одной реализации Python (или, на самом деле, других языков с постоянными строками, таких как Java), которые берут на себя задачу идентифицировать возможные дубликаты (для повторного использования одного объекта через несколько ссылок) при чтении данных из файла - это просто не кажется многообещающим компромиссом (и здесь вы будете платить время выполнения, а не компилировать время, поэтому компромисс еще менее привлекателен). Конечно, если вы знаете (благодаря соображениям уровня приложения), что такие неизменные объекты большие и довольно подвержены многим дублированиям, вы можете легко реализовать собственную стратегию «константы-пул» (стажер может помочь вам сделать это для строк, но его нетрудно сворачивать, например, кортежи с неизменяемыми предметами, огромные длинные целые числа и т. д.).
[первоначальный ответ]
Это больше комментарий, чем ответ, но комментарий система не очень хорошо подходит для проводки кода:
def main():
while True:
s = raw_input('feed me:')
print '"{}".id = {}'.format(s, id(s))
if __name__ == '__main__':
main()
Бега это дает мне:
"test".id = 41898688
"test".id = 41900848
"test".id = 41898928
"test".id = 41898688
"test".id = 41900848
"test".id = 41898928
"test".id = 41898688
Из моего опыта, по крайней мере, 2,7, есть некоторая оптимизация, продолжающаяся даже для raw_input()
.
Если реализация использует хеш-таблицы, я думаю, что существует более одного. Собираемся погрузиться в источник прямо сейчас.
[первое обновление]
Похоже, мой эксперимент был испорчен:
def main():
storage = []
while True:
s = raw_input('feed me:')
print '"{}".id = {}'.format(s, id(s))
storage.append(s)
if __name__ == '__main__':
main()
Результат:
"test".id = 43274944
"test".id = 43277104
"test".id = 43487408
"test".id = 43487504
"test".id = 43487552
"test".id = 43487600
"test".id = 43487648
"test".id = 43487744
"test".id = 43487864
"test".id = 43487936
"test".id = 43487984
"test".id = 43488032
В своем answer to another question пользователь tzot предупреждает об объекте жизни:
Замечание: очень важно знать время жизни объектов в Python. Обратите внимание на следующую сессию:
Python 2.6.4 (r264:75706, Dec 26 2009, 01:03:10)
[GCC 4.3.4] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> a="a"
>>> b="b"
>>> print id(a+b), id(b+a)
134898720 134898720
>>> print (a+b) is (b+a)
False
Ваше мышление, что печатая идентификаторы двух отдельных выражений и отметив, что «они равны Ergo два выражения должны быть равны/эквивалент/то же самое» является неисправен. Одна строка вывода не обязательно означает, что все ее содержимое было создано и/или сосуществует в один и тот же момент времени.
Если вы хотите узнать, являются ли два объекта одним и тем же объектом, спросите Python напрямую (используя оператор is
).
@PauloScardine - Вероятно. Я не эксперт в источнике C - я время от времени занимаюсь spelunking :) – mgilson
Кажется, что cpython всегда ставит строки размером в 0-1 байт, независимо от того, как они создаются: 's = 'a'; x = 'ab'; g = x [0]; print id (s), id (g) ' –
@PauloScardine - Это неудивительно. Их всего 256, поэтому это не так много накладных расходов. Подобно тому, как это делается с помощью небольших ints. (От -5 до 255, я считаю). – mgilson