我试图使用生成器来节省一些内存并遇到问题。我对结果感到有些惊讶,因为我的理解是整数是不可变的,所以有人可以解释发生了什么吗?
>>>> a = []
>>>> for i in range(10):
.... a.append((i for _ in [0]))
....
>>>> list(a[0])
[9]
当我使用列表理解而不是它做我想要的时候:
>>>> a = []
>>>> for i in range(10):
.... a.append([i for _ in [0]])
....
>>>> a
[[0], [1], [2], [3], [4], [5], [6], [7], [8], [9]]
我可以理解正在发生的事情是,生成器以某种方式获得i
的值的“引用”,在最后一次循环之后是9,但这似乎是反... pythonic作为python没有这样的引用(至少据我所知)。
问题:
更新
现实用例:
def get_some_iter(a, b):
iters = []
for i in a:
m = do_something(i, len(b))
iters.append((SomeObj(j, m) for j in itertools.combinations(b, i))
return itertools.chain(*iters)
答案 0 :(得分:2)
生成器被懒惰地评估。在您将其发送到list()
之前,该生成器没有执行任何操作。当您将其发送到list()
时,它会被执行。那时i
是什么?分配的最后一个值:9
。
list
理解。每次遇到该表达式时,都会立即评估其内容。第一次循环时,i
为0
,并立即检索并存储该值。这会为每个循环迭代生成不同的值。
答案 1 :(得分:-1)
是的,生成器保留对i的引用,因为内部没有任何其他解决方案。
最好的解决方案是将它全部作为生成器:
a = ((i for _ in [0]) for i in range(10))
for g in a:
print(list(g))
[0]
[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]
当然,如果您需要直接访问n + 1值而不获取第一个n,它就无法解决问题。如果你需要这样做,我认为最pythonic的方法是构建你自己的迭代器,如下所示:
class ListIter(object):
def __init__(self, i):
self.i= i
self.iter = 0
def __iter__(self):
return self
def __next__(self):
if self.iter > 0:
raise StopIteration
else:
self.iter += 1
return self.i
a = [ListIter(i) for i in range(10)]
list(a[0])
[0]