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

Eri*_*F89 9 python string reference immutability

我已经搜索了网络和堆栈溢出问题,但无法找到这个问题的答案.我所做的观察是,在Python 2.7.3中,如果为两个变量分配相同的单个字符串,例如

>>> a = 'a'
>>> b = 'a'
>>> c = ' '
>>> d = ' '
Run Code Online (Sandbox Code Playgroud)

然后变量将共享相同的引用:

>>> a is b
True
>>> c is d
True
Run Code Online (Sandbox Code Playgroud)

对于一些较长的字符串也是如此:

>>> a = 'abc'
>>> b = 'abc'
>>> a is b
True
>>> '  ' is '  '
True
>>> ' ' * 1 is ' ' * 1
True
Run Code Online (Sandbox Code Playgroud)

但是,在很多情况下,参考是(意外地)不共享:

>>> a = 'a c'
>>> b = 'a c'
>>> a is b
False
>>> c = '  '
>>> d = '  '
>>> c is d
False
>>> ' ' * 2 is ' ' * 2
False
Run Code Online (Sandbox Code Playgroud)

有人可以解释一下这个原因吗?

我怀疑解释器和/或某些缓存机制可能会进行简化/替换,这些机制利用了python字符串在某些特殊情况下不可变的事实,但我知道什么?我尝试使用str构造函数和copy.deepcopy函数制作字符串的深层副本,但字符串仍然不一致地共享引用.

我遇到这个问题的原因是因为我在为新式python类的克隆方法编写的一些单元测试中检查字符串引用的不等性.

Sve*_*ach 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
Run Code Online (Sandbox Code Playgroud)

请注意,这也会排除包含可变对象的元组.如果你想测试那些,你需要递归地下降到元组.


Lev*_*von 5

我认为这是一个实施和优化的事情。如果字符串很短,它们可以(并且经常?)“共享”,但你不能依赖于此。一旦你有了更长的字符串,你就会发现它们不一样。

In [2]: s1 = 'abc'
In [3]: s2 = 'abc'

In [4]: s1 is s2
Out[4]: True
Run Code Online (Sandbox Code Playgroud)

更长的字符串

In [5]: s1 = 'abc this is much longer'
In [6]: s2 = 'abc this is much longer'

In [7]: s1 is s2
Out[7]: False
Run Code Online (Sandbox Code Playgroud)

用于==比较字符串(而不是运算is符)。

--

OP 的观察/假设(在下面的评论中)认为这可能是由于代币数量所致,似乎得到以下支持:

In [12]: s1 = 'a b c'
In [13]: s2 = 'a b c'

In [14]: s1 is s2
Out[14]: False
Run Code Online (Sandbox Code Playgroud)

如果与上面的初始示例相比abc。


eca*_*mur 5

在CPython中,作为实现细节,空字符串是共享的,单字符字符串的代码点在Latin-1范围内.你应该不依赖于这一点,因为它有可能绕过此功能.

您可以要求将一个字符串实习使用sys.intern; 在某些情况下会自动发生:

通常,Python程序中使用的名称会自动实现,而用于保存模块,类或实例属性的字典具有实习键.

sys.intern 暴露,以便您可以使用它(在分析后!)的性能:

实习字符串对于在字典查找中获得一点性能很有用 - 如果字典中的键被中断,并且查找键被实现,则可以通过指针比较而不是字符串比较来完成键比较(在散列之后).

请注意,它intern是Python 2中内置的.