简短回答:str 切片,一般来说,复制。这意味着为每个字符串的 n 后缀执行切片的函数正在执行 O(n<sup>2</sup>) 工作。也就是说,如果您可以使用memoryviews to get zero-copy views of the original bytes data 处理类似bytes 的对象,则可以避免复制。请参阅下面的如何进行零复制切片,了解如何使其发挥作用。
长答案:(C)Python str 不要通过引用数据子集的视图进行切片。 str slicing 一共有三种操作模式:
- 完整的切片,例如
mystr[:]:返回对完全相同的str 的引用(不仅仅是共享数据,相同的实际对象,mystr is mystr[:],因为str 是不可变的,所以这样做没有风险)
- 零长度切片和(取决于实现)缓存长度为 1 的切片;空字符串是单例(
mystr[1:1] is mystr[2:2] is ''),长度为 1 的低序号字符串也是缓存的单例(在 CPython 3.5.0 上,看起来所有字符都可以用 latin-1 表示,即range(256) 中的 Unicode 序号, 被缓存)
- 所有其他切片:切片的
str 在创建时被复制,此后与原始str 无关
#3 是一般规则的原因是为了避免大的str 被保存在内存中的问题,因为它的一小部分。如果你有一个 1GB 的文件,把它读入并像这样切片(是的,当你可以寻找的时候很浪费,这是为了说明):
with open(myfile) as f:
data = f.read()[-1024:]
那么您将在内存中保存 1 GB 的数据来支持显示最后 1 KB 的视图,这是一种严重的浪费。由于切片通常很小,因此在切片上复制而不是创建视图几乎总是更快。这也意味着str 可以更简单;它需要知道它的大小,但它也不需要跟踪数据中的偏移量。
如何进行零拷贝切片
有种方法可以在 Python 中执行基于视图的切片,在 Python 2 中,它将适用于 str(因为 str 在 Python 2 中类似于字节,支持 @987654322 @)。使用 Py2 str 和 Py3 bytes(以及许多其他数据类型,如 bytearray、array.array、numpy 数组、mmap.mmaps 等),您可以创建 memoryview that is a zero copy view of the original object 和可以在不复制数据的情况下进行切片。因此,如果您可以使用(或编码)到 Py2 str/Py3 bytes,并且您的函数可以与任意 bytes 类似的对象一起使用,那么您可以这样做:
def do_something_on_all_suffixes(big_string):
# In Py3, may need to encode as latin-1 or the like
remaining_suffix = memoryview(big_string)
# Rather than explicit loop, just replace view with one shorter view
# on each loop
while remaining_suffix: # Stop when we've sliced to empty view
some_constant_time_operation(remaining_suffix)
remaining_suffix = remaining_suffix[1:]
memoryviews 的切片确实创建了新的视图对象(它们只是超轻量级的,大小固定,与它们查看的数据量无关),只是没有任何数据,所以some_constant_time_operation 可以存储一个副本,如果需要,并且在我们稍后将其切片时不会更改。如果您需要像 Py2 str/Py3 bytes 这样的正确副本,您可以调用 .tobytes() 来获取原始的 bytes obj,或者(在 Py3 中仅出现),将其直接解码为 str从缓冲区复制,例如str(remaining_suffix[10:20], 'latin-1').