我搜索过网络并堆叠溢出的问题,但无法找到这个问题的答案。我所做的观察是,在Python 2.7.3中,如果为两个变量分配相同的单个字符串,例如
>>> a = 'a'
>>> b = 'a'
>>> c = ' '
>>> d = ' '
然后变量将共享相同的引用:
>>> a is b
True
>>> c is d
True
对于一些较长的字符串也是如此:
>>> a = 'abc'
>>> b = 'abc'
>>> a is b
True
>>> ' ' is ' '
True
>>> ' ' * 1 is ' ' * 1
True
然而,在很多情况下,参考是(意外地)不共享:
>>> a = 'a c'
>>> b = 'a c'
>>> a is b
False
>>> c = ' '
>>> d = ' '
>>> c is d
False
>>> ' ' * 2 is ' ' * 2
False
有人可以解释一下这个原因吗?
我怀疑解释器和/或某些缓存机制可能会进行简化/替换,这些机制利用python字符串在某些特殊情况下不可变的事实,但我知道什么?我尝试使用str构造函数和copy.deepcopy函数制作字符串的深层副本,但字符串仍然不一致地共享引用。
我遇到问题的原因是因为我在为新式python类的克隆方法编写的一些单元测试中检查字符串引用的不等性。
答案 0 :(得分:8)
何时缓存和重用字符串的细节依赖于实现,可以从Python版本更改为Python版本,并且不能依赖。如果要检查字符串是否相等,请使用==
,而不是is
。
在CPython(最常用的Python实现)中,源代码中出现的字符串文字总是被实现,所以如果在源代码中出现两次相同的字符串文字,它们最终会指向同一个字符串对象。在Python 2.x中,您还可以调用内置函数intern()
来强制特定字符串被强制执行,但实际上您不应该这样做。
编辑关于检查属性是否在实例之间不正确共享的实际目的:这种检查仅对可变对象有用。对于不可变类型的属性,共享和非共享对象之间没有语义差异。您可以使用
从测试中排除不可变类型Immutable = basestring, tuple, numbers.Number, frozenset
# ...
if not isinstance(x, Immutable): # Exclude types known to be immutable
请注意,这也会排除包含可变对象的元组。如果你想测试它们,你需要递归地下降到元组。
答案 1 :(得分:5)
在CPython中,作为实现细节the empty string is shared,其代码点位于Latin-1范围内的单字符字符串也是如此。您应该不依赖于此,因为可以绕过此功能。
您可以使用sys.intern
请求实习字符串;在某些情况下会自动发生:
公开了通常,Python程序中使用的名称会自动实现,而用于保存模块,类或实例属性的字典具有实习键。
sys.intern
,以便您可以使用它(在分析之后!)以获得性能:
实习字符串对于在字典查找中获得一点性能很有用 - 如果字典中的键被中断,并且查找键被中断,则可以通过指针比较而不是字符串来完成键比较(在散列之后)相比。
请注意,intern
是Python 2中内置的。
答案 2 :(得分:4)
我认为这是一个实现和优化的事情。如果字符串很短,它们可以(通常是?)“共享”,但你不能依赖它。一旦你有更长的字符串,你就会发现它们不一样。
In [2]: s1 = 'abc'
In [3]: s2 = 'abc'
In [4]: s1 is s2
Out[4]: True
更长的字符串
In [5]: s1 = 'abc this is much longer'
In [6]: s2 = 'abc this is much longer'
In [7]: s1 is s2
Out[7]: False
使用==
来比较字符串(不 is
运算符)。
-
OP的观察/假设(在下面的评论中)这可能是由于令牌的数量似乎得到以下支持:
In [12]: s1 = 'a b c'
In [13]: s2 = 'a b c'
In [14]: s1 is s2
Out[14]: False
如果与上面abc
的初始示例进行比较。