【问题标题】:How does CPython handle list extend if there is insufficient contiguous memory after the list?如果列表后没有足够的连续内存,Python如何处理列表扩展?
【发布时间】:2016-07-17 12:43:42
【问题描述】:

由于是可变的,当扩展 Python list(例如,mylist.extend()mylist += anotherlist)时,列表的 id 不会改变。

我知道(至少在 CPython 中)列表在内存中是连续的(并且 id 恰好是列表头的地址)。如果列表之后的内存已经高度碎片化并且无法分配列表扩展名(即使有足够的可用空间,尽管该区域不连续)怎么办?分配失败?如何缓解?

【问题讨论】:

标签: python list append extend


【解决方案1】:

这是特定于实现的,但我假设您询问的是 CPython。

正如您所说,列表是连续内存,但它们是指向 PyObjects (*PyObject) 的 C 指针。对象本身,无论是整数还是您自己类的对象,都分配在动态内存中的某个位置,但(可能)不在连续内存中。

当第一次分配一个列表时,获得的内存比需要的多,在(概念上)右侧留下“松弛”条目。 list.append() 只会使用下一个 slack 条目。当然最终它们都满了。

此时执行内存的重新分配。在 C 术语中,这是对 realloc() 的调用,但实现可能实际上并未为此使用 C 运行时堆。基本上,从动态内存中获得更大的内存分配,然后将列表的所有元素复制到新列表中。请记住,我们复制的是指向 Python 对象的指针,而不是对象本身。

这样做会产生开销,但使用 slack 条目可以减少开销。从中可以看出list.append()比在列表的“左侧”添加一个条目效率更高。

如果分配失败是因为新列表的内存不足,那么您将收到内存不足错误。但是,如果你的内存那么短,那么任何新的 Python 对象都可能导致这种情况。

堆碎片可能是一个问题,但 Python 尽其所能来管理这个问题,这也是 Python 自己进行一些堆管理的原因之一。在最近的 Windows 实现中,使用低级虚拟内存 API 而不是 C RTL,因此 Python 可以执行自己的内存管理。

【讨论】:

  • 是的,这与 CPython 有关,我现在明白了。
  • 这是个好问题。如果您知道实现,那么您就知道为什么应该使用append() 而不是extend()
  • 我不知道为什么...extend()append() 在实现中似乎没有什么不同(上面@Padric 的评论中引用了CPython 代码)。此外,根据文档和stackoverflow.com/questions/252703/python-append-vs-extend 的长期讨论,最大的区别(查看代码)是extend() 迭代 - 这将节省对append() 的多次手动调用。在这和代码中的优化之间,似乎extend() 在将元素从一个列表添加到另一个列表时通常会更快。想法?
  • 这是一个数字问题。对于许多元素,然后extend 更有效,你是对的。但它不应该用于单个元素 - 我已经看到了。这里有一个可能感兴趣的讨论stackoverflow.com/questions/252703/python-append-vs-extend
  • 我现在明白你的意思并同意(这与我在上面的回复中引用的讨论相同,所以这就是我问的原因)。
【解决方案2】:

在 CPython 中,这是分配列表和元组的方式的不同。对于列表,对象包含一个指针为列表内容分配的内存。列表对象本身很小,永远不需要移动;它指向的向量的地址可以改变任意次数。

对于大多数时候期望很小的元组对象,元组内容的内存确实是直接在元组对象中分配的。但是元组无法调整大小,因此在这种情况下不会出现您的情况。

【讨论】:

  • 好的,那就回答这个问题 - 我想知道指针是指向实际的头部还是像你描述的那样具有一级间接性。事后看来,这看起来很明显,但我真的很感激你的回应! (在阅读有关extend vs += 的帖子时,我看到提到列表ID 是指向列表头部的直接指针......这就是我最终问这个的原因。)
  • 这是一个好问题!当然,同样的事情也适用于其他“可能很大的可变对象”,例如 dicts 和 set:一个分配给固定大小的对象标头(其“id” - 地址 - 永远不会改变),以及至少另一个分配给可变大小部分,标题指向 at.
  • 嗯,它更像是一个 CPython 的东西......这样做是完全有道理的,我只是误读了我找到的一个笔记,它让我失望了。我会删除原来的问题,但也许它会对新手有用。我想知道我是否必须在 PyCon 上戴上羞耻的口袋保护器? :-D
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-19
  • 2016-03-10
  • 1970-01-01
  • 2021-07-10
  • 1970-01-01
  • 2012-02-23
相关资源
最近更新 更多