Python的不可变字符串及其切片

时间:2013-08-04 10:42:12

标签: python string garbage-collection

Python中的字符串是不可变的,并且支持缓冲区接口。因此,在使用切片或split()方法时,不返回新字符串,而是返回旧字符串的部分是有效的。但是,据我所知,每次都会构造新的字符串对象。为什么会这样?我看到的唯一原因是它可以使垃圾收集更加困难。

正确:在常规的sutuations中,内存开销是线性的并且不明显:复制速度很快,我相信,分配也是如此。但是在python中做太多事情只是说它不值得付出努力!

编辑:

似乎使用这种方式会使内存管理变得更加复杂。在这种情况下,只使用1/5的任意字符串,并且我们无法解除分配整个字符串,这是一个简单的例子。我们可以改进内存分配器,因此它可以部分解除分配字符串,但它可能主要是反驳。如果内存使用非常重要,那么无论如何都可以使用缓冲区或内存视图模拟所有标准函数。是的,代码不会那么简洁,但我们必须放弃一些东西才能得到一些东西。

3 个答案:

答案 0 :(得分:3)

切片是如何工作的。切片总是执行浅拷贝,允许您执行

之类的操作
>>> x = [1,2,3]
>>> y = x[:]

现在可以为字符串做一个例外,但是它真的值得吗? Eric Lippert blogged about his decision not to do that for .NET;我猜他的论点也适用于Python。

另见this question

答案 1 :(得分:2)

如果你担心记忆(如果是非常大的字符串),请使用buffer()

>>> a = "12345"
>>> b = buffer(a, 2, 2)
>>> b
<read-only buffer for 0xb734d120, size 2, offset 2 at 0xb734d4a0>
>>> print b
34
>>> print b[:]
34

了解这一点可以让您替换split()等字符串方法。

如果你想split()一个字符串,但保留原始字符串对象(因为你可能需要它),你可以这样做:

def split_buf(s, needle):
    start = None
    add = len(needle)
    res = []
    while True:
        index = s.find(needle, start)
        if index < 0:
            break
        res.append(buffer(s, start, index-start))
        start = index + add
    return res

或使用.index()

def split_buf(s, needle):
    start = None
    add = len(needle)
    res = []
    try:
        while True:
            index = s.index(needle, start)
            res.append(buffer(s, start, index-start))
            start = index + add
    except ValueError:
        pass
    return res

答案 2 :(得分:2)

基础字符串表示是以null结尾,即使它跟踪长度,因此不能有一个引用子字符串的字符串对象是一个后缀。这已经限制了你的提案的有用性,因为它会增加许多复杂性,以便以不同的方式处理足够的事情和不足(并且放弃使用空终止字符串会带来其他后果)。

允许引用字符串的子字符串意味着使很多垃圾收集和字符串处理变得复杂。对于每个字符串,您必须跟踪有多少对象引用每个字符或每个索引范围。这意味着使struct字符串对象和处理它们的任何操作变得很复杂,这意味着可能很大的减速。

添加一个事实,即从python3字符串开始有3个不同的内部表示,并且事情会变得太乱以至于无法维护, 而你的提议可能没有给予足够的好处被接受。


这种“优化”的另一个问题是当你想要释放“大字符串”时:

a = "Some string" * 10 ** 7
b = a[10000]
del a

执行此操作后,您将获得子串b,以防止释放a(一个巨大的字符串)。当然你可以复制小字符串,但如果b = a[:10000](或其他大数字)怎么办? 10000个字符看起来像一个大字符串应该使用优化来避免复制,但它阻止重新发布数兆字节的数据。 垃圾收集器必须继续检查是否值得释放大字符串对象并进行复制,所有这些操作必须尽可能快,否则最终会降低时间性能。

程序中使用的字符串的99%是“小”(最多10k个字符),因此复制速度非常快,而你提出的优化开始变得非常有效(例如,取大小为100k的子串)来自大文本) 并且对于非常小的字符串来说要慢得多,这是常见的情况,即应该优化的情况。


如果您认为重要,那么您可以自由地提出PEP,显示实施以及由此产生的提案速度/内存使用情况的变化。如果真的值得付出努力,它可能会包含在未来的python版本中。

相关问题