【问题标题】:When are generators converted to lists in Dask?生成器何时转换为 Dask 中的列表?
【发布时间】:2016-03-03 08:19:01
【问题描述】:

在 Dask 中,生成器何时转换为列表,或者它们通常是懒惰地消耗? 比如用代码:

from collections import Counter
import numpy as np
import dask.bag as db

def foo(n):
    for _ in range(n):
        yield np.random.randint(10)

def add_to_count(acc, x):
    acc.update(x)
    return acc

def add(x,y):
    return x + y

b1 = db.from_sequence([1,2,3,4,5])
b2 = b1.map(foo)
result = b2.fold(add_to_count, add, Counter())

我得到以下输出,其中生成器 已(合理地)转换为列表供我检查:

>>> b2.compute()
[[5], [5, 6], [3, 6, 1], [5, 6, 6, 0], [5, 6, 6, 0, 3]]

虽然合理,但它不同于我通常期望生成器在 Python 中的行为方式,后者需要显式转换为列表。

所以,在计算 fold (result.compute()) 时, 是add_to_count 的输入参数x 生成器,还是已经转换为列表?

我对列表很长的情况感兴趣, 所以惰性评估更有效,比如说, b1 = db.from_sequence([10**6]*10).

我猜我也可以用bag.frequencies 解决上述问题,但我对惰性求值和高效约简也有类似的担忧。

Dask 的一个基本方面是不是我没有摸索,还是我只是懒惰,我在哪里可以查看代码来自己解决这个问题?

【问题讨论】:

  • 好吧,很容易,将print(type(x)) 添加到add_to_count() 并尝试使用不同的调度程序似乎可以确认x 确实是一个生成器对象。如果有什么要补充的,我会暂时打开它。

标签: python dask


【解决方案1】:

不完全合适,但我会提供一个稍微不同的问题的答案:

Dask.bag 为您添加了对 list` 的防御性调用,以防您决定在一次计算中分支并使用包两次:

x = b.map(func1, b)
y = b.map(func2, b)
compute(x.frequencies(), b.frequencies())

这在使用多处理或分布式等后端时也很有用,因为我们不能跨进程边界发送生成器,但可以发送列表。

但是,这些对list 的防御性调用会在可能的情况下在计算之前进行优化,以促进惰性。

总之,一切都应该按你想要的方式工作,但如果懒惰会妨碍正确性,就会恢复为具体的非懒惰值。

【讨论】:

  • 谢谢。这正是我一直在寻找的。这是否在任何地方的文档中都有概述(我找不到它),它会有用吗?如果你这么认为,我可能会在一些帮助下尝试一下。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-01
  • 1970-01-01
  • 2016-06-30
  • 2020-10-13
  • 2017-05-13
  • 1970-01-01
相关资源
最近更新 更多