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, '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__
以返回副本使用字典而不是共享引用使得这种情况不可能发生。