优草派  >   Python

Python如何清理驻留的字符串?

孙悦            来源:优草派

Python 语言中经常遇到字符串类型的数据,但是字符串是不可变类型,即无法在原有内存空间上做修改,任何修改操作都会导致开辟新的内存空间。这也就是 Python 语言中所说的字符串驻留(interning)。那么本文将介绍如何清理驻留的字符串,以避免内存泄露。

Python如何清理驻留的字符串?

1. 什么是字符串驻留?

字符串驻留是指 Python 在编译、解释程序时,对于相同的字符串只在内存中保存一份的过程。这是由于 Python 对字符串类型进行了优化,使用了一种常量折叠的技术,将相同的常量折叠成同一个共享对象。这样做不仅可以节省内存,还能减少字符串之间的比较操作。

2. Python 中哪些字符串会驻留?

Python 对于长度小于等于等于 1 的字符串,默认进行驻留;对于换行符、空字符和空格字符,在一定情况下也会进行驻留处理。需要注意的是,在 Python 3.7 之后,只有大小写相同的字符串才会进行驻留(包括长度大于 1 的字符串),而在之前的版本中,任何相同的字符串都会进行驻留。

3. 如何避免字符串驻留带来的问题?

虽然字符串驻留机制在大多数情况下都能带来的好处,但是如果没有控制就会出现内存泄露的问题。假设我们有一个长时间运行的 Python 程序,程序使用了很多相同的字符串,由于字符串驻留的机制,这些相同的字符串都指向同一个对象,在一些特殊情况下,这些字符串可能无法被及时释放。这时候就需要手动禁止字符串驻留,在需要的时候使用 sys.intern() 方法进行驻留。

4. 如何清理驻留的字符串?

在 Python 中,由于字符串是不可变的,所以字符串的引用计数器是不会减少的,也就是说,Python 解释器将始终保留对字符串的引用,除非强制解除驻留时。

虽然 Python 提供了 sys.intern() 方法,可以对字符串进行驻留,但是在实际开发中,由于字符串驻留带来的风险,通常不会使用该方法。如果需要清理驻留的字符串,可以通过以下代码中的 gc 模块和 ctypes 模块来实现。具体实现可以参考以下代码:

code

def get_referents_cb(c:

return [r for r in gc.get_referents(c) if isinstance(r, str)]

strings = ['hello', 'world', 'python', 'hello', 'python', 'example']

interned = set(sys.intern(s) for s in strings)

tuples = [(s, id(s), id(sys.intern(s))) for s in strings]

for s in interned:

print(s)

for s, s_id, interned_id in tuples:

if s_id != interned_id:

print(s, ctypes.cast(s_id, ctypes.py_object).value, ctypes.cast(interned_id, ctypes.py_object).value)

这里首先创建了一组字符串,其中包含了多个相同的字符串,然后使用 sys.intern() 方法对这些字符串进行驻留,最后打印出驻留之后的字符串集合。在打印出字符串集合之后,循环遍历每一个字符串,将其引用打印出来,这里使用的是 ctypes.cast() 方法,将引用转化为 Python 对象,打印出对象的值和类型。如果有相同的字符串在不同内存地址上,则说明之前进行了驻留处理,可以使用 gc 模块将该字符串对象清理掉。

5. 总结

字符串驻留是 Python 中优化字符串计算和比较的一种方式。在大多数情况下,它能带来很多好处,但在某些情况下也会带来一些问题。为了避免内存泄露,我们可以通过禁止字符串驻留和清理驻留的字符串来解决这些问题。

【原创声明】凡注明“来源:优草派”的文章,系本站原创,任何单位或个人未经本站书面授权不得转载、链接、转贴或以其他方式复制发表。否则,本站将依法追究其法律责任。
TOP 10
  • 周排行
  • 月排行