【问题标题】:How does Python determine if two strings are identicalPython如何判断两个字符串是否相同
【发布时间】:2018-03-22 11:10:17
【问题描述】:

我试图了解 Python 字符串何时相同(也就是共享相同的内存位置)。但是在我的测试中,当两个相等的字符串变量共享相同的内存时,似乎没有明显的解释:

import sys
print(sys.version) # 3.4.3

# Example 1
s1 = "Hello"
s2 = "Hello"
print(id(s1) == id(s2)) # True

# Example 2
s1 = "Hello" * 3
s2 = "Hello" * 3
print(id(s1) == id(s2)) # True

# Example 3
i = 3
s1 = "Hello" * i
s2 = "Hello" * i
print(id(s1) == id(s2)) # False

# Example 4
s1 = "HelloHelloHelloHelloHello"
s2 = "HelloHelloHelloHelloHello"
print(id(s1) == id(s2)) # True

# Example 5
s1 = "Hello" * 5
s2 = "Hello" * 5
print(id(s1) == id(s2)) # False

字符串是不可变的,据我所知,Python 试图重用现有的不可变对象,方法是让其他变量指向它们,而不是在内存中创建具有相同值的新对象。

考虑到这一点,Example 1 返回True 似乎很明显。
(对我来说)Example 2 返回True 仍然很明显。

Example 3 返回False 对我来说并不明显——我在做的事情和Example 2 不一样吗?!?

我偶然发现了这个 SO 问题:
Why does comparing strings in Python using either '==' or 'is' sometimes produce a different result?

并通读http://guilload.com/python-string-interning/(虽然我可能没有完全理解),然后想——好吧,也许“实习”字符串取决于长度,所以我在Example 4 中使用了HelloHelloHelloHelloHello。结果是True

让我感到困惑的是,与Example 2 中的操作相同,只是使用更大的数字(但它实际上会返回与Example 4 相同的字符串) - 但是这次结果是False?!?

我真的不知道 Python 如何决定是否使用同一个内存对象,或者何时创建一个新对象。

有任何官方资料可以解释这种行为吗?

【问题讨论】:

标签: python string memory


【解决方案1】:

来自the link you posted

避免使用大的 .pyc 文件

那么为什么'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa' 不计算为True?你还记得你在所有包中遇到的 .pyc 文件吗?好吧,Python 字节码存储在这些文件中。如果有人写了这样的['foo!'] * 10**9,会发生什么?生成的 .pyc 文件会很大!为了避免这种现象,通过窥孔优化生成的序列如果长度超过 20 则被丢弃。

如果你有字符串"HelloHelloHelloHelloHello",Python 必须按原样存储它(要求解释器检测字符串中的重复模式以节省空间可能太多了)。然而,当涉及到可以在解析时计算的字符串值时,例如"Hello" * 5,Python 会评估这些值作为所谓的“窥孔优化”的一部分,这可以决定是否值得预先计算细绳。由于len("Hello" * 5) > 20,解释器保持原样以避免存储过多的长字符串。

编辑:

this question所示,您可以在peephole.c的源代码中查看这一点,函数fold_binops_on_constants,在接近尾声时您会看到:

// ...
} else if (size > 20) {
    Py_DECREF(newconst);
    return -1;
}

编辑 2:

实际上,该优化代码最近是 Python 3.7 的 moved to the AST optimizer,所以现在您必须查看 ast_opt.c,函数 fold_binop,它现在调用函数 safe_multiply,它检查字符串是否不超过MAX_STR_SIZEnewly defined as 4096。因此,下一个版本的限制似乎已大大提高。

【讨论】:

    【解决方案2】:

    在示例 2 中:

    # Example 2
    s1 = "Hello" * 3
    s2 = "Hello" * 3
    print(id(s1) == id(s2)) # True
    

    这里 s1 和 s2 的值是在编译时计算的。所以这将返回 true。

    在示例 3 中:

    # Example 3
    i = 3
    s1 = "Hello" * i
    s2 = "Hello" * i
    print(id(s1) == id(s2)) # False
    

    这里的 s1 和 s2 的值是在运行时计算的,结果不会自动实习,所以这将返回 false。这是为了通过在运行时本身创建 "HelloHelloHello" 字符串来避免过多的内存分配。

    如果您手动进行实习,它将返回 True

    i = 3
    s1 = "Hello" * i
    s2 = "Hello" * i
    print(id(intern(s1)) == id(intern(s2))) # True
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-05-14
      • 1970-01-01
      • 2013-12-18
      • 1970-01-01
      • 2010-11-21
      • 1970-01-01
      • 2015-05-25
      相关资源
      最近更新 更多