字符串切片是否在内存中执行复制?

Fre*_*red 40 python python-3.x

我想知道是否:

a = "abcdef"
b = "def"
if a[3:] == b:
    print("something")
Run Code Online (Sandbox Code Playgroud)

实际上是否执行了a内存中某处“def”部分的副本,或者是否就地完成了字母检查?

注意:我说的是一个字符串,而不是一个列表(我知道答案)

wim*_*wim 39

字符串切片在 CPython 中进行复制。

查看源代码,此操作在unicodeobject.c:unicode_subscript. 当 step 为 1,start 为 0,并且字符串的整个内容被切片时,显然有一种特殊情况可以重用内存 - 这会进入unicode_result_unchanged并且不会有副本。但是,一般情况下PyUnicode_Substring,所有道路都通向memcpy.

要凭经验验证这些说法,您可以使用 stdlib 内存分析工具tracemalloc

# s.py
import tracemalloc

tracemalloc.start()
before = tracemalloc.take_snapshot()
a = "." * 7 * 1024**2  # 7 MB of .....   # line 6, first alloc
b = a[1:]                                # line 7, second alloc
after = tracemalloc.take_snapshot()

for stat in after.compare_to(before, 'lineno')[:2]:
    print(stat)
Run Code Online (Sandbox Code Playgroud)

您应该会看到前两个统计信息输出,如下所示:

/tmp/s.py:6: size=7168 KiB (+7168 KiB), count=1 (+1), average=7168 KiB
/tmp/s.py:7: size=7168 KiB (+7168 KiB), count=1 (+1), average=7168 KiB
Run Code Online (Sandbox Code Playgroud)

这个结果显示了两个7 meg 的分配,内存复制的有力证据,并且将指示这些分配的确切行号。

尝试将切片从b = a[1:]into更改b = a[0:]为查看整个字符串特殊情况的效果:现在应该只有一个大分配,并且sys.getrefcount(a)会增加一个。

理论上,由于字符串是不可变的,因此实现可以为子字符串切片重用内存。这可能会使任何基于引用计数的垃圾收集过程复杂化,因此在实践中它可能不是一个有用的想法。考虑从更大的字符串中取出一小部分的情况 - 除非您在该切片上实现某种子引用计数,否则在子字符串的生命周期结束之前无法释放来自更大字符串的内存。

对于特别需要可以在不复制底层数据的情况下进行切片的标准类型的用户,有memoryview. 有关更多信息,请参阅Python 中内存视图的重点是什么

  • 啊,是的,`s[:] 的特殊情况是 s` (2认同)