【问题标题】:Python - Does iterating a generator expression impact the order of iterating another generator expression?Python - 迭代生成器表达式会影响迭代另一个生成器表达式的顺序吗?
【发布时间】:2021-08-13 23:34:53
【问题描述】:

我正在使用两个不同的 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,0.03266907,...]),数组([0.04994202,0.15716553,...])],

[数组([-0.06970215,0.01029968,...]),数组([0.02503967,0.13970947,...])],.......)

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

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

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

同样,如果我再次继续迭代 txt_emb,我会得到第三个元素(索引 2 处的列表),而不是获取 txt_emb 的索引 1 处的元素,因为在此之前我只迭代了 txt_emb 一次。

当我压缩两个生成器表达式 txt_emb 和 phr_emb 并尝试遍历它时,我遇到了类似的问题。

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

【问题讨论】:

  • 据我所知,您的代码中没有任何生成器。请提供minimal reproducible example
  • 您的问题的答案完全取决于被迭代的生成器。您的两次迭代之间本质上不会有任何联系,但生成器的实现可能会以某种方式进行交互。
  • @Blckknght 我不确定这里使用“发电机”这个词的含义。
  • 如果您的 data1data2 序列确实像您在此处显示的那样是静态值,那么您基于它们生成的生成器表达式似乎不太可能根据您运行循环的顺序而有所不同。也许在we 中查找内容有时会产生某种副作用?我们无法真正猜测,但我想可能有人对您正在使用的 NLP 库有经验。
  • 您的dataX 变量本身是否都是生成器表达式?因为如果他们都在同一个迭代器(例如文件)上绘图,这可以解释您的问题。

标签: python nlp iterator generator


【解决方案1】:

从您的 cmets 来看,这似乎是您的两个生成器表达式从其他一些共享迭代器(可能是另一个生成器表达式)中提取数据的问题。当第一个生成器表达式前进时,它会从第三个迭代器中获取数据,这使得它对第二个生成器表达式不可用。

您可以使用如下更简单的代码重新创建此问题:

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 返回的迭代器可以如果您计划在启动另一个生成器之前完全消耗一个生成器,但在这种情况下它们的效率要低得多(只是将整个中间迭代器转储到列表中可能更好)。

【讨论】:

  • 这很有帮助...此外,生成器表达式 dataX 内部有 214K 列表,每个列表的长度从 2 到 20 个文本元素不等。我正在尝试将此生成器表达式转换为列表,但是生成器表达式非常大需要很长时间才能转换为列表。是否有任何选项可以帮助这种转换既节省内存又节省时间。我尝试过 list(dataX) 之类的方法,甚至使用池进行多处理,但仍然需要很多时间。在将整个表达式转换为列表时,我什至一次都没有成功。你能帮忙吗
  • 我能想象到你会遇到严重的性能问题的唯一真正原因是,如果你在某个地方内存不足,并且程序在开始尝试交换内存时几乎停止。或者我想如果你的代码只是在某个地方使用了一些深度次优的算法,那么你可能会遇到与惰性生成器相同的问题。但无论哪种情况,我都不认为有一个通用的解决方案。你应该问另一个关于你实际计算的更多细节的问题,也许有人可以帮助你优化它。
猜你喜欢
  • 2014-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-08
  • 1970-01-01
  • 2018-05-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多