Dos cadenas de Python con los mismos caracteres, a == b, pueden compartir memoria, id (a) == id (b), o pueden estar en la memoria dos veces, id (a)! = Id (b). Trate ¿Cuándo asigna Python nueva memoria para cadenas idénticas?
ab = "ab"
print id(ab), id("a"+"b")
Aquí Python reconoce que la recién creada "a" + "b" es la misma como el "ab" ya en la memoria - no está mal.
Considere ahora una lista N-larga de nombres de estado ["Arizona", "Alaska", "Alaska", "California" ...] (N ~ 500000 en mi caso).
Veo 50 ID diferentes() s ⇒ cada cadena "Arizona" ... se almacena solo una vez, bien.
PERO escribe la lista en el disco y la vuelves a leer: la "misma" lista ahora tiene N id diferentes() s, forma más memoria, mira a continuación.
¿Cómo puede alguien explicar la asignación de memoria de cadena de 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
Agregado 25jan:
Hay dos tipos de cadenas en la memoria de Python (o cualquier programa de):
- Ustrings, en un Ucache de cadenas únicas: éstos ahorra memoria y hacen a = = b rápido si ambos están en Ucache
- Ostrings, los demás, que se pueden almacenar cualquier cantidad de veces.
intern(astring)
pone astring en el Ucache (Alex +1); aparte de eso, no sabemos nada sobre cómo Python mueve los Ostrings al Ucache - ¿cómo entró "a" + "b", después de "ab"? ("Cadenas de archivos" no tiene sentido, no hay forma de saberlo)
En resumen, los Ucaches (puede haber varios) permanecen turbios.
Una nota al pie de página histórica: SPITBOL uniquified all strings ca. 1970.
¿Hay algo de valor en mi respuesta que no piense que está cubierto en el suyo? Si no, borraré mi respuesta. Si la hay, ¿quieres editarla en la tuya y * entonces * borraré mi respuesta? –
+1 por mencionar 'interno'. Había olvidado por completo que esta función existía. Usar 'joinsplit = [interno (n) para n en" \ n ".join (nombres) .split()]' hizo el trabajo y redujo el uso de memoria de 4,374,528 a 3,190,783 en mi MacBook. –
@John, creo que tener los dos puntos de vista (el mío desde una "perspectiva interna", el tuyo de un programador experimentado sin una "perspectiva privilegiada" especial en Python) es valioso tal como está: no estoy seguro de que haya una forma óptima de obtener el la misma "triangulación" dentro de una sola respuesta! –