将生成器转换为列表会覆盖Python中的值

时间:2018-08-09 16:22:13

标签: python

我在Python 3.5中具有这段代码,该代码实现了用于生成可能较大的缓冲数据批处理的生成器:

def batched_data(data, size=20):                             
    batch = []  
    for d in data:                                                                                                                                                                                
        batch.append(d)
        if len(batch) == size:
            yield batch
            batch.clear()
    yield batch

def buffered_data(data, bufminsize=10, bufmaxsize=20): 
    diter = iter(data)                                                                                            
    buffer = collections.deque(next(diter) for _ in range(bufmaxsize))

    while buffer:                                                                                                                                                                                 
        yield buffer.popleft()
        if len(buffer) < bufminsize:
            buffer.extend(
                next(diter) for _ in range(bufmaxsize - len(buffer)))

def batched_buffered_data(data, bufsize=100, batch=20):      
    yield from batched_data(
        buffered_data(data, bufmaxsize=bufsize, 
                      bufminsize=bufsize - batch),
        size=batch)

现在,当我初始化生成器并在for循环中遍历批处理时,一切都很好:

In [351]: gen = batched_buffered_data(range(27), bufsize=10, batch=7)
In [352]: for g in gen:
     ...:     print(g)
     ...:
[0, 1, 2, 3, 4, 5, 6]
[7, 8, 9, 10, 11, 12, 13]
[14, 15, 16, 17, 18, 19, 20]
[21, 22, 23, 24, 25, 26]

但是,当我尝试使用列表理解或通过list进行转换时,会发生这种情况:

In [353]: gen = batched_buffered_data(range(27), bufsize=10, batch=7)

In [354]: list(gen)
Out[354]: 
[[21, 22, 23, 24, 25, 26],
 [21, 22, 23, 24, 25, 26],
 [21, 22, 23, 24, 25, 26],
 [21, 22, 23, 24, 25, 26]]

我对此感到非常困惑。必须涉及某种可变元素,但我真的不知道这种行为的背后是什么。

2 个答案:

答案 0 :(得分:2)

batch.clear()更改为batch = []应该可以解决。问题是batch之后的clear()列表仍然是对一个原始列表的引用。产量似乎起作用,因为它正在打印一个列表,该列表是在更改下一个yield上的元素之前显示的。在每个yield上将其设置为新列表会破坏对上一次迭代的引用,因此不会出现别名。

如果您仍然感到困惑,请使用带有.clear()的原始代码来查看此示例:

result = []
gen = batched_buffered_data(range(27), bufsize=10, batch=7)

for g in gen:
     result.append(g)

[print(x) for x in result]

输出:

[21, 22, 23, 24, 25, 26]
[21, 22, 23, 24, 25, 26]
[21, 22, 23, 24, 25, 26]
[21, 22, 23, 24, 25, 26]

答案 1 :(得分:2)

您应将batch.clear()更改为batch = []

这是因为.clear()清除了列表,指向该列表的所有变量都变成了[],否则batch = []只是创建了一个新列表并将其分配给批处理