Python 迭代器类始终返回字典对象的初始状态为什么?

问题描述

class C:
 def __init__(self):
  self.ii={ 'c':0,'t':"kkk" }

 def __iter__(self):
   return self

 def __next__(self):
   if self.ii['c']>5:
     self.ii['c']=0
     raise stopiteration

   self.ii['c']+=1
   print(self.ii)
   return self.ii


x=C()

for i in x:
    pass

print([i for i in x])

我想,print([i ...]) 会在“c”字段中显示一个值越来越大的列表。但是它显示了初始 { 'c':0,'t': 'kkk' } 的六个片段。

好的,我知道,我可以通过将 .copy() 添加return self.ii 来使其工作,但我想知道,当没有 copy() 时会发生什么。 如果它给我一个带有 { 'c':6,'t':'kkk' } 的列表,我可以接受,因为字典是按地址处理的,而不是按值处理的,但这...我无法理解。

解决方法

您使用理解语句构建的列表包含对同一字典 (x.ii) 的六个引用。它不是“原始”字典,而是 __next__self.ii['c'] 重置为 0 之后的字典。

如果您的列表推导式在重置之前捕获了 i['c'] 的值,您将获得不同的值:

>>> print([i['c'] for i in x])
[1,2,3,4,5,6]

您可以通过更改其中一个来观察原始列表推导式捕获六个相同引用的问题:

>>> y = [i for i in x]
>>> y[0]['test'] = 'foobar'
>>> y[1]['c'] = 42
>>> x.ii
{'c': 42,'t': 'kkk','test': 'foobar'}
>>> y
[{'c': 42,'test': 'foobar'},{'c': 42,'test': 'foobar'}]

正如您所观察到的,更改 __next__ 以返回字典的副本而不是共享引用使得这种情况不可能发生。