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

Python 在哪些情况下,相等的字符串共享相同的引用?,python,string,reference,immutability,Python,String,Reference,Immutability,我已经搜索了网页和堆栈溢出问题,但找不到这个问题的答案。我观察到,在Python2.7.3中,如果将两个变量指定为相同的单个字符串,例如 >>> a = 'a' >>> b = 'a' >>> c = ' ' >>> d = ' ' 然后变量将共享相同的引用: >>> a is b True >>> c is d True 对于某些较长的字符串也是如此: >>> a

我已经搜索了网页和堆栈溢出问题,但找不到这个问题的答案。我观察到,在Python2.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类的克隆方法编写的一些单元测试中检查了字符串引用的不平等性。

缓存和重用字符串的时间细节取决于实现,可以从python版本更改为python版本,不能依赖。如果要检查字符串是否相等,请使用
=
,而不是
is

在CPython(最常用的Python实现)中,源代码中出现的字符串文本始终是内部的,因此,如果相同的字符串文本在源代码中出现两次,它们最终将指向相同的字符串对象。在Python2.x中,您还可以调用内置函数强制插入特定字符串,但实际上不应该这样做

编辑关于检查实例之间是否不当共享属性的实际目的:这种检查仅对可变对象有用。对于不可变类型的属性,共享对象和非共享对象之间没有语义差异。您可以使用

Immutable = basestring, tuple, numbers.Number, frozenset
# ...
if not isinstance(x, Immutable):    # Exclude types known to be immutable

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

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

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
使用
==
比较字符串(而不是
运算符)

--

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

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

In [14]: s1 is s2
Out[14]: False

如果与上面CPython中的
abc
的初始示例进行比较,作为实现细节,代码点在拉丁-1范围内的单个字符串也是如此。您不应该依赖于此,因为可以绕过此功能

您可以使用请求一个字符串进行实习;在某些情况下,这将自动发生:

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

sys.intern
已公开,以便您可以(在分析之后!)使用它来提高性能:

插入字符串有助于提高字典查找的性能–如果字典中的键被插入,并且查找键被插入,则可以通过指针比较而不是字符串比较来进行键比较(哈希后)


请注意,这是Python 2中的一个内置项。

对我来说,字符串中的标记数(由字符串或其他符号分隔的字母数字序列)似乎很重要:in[1]s1='abcthisismuchlone'in[2]s2='abcthisismuchlone'in[3]s1中的'abcthisismuchlone'是s2 Out[3]如果有多个标记,字符串未共享。@EriF89有可能,我从未真正研究过如何实现此功能的细节,我只是尽量记住在比较字符串时使用
==
,而不依赖于共享的“相同”字符串之间的存储。不过,您关于如何决定这一点的假设是有道理的,我刚刚尝试了
'abc'
,结果为
False
。感谢您的回答,您似乎正确地认为源代码中的字符串文字总是被插入的(非文字的字符串没有此属性)。因此,我的示例只有在python交互式解释器中编写时才有效。我用的是CPython,你的回答证实了我的一些怀疑。我同意应使用==运算符检查字符串是否相等。有什么优雅的方法可以检查属性初始化的位置吗?或者我应该避免这种测试吗?@EriF89:检查“属性初始化的位置”意味着什么?这又有什么关系呢?因为我正在编写测试以确保属性引用不会在克隆对象之间不正确地共享,所以我想要一种通用的方法来检查其中一个对象中的更改不会泄漏到另一个对象。我原以为is运算符会这样做,但由于引用是共享的,我想我必须为其中一个对象的属性指定新值,并检查它们在另一个对象中是否没有更改。@EriF89:如果您认为需要这种测试,请将其限制为可变类型。如果属性属于已知不可变的某种类型(例如
基串
元组
数字.Number
冻结集
),则检查项目是否共享没有意义。没关系。相关源代码链接+1。有趣的是,虽然关于优化字典查找的实习字符串的细节目前与我无关。