Python如何知道两个字符串变量指向同一对象?

7

我是Python新手,不得不说Python对变量赋值和函数参数的处理方式非常令人困惑。以下是一些我不理解的内容。如果我用相同的内容明确定义两个字符串,比如'abc',那么它们实际上是同一个对象,如下所示。

x = 'abc'
y = 'abc'
x is y
True

这让我想到Python是如何知道它们是一样的。通过比较代码中的文字?如果在x ='abc'和y ='abc'之间发生了数百万件不同的事情,Python是否会返回并说已经有一个对象'abc',所以我不会创建一个新的'abc'?

我想知道如果我使用一个非常长而且复杂的字符串做同样的事情会发生什么。下面是结果。

x = 'nao;uh gahasjhd;fjkhag;sjdgfuiwgfashksghdfaihghehwq3473fsd_@'
y = 'nao;uh gahasjhd;fjkhag;sjdgfuiwgfashksghdfaihghehwq3473fsd_@'
x is y
False
x == y
True

现在你可以看出我为什么感到困惑了。那么当创建新的字符串对象时,Python只检查“简单”的字符串而不是长字符串?那么复杂/长的程度应该达到多少才算太复杂/长呢?

1
这些细节取决于具体的实现。Python允许实现共享任何已知为不可变的值的实例,但从不要求这样做,因此依赖于x is yx is not y在这里为真的任何代码都是错误的,或者最多也是不可移植的。如果您想了解特定实现(如CPython 3.7、CPython 2.7或PyPy 2.1/3.5)的内部实现细节,或者如果您想了解Python实现在这里可能使用的一般方法,那么这些确实是完全独立的问题。 - abarnert
1个回答

3

"x is y"和"x==y"有所不同。
x is y会检查x和y是否指向堆中的同一对象。
x == y将检查x和y的值是否相同。

现在让我们看看为什么你得到了两个不同的结果

如果值的长度很小(最多3到4位数),那么Python只会检查堆中是否存在另一个具有相同值的对象。如果存在,则不会创建新对象,如果不存在,则创建新对象。
如果值的长度很大(超过4位数),Python将创建新对象,而不检查是否已经存在具有相同值的对象。

当字符串、整数或浮点数的长度较小时

在Python中,如果两个变量具有相同的字符串、整数或浮点数值,并且值的长度较小,则两个变量都指向同一个对象,即在堆内存中只创建了一个对象。
通过以下示例进行尝试。

a=10
b=10
a is b
True     #output
a == b
True     #output  

这里的a is b检查a和b是否引用(即指向)堆中的同一对象。
由于10的长度为2,很小,Python解释器只会创建一个值为10的对象,因此a和b将引用同一对象,输出为True

a == b将检查a的值是否等于b的值。
由于a的值为10,b的值也为10,因此输出为True

您也可以尝试使用字符串值,例如

s1='abc'
s2='abc'
s1 is s2
True      #output
s1 == s2
True      #output  

当字符串、整数、浮点数很长时

现在,当字符串、整数、浮点数很长时,Python解释器不会检查堆中是否有相同值的对象,即使已经存在带有相同值的对象,它也会直接创建新对象。

x = 'nao;uh gahasjhd;fjkhag;sjdgfuiwgfashksghdfaihghehwq3473fsd_@'
y = 'nao;uh gahasjhd;fjkhag;sjdgfuiwgfashksghdfaihghehwq3473fsd_@'
x is y
False
x == y
True

由于 nao;uh gahasjhd;fjkhag;sjdgfuiwgfashksghdfaihghehwq3473fsd_@ 太长,Python 解释器不会检查堆中是否有另一个具有相同值的对象,它将创建一个对象并引用该对象作为 x
现在再次将 nao;uh gahasjhd;fjkhag;sjdgfuiwgfashksghdfaihghehwq3473fsd_@ 分配给 y,但是由于字符串长度较大,Python 解释器将创建新对象(即使该对象已存在)
现在,由于 xy 引用不同的对象,输出结果将为 False
由于 xy 具有相同的值,输出结果将为 True

您也可以尝试此示例

a = 10000
b = 10000
a is b
False      #output
a == b
True      #output  

为什么Python这样做

Python这样做是为了减少解释时间(即代码执行时间)。如果Python一直检查堆中是否已经存在那个长字符串(例如长度为10的字符串),它将需要更多时间,因为它将逐个字母地与所有对象进行比较。比较10个字符将消耗大量时间。
而如果字符串长度小于4,则很容易进行比较(因为只需要比较3个字母),并且不会花费太多时间。


感谢您抽出时间来解释。根据我得到的回复,我现在明白这是一个字符串内部化的问题。但是,一个字符串是否被内部化并不仅仅取决于它的长度是否小于等于4,还与字符串的类型、长度以及其他一些因素有关。正如其中一个答案指出的那样,这是实现特定的。 - Frank
没问题。重要的是你已经理解了。 - swapnil
字符串太长并不是原因...我仍然不知道... 我将在下面发布反例 如果您将所有特殊字符替换为字母或数字(在本例中为X),它们将成为相同的对象,即使字符串很长... x = 'naoXuhXgahasjhdXfjkhagXsjdgfuiwgfashksghdfaihghehwq3473fsdXX' y = 'naoXuhXgahasjhdXfjkhagXsjdgfuiwgfashksghdfaihghehwq3473fsdXX' print(x is y) # 输出True - famaral42

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接