两个具有相同字符a == b的Python字符串可能共享内存id(a)== id(b),或者可能在内存中两次,id(a)!= id(b)。尝试
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(),增加了内存,请参见下文。
怎么了-有人能解释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内存(或任何程序的)中有两种字符串:
intern(astring)将字符串放入Ucache(Alex +1);除此之外,我们对Python如何将Ostrings移到Ucache一无所知-在“ ab”之后,“ a” +“ b”是如何进入的?(“文件中的字符串”是没有意义的-无法知道。) 简而言之,Ucache(可能有多个)仍然模糊。
intern(astring)
历史脚注: SPITBOL 统一所有字符串ca。1970年。
在分配不可变对象(例如字符串)时,Python语言的每种实现都可以自由地做出自己的取舍-制作一个新对象,或者找到一个相等的对象,并使用一个以上的引用,从该语言的角度来看都很好观点看法。当然,在实践中,现实世界中的实现会做出合理的折衷:在定位这样的对象时,再引用一个合适的现有对象既便宜又容易,如果要找到合适的现有对象(可能会可能不存在),看起来可能需要很长时间才能搜索到。
因此,例如,在一个函数中多次出现相同字符串文字(在我所知道的所有实现中)将使用“对同一对象的新引用”策略,因为在构建该函数的常量池时,它非常容易快捷避免重复;但是跨单独的功能执行此操作可能是一项非常耗时的任务,因此,现实世界中的实现要么根本不执行此操作,要么仅在某些启发式确定的情况子集中执行此操作,而这些子集可以希望我们合理地权衡编译时间(通过搜索相同的现有常量而降低)与内存消耗(如果不断制作新的常量副本会增加内存消耗)。
我不知道Python的任何实现(或与此有关的其他具有常量字符串的语言,例如Java)在从文件中读取数据时会麻烦识别可能的重复项(以通过多个引用重用单个对象)的麻烦- -这似乎不是一个有希望的折衷办法(这里您要付出运行时的费用,而不是编译时的费用,所以这种折衷的吸引力就更小了)。当然,如果你知道(得益于应用层面的考虑),这样的不可变对象是大,非常容易出现很多重复,你可以很容易地实现自己的“常量池”战略(实习生可以帮助您为字符串做,但不难为自己而生,例如具有不可变项的元组,巨大的长整数,