[英]Does python iterator cost additional memory?
我想知道my_iter = iter(iterable_obj)
复制了iterable_obj
? 换句话说,上面的调用是否需要额外的内存?
它可以复制,但不应该。 它应该只在现有数据结构上提供迭代,并根据迭代所需的最小内存开销。 例如, list
迭代器仅存储对列表的引用和索引。
它有什么作用? 那要看。 iter
函数将提供一个迭代器,覆盖整个世界中任何可能的可迭代对象,包括一个你明天只会编写的具有复杂内部数据结构的可迭代类。 iter
怎么可能做到这一点? 人工智能? 魔法? 不。嗯……实际上是的,魔法。 即用所谓的“魔法方法”(或“dunder方法”)。 在这种情况下, __iter__
或__getitem__
。 诀窍是, iter
不知道如何迭代可迭代对象。 可迭代的。 并使用这两种魔术方法之一来访问迭代。 在iter
功能只是调用它的代码(希望迭代)和迭代(它提供了迭代)之间简单的中间人。
__iter__
方法返回迭代器的示例:
class MyIterable:
def __iter__(self):
return iter('abcde')
print(list(MyIterable()))
输出:
['a', 'b', 'c', 'd', 'e']
使用__getitem__
方法返回索引 0、1、2 等元素的示例(直到IndexError
):
class MyIterable:
def __getitem__(self, index):
return 'abcde'[index]
print(list(MyIterable()))
输出:
['a', 'b', 'c', 'd', 'e']
那么,是什么iter(iterable)
吗? 取决于迭代器的作用。 它可能会复制,也可能不会,它可能会试图让你的房子着火。
对于像列表迭代器这样简单的东西,选择是显而易见的:使用对列表的引用和迭代器所在位置的索引既简单又高效。
让我们考虑一个不那么明显并且您可能想复制的情况:一个二叉搜索树迭代器,它提供按排序顺序迭代树的值。 让我们考虑三种可能的实现,其中 n 是树中值的数量。 该树将表示为BST
节点对象的结构:
class BST:
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right
class BST:
...
def __iter__(self):
if self.left:
yield from self.left
yield self.value
if self.right:
yield from self.right
好处:
缺点:
由于缓慢的迭代,尤其是二次时间,非常令人失望,我们可以将树中的所有值复制到一个列表中,并在该列表上返回一个迭代器:
class BST:
...
def __iter__(self):
values = []
def collect(node):
if node:
collect(node.left)
values.append(node.value)
collect(node.right)
collect(self)
return iter(values)
好处:
缺点:
这是一个使用堆栈的迭代。 堆栈将保存其值和其右子树仍需要迭代的节点:
class BST:
...
def __iter__(self):
node = self
stack = []
while node or stack:
while node:
stack.append(node)
node = node.left
node = stack.pop()
yield node.value
node = node.right
结合了前两种实现的优点(既节省时间又节省内存),但缺点是不容易。 与前两个实现不同,我觉得有必要对它的工作原理添加一点解释,如果你以前没有见过它,你可能仍然需要考虑一下。
如果这对您来说只是一个小练习,并且您没有效率问题,那么前两个实现很好且易于编写。 尽管将值复制到列表中并不是真正的普通迭代器,因为复制值从根本上不是迭代的意思。 不过,这与内存无关。 递归生成器和迭代方法也需要 O(log n) 和 O(n) 内存,但这是组织数据,对于促进迭代有些必要。 他们没有复制内容数据。
如果它是一个认真使用的 BST 包,那么我会发现前两个实现的缺点是不可接受的,并且会使用迭代实现。 一次编写更多的努力,但具有优势和适当的迭代器。
顺便说一句,如果节点也有对其父节点的引用,我认为迭代器可以使用它来使用 O(1) 内存进行高效迭代。 留给读者练习:-P
可使用的 BST 代码( 在线试用! ):
from random import shuffle
class BST:
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right
def insert(self, value):
if value < self.value:
if self.left:
self.left.insert(value)
else:
self.left = BST(value)
elif value > self.value:
if self.right:
self.right.insert(value)
else:
self.right = BST(value)
def __repr__(self):
return f'BST({self.value}, {self.left}, {self.right})'
def __iter__(self):
yield from self.left or ()
yield self.value
yield from self.right or ()
def __iter__(self):
values = []
def collect(node):
if node:
collect(node.left)
values.append(node.value)
collect(node.right)
collect(self)
return iter(values)
def __iter__(self):
node = self
stack = []
while node or stack:
while node:
stack.append(node)
node = node.left
node = stack.pop()
yield node.value
node = node.right
# Build a random tree
values = list(range(20)) * 2
shuffle(values)
tree = BST(values[0])
for value in values[1:]:
tree.insert(value)
# Show the tree
print(tree)
# Iterate the tree in sorted order
print(list(tree))
示例输出:
BST(1, BST(0, None, None), BST(17, BST(10, BST(6, BST(2, None, BST(4, BST(3, None, None), BST(5, None, None))), BST(9, BST(7, None, BST(8, None, None)), None)), BST(15, BST(11, None, BST(13, BST(12, None, None), BST(14, None, None))), BST(16, None, None))), BST(18, None, BST(19, None, None))))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.