[英]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.