когда Python выделяет новую память для идентичных строк?

Две строки Python с теми же символами, == b, могут совместно использовать память, идентификатор (a) == идентификатор (b), или могут быть в памяти дважды, идентификаторе (a)! = идентификатор (b). Попробовать

ab = "ab"
print id( ab ), id( "a"+"b" )

Здесь Python распознает, что недавно созданный "a" + "b" уже совпадает с "ab" в памяти - не плохо.

Теперь считайте N-длинный-список имен состояния ["Аризоной", "Аляской", "Аляской", "Калифорнией"...] (N ~ 500000 в моем случае).
Я вижу, что 50 других идентификаторов () s ⇒ каждая строка "Аризона"... хранятся только однажды, прекрасные.
НО напишите список к диску и читайте, он въезжает задним ходом снова: "тот же" список теперь имеет другой идентификатор N () s, путь больше памяти, посмотрите ниже.

Каким образом - кто-либо может объяснить, что Python представляет выделение памяти в виде строки?

""" when does Python allocate new memory for identical strings ?
    ab = "ab"
    print id( ab ), id( "a"+"b" )  # same !
    list of N names from 50 states: 50 ids, mem ~ 4N + 50S, each string once
    but list > file > mem again: N ids, mem ~ N * (4 + S)
"""

from __future__ import division
from collections import defaultdict
from copy import copy
import cPickle
import random
import sys

states = dict(
AL = "Alabama",
AK = "Alaska",
AZ = "Arizona",
AR = "Arkansas",
CA = "California",
CO = "Colorado",
CT = "Connecticut",
DE = "Delaware",
FL = "Florida",
GA = "Georgia",
)

def nid(alist):
    """ nr distinct ids """
    return "%d ids  %d pickle len" % (
        len( set( map( id, alist ))),
        len( cPickle.dumps( alist, 0 )))  # rough est ?
# cf http://stackoverflow.com/questions/2117255/python-deep-getsizeof-list-with-contents

N = 10000
exec( "\n".join( sys.argv[1:] ))  # var=val ...
random.seed(1)

    # big list of random names of states --
names = []
for j in xrange(N):
    name = copy( random.choice( states.values() ))
    names.append(name)
print "%d strings in mem:  %s" % (N, nid(names) )  # 10 ids, even with copy()

    # list to a file, back again -- each string is allocated anew
joinsplit = "\n".join(names).split()  # same as > file > mem again
assert joinsplit == names
print "%d strings from a file:  %s" % (N, nid(joinsplit) )

# 10000 strings in mem:  10 ids  42149 pickle len  
# 10000 strings from a file:  10000 ids  188080 pickle len
# Python 2.6.4 mac ppc

Добавленный 25 января:
Существует два вида строк в памяти Python (или любая программа):

  • Ustrings, в Ucache уникальных строк: они сохраняют память и делают == b быстро, если оба находятся в Ucache
  • Ostrings, другие, которые могут быть сохранены любое количество раз.

intern(astring) помещает astring в Ucache (Alex +1); кроме этого мы не знаем ничего вообще о том, как Python перемещает Ostrings в Ucache - как сделал "a" + "b", входят, после "ab"? ("Строки из файлов" бессмысленны - нет никакого способа знать.)
Короче говоря, Ucaches (могут быть несколько), остаются темными.

Историческая сноска: SPITBOL uniquified все строки приблизительно 1970.

39
задан denis 25 January 2010 в 17:37
поделиться

4 ответа

Каждое реализация языка Python свободна сделать свои собственные компромиссы при выделении неизменных объектов (таких как строки) - либо создание нового, либо нахождение существующих равных и использующих еще одну ссылку Для этого просто отлично с точки зрения языка. На практике, конечно, реальная минимальная реализация удара разумного компромисса: еще одна ссылка на подходящий существующий объект при наличии такого объекта является дешевым и простым, просто сделайте новый объект, если задача обнаружения подходящего существующего (которая может или Может не существовать) Похоже, это может потенциально понадобиться долгое время.

Итак, например, несколько вхождений одинакового строкового литерала в пределах одной функции будут (во всех реализациях, которые я знаю), используйте стратегию «нового ссылки на тот же объект», потому что при построении константа константа функции это довольно быстро и легко избежать дубликатов; Но это по всему отдельными функциями [113751212] функции могут быть самой трудоемкой, поэтому реальные мировые реализации либо не делают это вообще, либо только сделать это в некотором эвристически идентифицированном подмножестве случаев, когда можно Надежда на разумный компромисс времени компиляции (замедляется путем поиска идентичных существующих констант) против потребления памяти (увеличено, если новые копии постоянных постоянно хранятся).

Я не знаю о какой-либо реализации Python (или в этом отношении других языков с постоянными строками, такими как Java), что требует проблемы определения возможных дубликатов (для повторного использования одного объекта с помощью нескольких ссылок) при чтении данных Файл - это просто не кажется многообещающим компромисс (и здесь вы платите время выполнения , а не С компиляции Время Time, поэтому компромисс еще менее привлекательна). Конечно, если вы знаете (благодаря соображениям уровня приложения), что такие неизменные объекты являются большие и довольно склонные к многим дубликациям, вы можете реализовать свою стратегию «констант-бассейн» довольно легко ( Стажер может помочь вам Сделайте это для струн, но не сложно катиться, например, кортежи с неизменными предметами, огромными длинными целыми числами и так далее).

40
ответ дан 27 November 2019 в 02:32
поделиться
x = 42
y = 42
x == y #True
x is y #True

В этом взаимодействии X и Y должны быть == (то же значение), но не является (тот же объект), потому что мы побежали два разных буквальные выражения. Потому что маленький целые числа и струны кэшируются и повторно используется , хотя, говорит нам, что они ссылка на один и тот же единственный объект.

на самом деле, если вы действительно хотите посмотреть Под капотом вы всегда можете спросить Python Сколько ссылок есть к объекту, используя getrefcount Функция в стандартном модуле SYS Возвращает ссылочный счет объекта. Такое поведение отражает одно из многих Способы Python оптимизирует свою модель для Скорость исполнения.

Обучение Python

3
ответ дан 27 November 2019 в 02:32
поделиться

Я сильно подозреваю, что здесь Python ведёт себя как многие другие языки - распознаёт строковые константы в исходном коде и использует для них общую таблицу, но не , применяя те же самые правила при динамическом создании строк. Это имеет смысл, так как внутри вашего исходного кода будет только конечный набор строк (хотя Python, конечно, позволяет вам динамически оценивать код), в то время как гораздо более вероятно, что вы будете создавать огромное количество строк в процессе работы вашей программы.

Этот процесс обычно называется interning - и на самом деле, судя по виду на этой странице , на языке Python он тоже называется interning.

17
ответ дан 27 November 2019 в 02:32
поделиться

Примечание: очень важно знать время жизни объектов на Питоне. Обратите внимание на следующий сеанс:

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

Ваше мнение, что, распечатывая идентификаторы двух отдельных выражений и отмечая "они равны, эти два выражения должны быть равны/эквивалентны/одно и то же", является ошибочным . Одна строка вывода не обязательно означает, что все ее содержимое было создано и/или сосуществовало в один и тот же момент времени.

Если вы хотите узнать, являются ли два объекта одним и тем же объектом, спросите напрямую у Python (используя оператор is).

13
ответ дан 27 November 2019 в 02:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: