Python生成器进行参考

时间:2016-01-24 00:06:38

标签: python reference generator

我试图使用生成器来节省一些内存并遇到问题。我对结果感到有些惊讶,因为我的理解是整数是不可变的,所以有人可以解释发生了什么吗?

>>>> 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没有这样的引用(至少据我所知)。

问题:

  1. 发生了什么事?这怎么可能?一些python文档的链接可以很好地解释发生了什么。
  2. 我怎样才能做我想做的事情(使用一个带有变量的发生器,将来会发生变化,但我需要当前的值)?
  3. 更新

    现实用例:

    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)
    

2 个答案:

答案 0 :(得分:2)

生成器被懒惰地评估。在您将其发送到list()之前,该生成器没有执行任何操作。当您将其发送到list()时,它会被执行。那时i是什么?分配的最后一个值:9

热切评估

list理解。每次遇到该表达式时,都会立即评估其内容。第一次循环时,i0,并立即检索并存储该值。这会为每个循环迭代生成不同的值。

答案 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]