111

虽然这个问题在实践中没有任何实际用途,但我很好奇 Python 如何进行字符串实习。我注意到以下内容。

>>> "string" is "string"
True

这正如我所料。

你也可以这样做。

>>> "strin"+"g" is "string"
True

这很聪明!

但你不能这样做。

>>> s1 = "strin"
>>> s2 = "string"
>>> s1+"g" is s2
False

为什么 Python 不会评估s1+"g",并意识到它与相同s2并将其指向相同的地址?最后一个区块实际上发生了什么让它返回False

4

2 回答 2

110

这是特定于实现的,但您的解释器可能正在实习编译时常量,而不是运行时表达式的结果。

下面使用 CPython 3.9.0+。

在第二个示例中,表达式"strin"+"g"在编译时计算,并替换为"string". 这使得前两个示例的行为相同。

如果我们检查字节码,我们会发现它们完全相同:

  # s1 = "string"
  1           0 LOAD_CONST               0 ('string')
              2 STORE_NAME               0 (s1)

  # s2 = "strin" + "g"
  2           4 LOAD_CONST               0 ('string')
              6 STORE_NAME               1 (s2)

此字节码是通过以下方式获得的(在上面之后打印了几行):

import dis

source = 's1 = "string"\ns2 = "strin" + "g"'
code = compile(source, '', 'exec')
print(dis.dis(code))

第三个示例涉及运行时连接,其结果不会自动实习:

  # s3a = "strin"
  3           8 LOAD_CONST               1 ('strin')
             10 STORE_NAME               2 (s3a)

  # s3 = s3a + "g"
  4          12 LOAD_NAME                2 (s3a)
             14 LOAD_CONST               2 ('g')
             16 BINARY_ADD
             18 STORE_NAME               3 (s3)
             20 LOAD_CONST               3 (None)
             22 RETURN_VALUE

这个字节码是通过以下方式获得的(它在上面打印了几行,这些行与上面给出的第一个字节码块完全相同):

import dis

source = (
    's1 = "string"\n'
    's2 = "strin" + "g"\n'
    's3a = "strin"\n'
    's3 = s3a + "g"')
code = compile(source, '', 'exec')
print(dis.dis(code))

如果您要手动sys.intern()获取第三个表达式的结果,您将获得与以前相同的对象:

>>> import sys
>>> s3a = "strin"
>>> s3 = s3a + "g"
>>> s3 is "string"
False
>>> sys.intern(s3) is "string"
True

此外,Python 3.9 会为上面的最后两个语句打印警告:

SyntaxWarning: "is" 带有文字。你的意思是“==”吗?

于 2013-03-21T07:15:55.527 回答
3

情况1

>>> x = "123"  
>>> y = "123"  
>>> x == y  
True  
>>> x is y  
True  
>>> id(x)  
50986112  
>>> id(y)  
50986112  

案例2

>>> x = "12"
>>> y = "123"
>>> x = x + "3"
>>> x is y
False
>>> x == y
True

现在,您的问题是为什么 id 在 case 1 和 case 2 中相同。
在 case 1 中,您已将字符串文字分配"123"xand y

由于字符串是不可变的,解释器只存储一次字符串文字并将所有变量指向同一个对象是有意义的。
因此,您会看到 id 相同。

在情况 2 中,您正在x使用串联进行修改。两者xy具有相同的值,但不同的身份。
两者都指向内存中的不同对象。因此他们有不同的idis运营商返回False

于 2014-09-10T05:56:00.677 回答