Python-迭代生成器表达式是否会影响迭代另一个生成器表达式的顺序



我正在使用两个不同的for循环迭代两个不同生成器。但我可以看到,通过一个生成器表达式的迭代正在影响另一个生成器表达的迭代顺序。

虽然我理解并希望这是不可能的,但不确定为什么我会经历这种奇怪的行为。

we=KeyedVectors.load_word2vec_format('../input/nlpword2vecembeddingspretrained/GoogleNews-vectors-negative300.bin', binary='True')

data1=(['this','is','an','example','text1'],['this','is','an','example','text2'],....)
data2=(['test data1','test data2'],['test data3','test data4'],....)
txt_emb=(sum([we[token] for token in doc if token in we.key_to_index])/len(doc) for doc in data1)
phr_emb=([sum([we[token] for token in phrase.split(' ') if token in we.key_to_index])/len(phrase.split(' ')) for phrase in phrases]for phrases in data2)
for i in txt_emb:
print(i)
break
for j in phr_emb:
print(j)
break

txt_emb:

(【-0.06002714 0.00999211 0.0358354…………【0.07940271-0.02072765-0.03981323……】(

phr_emb:

([阵列([-0.13269043.03266907,…](,阵列([0.04994202,0.15716553,…(],

[阵列([-0.06970215,0.01029968,…](,阵列([0.02503967,.13970947,…]

这里txt_emb是一个生成器表达式,每个可迭代项都是一个列表。

phr_emb是一个生成器表达式,每个可迭代项都是一个列表,每个列表包含不同数量的数组(比如2-6(。

当我像上面的代码中那样首先迭代txt_emb时,我得到了txt_embe的第一个元素(索引0处的列表(,这是预期的。类似地,当我遍历phr_emb时,我希望得到第一个元素(索引0处的列表(,但我得到了第二个元素(在索引1处的列表。

类似地,如果我再次继续迭代txt_emb,我会得到第三个元素(索引2处的列表(,而不是得到txt_embe的索引1处的元素,因为在此之前我只迭代过一次txt_embeb。

当我压缩两个生成器表达式txt_emb和phr_emb并尝试对其进行迭代时,我会遇到类似的问题

我正在kaggle笔记本上运行这一切。但是,如果我在笔记本的不同单元格中分别迭代两个生成器表达式,那么我会按照预期的顺序获得元素。

从您的评论中,这似乎是您的两个生成器表达式从其他共享迭代器(可能是另一个生成器表达式(提取数据的问题。当第一个生成器表达式前进时,它从第三个迭代器获取数据,这使得第二个生成器表达式无法使用数据。

你可以用更简单的代码重新创建这个问题,比如:

data = range(10) # our underlying data
data_iterator = iter(data) # our shared iterator, which could be a generator expression
doubles = (x * 2 for x in data_iterator) # first generator expression
squares = (x * x for x in data_iterator) # second generator expression
print(next(doubles), next(doubles), next(doubles)) # prints 0 2 4
print(next(squares), next(squares), next(squares)) # prints 9 16 25, not 0 1 4 as you might expect

如果您从其中一个生成器表达式中获取一些值,则在另一个表达式中将跳过相应的值。这是因为他们每个人都在后台推进共享的data_iterator,它只对列表中的每个值进行一次遍历。

解决方案是为每个生成器创建单独的迭代器(例如,data_iterator的多个版本,或者如果重新计算很困难或耗时,则将其转储到列表之类的序列中,以便重复迭代。

例如,我们可以像这样将data_iterator转储到data_list中,然后从列表中构建生成器表达式:

data = range(10)
data_iterator = iter(data)
data_list = list(data_iterator)    # this can be iterated upon repeatedly
doubles = (x * 2 for x in data_list)
squares = (x * x for x in data_list)
print(next(doubles), next(doubles), next(doubles)) # prints 0 2 4
print(next(squares), next(squares), next(squares)) # prints 0 1 4 as expected

现在,将数据存储在这样的列表中可能会占用比您想要的更多的内存。生成器和生成器表达式的好处之一是它们允许延迟计算。如果您想保持这种延迟计算方法,并且只需要在一个生成器之前为另一个生成器提供几个值,因为它们主要是并行消耗的(例如,由zip消耗(,那么itertools.tee可能正是您所需要的。

import itertools
data = range(10)
data_iterator = iter(data)
data_it1, data_it2 = itertools.tee(data_iterator) # two iterators that will yield the same results
doubles = (x * 2 for x in data_it1)
squares = (x * x for x in data_it2)
for d, s in zip(doubles, squares):  # consume the values in parallel
print(d, s)

如果您计划在启动另一个生成器之前完全消耗一个生成器,那么tee返回的迭代器仍然可以使用,但在这种情况下,它们的效率要低得多(只是将整个中间迭代器转储到列表中可能会更好(。

最新更新