两个具有相同字符的Python字符串,a == b, 可能共享内存,即id(a) == id(b), 也可能在内存中出现两次,即id(a) != id(b)。 尝试
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() 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 https://dev59.com/vXI95IYBdhLWcg3w3h_G
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
添加于1月25日:
在Python(或任何程序的)内存中有两种字符串:
- Ustrings,保存在Ucache中的唯一字符串:这样可以节省内存,并且如果a和b都在Ucache中,使用==比较会很快
- Ostrings,其他的字符串,可能被存储多次。
intern(astring)
将astring放入Ucache中(Alex +1);
除此之外,我们对Python如何将Ostrings移动到Ucache几乎一无所知 -
"ab"之后如何进入"a"+"b"?
("来自文件的字符串"毫无意义 - 没有办法知道。)
简而言之,Ucache(可能有几个)仍然是不清楚的。
历史注释: SPITBOL编译器于1970年左右唯一化所有字符串。
intern
在Python 3.4中已经不再使用。您提到可以“自己编写”,但我不确定如何做到这一点... - maxintern
在sys
模块中:https://docs.python.org/3/library/sys.html。 一般来说,您可以建立一个包含您喜欢的类型对象(例如字典)的数据结构,并执行与intern相同的操作:建立存储/查找方法,该方法将字典中的键作为引用返回。 - nealmcb