具有相同字符的两个Python字符串,a == b, 可以共享内存,id(a)== id(b), 或者可能在内存中两次,id(a)!= id(b)。 尝试Python何时为相同的字符串分配新内存?
ab = "ab"
print id(ab), id("a"+"b")
这里的Python认识到新创建的“a” +“b”的相同 为“AB”已在内存中 - 不坏。
现在考虑一个N长的州名称列表 [“Arizona”,“Alaska”,“Alaska”,“California”...] (在我的情况下,N〜500000)。
我看到50个不同的id()s ⇒每个字符串“Arizona”...只存储一次,很好。
但是将列表写入磁盘并再次读回: “相同”列表现在具有N个不同的id(),方式更多的内存,请参见下文。
怎么回事 - 任何人都可以解释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
新增25jan:
在Python内存2种串(或任何程序):
- Ustrings,在唯一的字符串的Ucache:这些节省内存,并做出= = b快,如果两者都在Ucache中
- Ostrings,其他可能会被存储多次。
intern(astring)
将astring放在Ucache(Alex +1)中; 除了我们什么都不知道Python如何将Ostrings移到Ucache - “ab”之后“a”+“b”是如何进入的? (“文件中的字符串”没有意义 - 没有办法知道。)
简而言之,Ucaches(可能有几个)仍然不明朗。
一个历史注脚: SPITBOL 独特的所有字符串约。 1970年
我的答案中有什么有价值的东西,你不认为是你的?如果不是,我会删除我的答案。如果有的话,你想编辑它到你的和*然后*我会删除我的答案? – 2010-01-23 17:54:23
+1提到'实习生'。我完全忘记了这个功能的存在。在\ n“.join(names).split()]中使用'joinsplit = [intern(n)'来完成这项工作,并将我的MacBook上的内存使用量从4,374,528降低到3,190,783。 – 2010-01-23 18:20:28
@John,我认为有两个观点(我从一个“内幕人士的角度来看”,你是一位经验丰富的程序员,对Python没有特别的“内幕人士的观点”)是非常有价值的 - 不确定是否有最佳的方式来获得同一个“三角测量”在一个单一的答案! – 2010-01-23 18:36:33