2016-10-22 90 views
2

比方说,我们有以下列表,我们为它创造一个迭代器:列表的Python迭代器如何实际工作?

lst = [1,2,3] 
itr = iter(lst) 

接下来让我们说我们是完全不同的价值观改变着我们的列表:

lst = ['a', 'b', 'c'] 

如果我我们运行以下循环:

for x in itr: 
    print x 

我们将得到'1,2,3'。但为什么?据我所知,迭代器不会复制迭代对象的所有值。至少来自三个元素的列表的迭代器与100000个元素的列表具有相同的大小。 sys.getsizeof(i)返回64。迭代器如何能够按大小如此小,并保持列表的“旧”值?

+1

您尚未更改列表,您创建了一个完全不相关的新列表。 – jonrsharpe

回答

4

迭代器本身包含对列表的引用。由于lst是反弹而不是变异,因此此参考不会更改。

>>> lst = [1, 2, 3] 
>>> itr = iter(lst) 
>>> lst[:] = ['a', 'b', 'c'] 
>>> for x in itr: 
... print x 
... 
a 
b 
c 
3

迭代器引用列表对象而不是名称。所以重新分配名称lst到另一个对象并不影响迭代器;名称绑定到对象,并引用对象,但名称不是对象本身。

你可以得到迭代器引用对象的窥探与gc.get_referents

>>> import gc 
>>> lst = [1,2,3] 
>>> itr = iter(lst) # return an iterator for the list 
>>> lst = ['a', 'b', 'c'] # Bind name lst to another object 
>>> gc.get_referents(itr)[0] 
[1, 2, 3] 

正如您看到的,迭代器仍参照第一列表对象。


以下参考将帮助您了解更多关于名称和Python绑定

Execution model - Naming and binding

2

欢迎Python的对象引用系统。变量名称与存储器中存储的实际对象之间并没有很深的关系。

假设你有一个朋友lst,和你聘请了抢劫犯iter以抢劫他。现在你告诉抢劫者你的朋友是电话簿中的第三个杰克(globals)。

lst = [1, 2, 3] 
itr = iter(lst)  # iter object now points to the list pointed to by lst 
        # it doesn't care about its name (doesn't even knows its name actually) 

# Now the mugger has found the friend, and knows his address (the actual object in memory). 
# The mugger catches him, and takes his jacket. 
print itr.next() # outputs 1 

# Now the telephone directory was updated (yes it's updated very frequently). 
lst1 = lst    # your friend is now the fourth Jack 
lst = ['a', 'b', 'c'] # someone else is the third Jack now 
         # but mugger doesn't know, he won't see the directory again 

print itr.next()  # (output 2), mugger takes t-shirt, and leaves him for now 

# Meanwhile your friend buys new clothes. 
lst1.append(4)  # here the actual object pointed to by iter is updated 
lst1.append(5) 

# You call the mugger and say, don't leave him until he's got nothing. 
# The mugger goes full retard. 
for i in iter: 
    print i   # outputs 3, 4 and 5 

NTL; DR:Python变量名都只是标签,是指一些对象的空间。 当您在名为lstlist上调用iter时,迭代器对象获取指向实际对象的指针,甚至现在它的名称也不是lst

如果您可以通过调用修改原始对象,appendextendpopremove等,迭代器的行为都将受到影响。但是,当您为lst分配新值时,会创建一个新对象(如果它以前不存在),并且lst只是开始指向该新对象。

如果没有其他对象指向它,垃圾收集器将删除原始对象(在这种情况下,itr指向它,因此原始对象不会被删除)。

http://foobarnbaz.com/2012/07/08/understanding-python-variables/

附加:

# The friend goes and buys more clothes. 
lst1.extend([6, 7, 8]) 

# You call the mugger and ask him to take a shot at the friend again. 
itr.next() # But the mugger says, chill man he's got nothing now 
       # raises StopIteration 

这没有什么关系对象的引用,迭代器内部只存储了已经重复的完整列表。

+0

很好的解释,谢谢! – Paul