在哪种情况下,相同的字符串共享相同的引用?

时间:2012-07-23 11:35:28

标签: python string reference immutability

我搜索过网络并堆叠溢出的问题,但无法找到这个问题的答案。我所做的观察是,在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类的克隆方法编写的一些单元测试中检查字符串引用的不等性。

3 个答案:

答案 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的初始示例进行比较。