首页 > 解决方案 > 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' },因为字典是按地址处理的,而不是按值处理的,但是这个......我不明白。

标签: pythoniterator

解决方案


您使用理解语句构建的列表包含对同一 dict (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, 't': 'kkk', 'test': 'foobar'}, {'c': 42, 't': 'kkk', 'test': 'foobar'}, {'c': 42, 't': 'kkk', 'test': 'foobar'}, {'c': 42, 't': 'kkk', 'test': 'foobar'}, {'c': 42, 't': 'kkk', 'test': 'foobar'}, {'c': 42, 't': 'kkk', 'test': 'foobar'}]

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


推荐阅读