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

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

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

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

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

Почему? Кто-нибудь может объяснить распределение памяти строк в 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 уникальных строк: они экономят память и делают a == b быстрым, если оба находятся в UcacheОстрины, остальные, которые могут храниться любое количество раз.

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

Историческая сноска:SPITBOL Uniquified все струны ок. 1970.

Ответы на вопрос(5)

Ваш ответ на вопрос