python - 字符串切片是否在内存中执行复制?
问题描述
我想知道是否:
a = "abcdef"
b = "def"
if a[3:] == b:
print("something")
确实执行了a
内存中某处的“def”部分的副本,或者是否就地完成了字母检查?
注意:我说的是字符串,而不是列表(我知道答案)
解决方案
字符串切片在 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)
您应该会看到前两个统计信息输出,如下所示:
/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
这个结果显示了两个7 meg 的分配,这是内存复制的有力证据,并且将指示这些分配的确切行号。
尝试将切片从更改b = a[1:]
为b = a[0:]
查看整个字符串特殊情况的效果:现在应该只有一个大分配,并且sys.getrefcount(a)
会增加一。
理论上,由于字符串是不可变的,因此实现可以将内存重新用于子字符串切片。这可能会使任何基于引用计数的垃圾收集过程复杂化,因此在实践中它可能不是一个有用的想法。考虑从大得多的字符串中取出一个小切片的情况——除非您在切片上实现某种子引用计数,否则在子字符串的生命周期结束之前,无法释放来自大得多的字符串的内存。
对于特别需要可以在不复制基础数据的情况下进行切片的标准类型的用户,有memoryview
. 有关更多信息,请参阅Python 中的 memoryview 到底是什么。
推荐阅读
- c++ - 抑制关于 Eigen 的整洁的误报警告
- java - 在 Java 中使用 LinkedHashSet 检查数组中的唯一值
- scala - 在不更改 Spark 属性的情况下进行连接时未广播数据帧的示例
- python - BigQuery 作业状态已完成,但未使用气流插入任何行
- postgresql - 如何在postgres shell脚本的密码中声明@符号
- python - 为什么即使单个变量的使用量下降了,内存使用量也会上升?
- xml - 将存储在 blob 中的 XML 文件中的数据导入 Azure SQL Server
- ruby-on-rails - 如何在 Rails 6 中使用茧宝石
- html - IE11 中奇怪的边界半径问题
- aspnetboilerplate - 如何创建像 aspnetboilerplate 这样的文档站点?