sys.intern() 是做什么的?什么时候应该使用它?

70
我遇到了关于字典内存管理的这个问题,它提到了intern函数。它确切地是做什么的?在什么情况下会使用它? 举个例子:如果我有一个名为seen的集合,其中包含形式为(string1,string2)的元组,我用它来检查重复项,那么存储(intern(string1),intern(string2))是否会提高内存或速度方面的性能?

我想在这里分享这篇文章,它提供了有关优化Python性能的一些有趣示例和概念。链接如下:https://levelup.gitconnected.com/optimization-in-python-the-interning-technique-for-improved-performance-3ff14d376176 - ivanleoncz
4个回答

95

来自 Python 3 文档:

sys.intern(string)

在“interned”字符串表中输入字符串并返回interned字符串——它本身或副本。将字符串interned是有用的,可以在字典查找时获得一点性能优化——如果字典中的键被interned,并且查找键也被interned,则在哈希后的键比较可以通过指针比较而不是字符串比较来完成。通常,在Python程序中使用的名称会自动interned,并且用于保存模块、类或实例属性的字典具有interned键。
Interned字符串并非不朽;您必须保留对intern()的返回值的引用才能从中受益。
澄清:
正如文档所建议的那样,sys.intern函数旨在用于性能优化。
sys.intern函数维护一个interned字符串表。当您尝试intern字符串时,该函数会在表中查找它并:
  1. 如果字符串不存在(尚未被内部化),该函数会将其保存在表中,并从内部化的字符串表中返回它。

    >>> import sys
    >>> a = sys.intern('why do pangolins dream of quiche')
    >>> a
    'why do pangolins dream of quiche'
    

    在上面的示例中,a 保存了内部化的字符串。即使它不可见,sys.intern 函数也已将 'why do pangolins dream of quiche' 字符串对象保存在内部化的字符串表中。

  2. 如果字符串存在(已经被内部化),该函数会从内部化的字符串表中返回它。

    >>> b = sys.intern('why do pangolins dream of quiche')
    >>> b
    'why do pangolins dream of quiche'
    

    即使它不是立即可见的,因为字符串 'why do pangolins dream of quiche' 已经被内部化,b 现在保存的是与 a 相同的字符串对象。

    >>> b is a
    True
    

    如果我们创建相同的字符串但没有使用内部化,我们最终会得到两个具有相同值的不同字符串对象。

    >>> c = 'why do pangolins dream of quiche'
    >>> c is a
    False
    >>> c is b
    False
    
通过使用 sys.intern,您可以确保不会创建两个具有相同值的字符串对象——当您请求创建一个与现有字符串对象具有相同值的第二个字符串对象时,您将收到对预先存在的字符串对象的引用。这样,您就可以节省内存。此外,字符串对象比较现在非常高效,因为它是通过比较两个字符串对象的内存地址而不是它们的内容来进行的。

3
如果我们在一个.py文件中编写代码并运行它,我们会得到c is a的结果为True。为什么会这样呢? - Shashank Singh
6
我的理解是,当 Python 文件编译成 PYC 文件时,它会在文件中创建一个常量列表,包括字符串等内容,而每次出现该内容时,都不会重新创建字符串,而是引用常量。因此,当文件被读取时,所有将字符串“why do ...”赋值的位置都指向同一个字符串实例。 - troutinator

24

实质上,intern 函数会在一个 interned 字符串集合中查找(或存储,如果不存在)该字符串,因此所有 interned 实例都将共享相同的标识。你可以通过一次查找字符串来换取更快的比较速度(只需检查标识即可返回 True,而不必比较每个字符),并减少内存使用。

然而,Python 会自动intern小型字符串或看起来像标识符的字符串,因此您可能会发现没有任何改进,因为您的字符串已经在后台进行了 intern 处理。例如:

>>> a = 'abc'; b = 'abc'
>>> a is b
True

过去,一个缺点是 interned 字符串是永久的。一旦 interned,即使所有引用都被删除,字符串内存也永远不会被释放。不过,我认为对于更近期的 Python 版本来说,这已经不再是问题了。

4
CPython 会自动将小字符串进行内部化处理 — 这是一种实现行为,不能保证所有实现都是如此(但很可能如此)。 - gsnedders
3
CPython会自动对小字符串进行内部化处理,但只有当它们是代码中的常量表达式时才会这样做,而不是在运行时创建的字符串。请参阅stackoverflow.com/questions/15541404/python-string-interning。 - mhsmith

10

他们没有谈论Python中的关键字intern,因为这个关键字在Python中并不存在。他们正在谈论非必要内置函数intern,该函数在py3k中已经被移动到sys.intern中。文档中有详尽的描述。


Python 2之后的正确intern()文档链接:https://docs.python.org/2/library/functions.html#intern - Mna

4

它返回字符串的规范实例。

因此,如果您有许多相等的字符串实例,则可以节省内存,并且还可以通过标识比较规范化的字符串而不是相等性,这样更快。


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