python - 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,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 笔记本中运行所有这些。但是如果我在笔记本的不同单元格中分别迭代两个生成器表达式,那么我会按预期顺序获得元素。
解决方案
从您的评论来看,这似乎是您的两个生成器表达式从其他一些共享迭代器(可能是另一个生成器表达式)中提取数据的问题。当第一个生成器表达式前进时,它从第三个迭代器中获取数据,这使得它对第二个生成器表达式不可用。
您可以使用如下更简单的代码重新创建此问题:
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
现在,将数据存储在这样的列表中可能会占用比您想要的更多的内存。生成器和生成器表达式的优点之一是它们允许的惰性计算。如果您想保持这种惰性计算方法,并且只需要几个值在另一个生成器之前可用,因为它们主要是并行消耗的(例如 by 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
)。
推荐阅读
- python - 使用 .loc 基于时间序列删除行
- swift - PassthroughSubject 完成完成未调用
- r - 使用自定义投影仪主题在 rmarkdown::beamer_presentation 中创建从徽标到目录幻灯片的超链接
- php - 获取第一个分隔符和最后一个点之间的字符串
- android - 当我的手指触摸屏幕时,自动点击停止
- vue.js - nuxt 配置,$route 未定义?
- r - 在R中使用summarySE函数时,如何抑制仅具有一个观察值的分组的NaN值的警告消息?
- google-colaboratory - 关于在 Google Colab 上运行 .ipynb 文件的问题
- class - 了解基类到派生类的转换和转换错误
- node.js - 替换Loopback测试环境中的数据源