规范
内容
规范¶
Dask 是一种规范,用于使用普通的 Python 数据结构(即字典、元组、函数和任意 Python 值)编码一个图——特别是具有数据依赖关系的任务的有向无环图。
定义¶
Dask图 是一个将 键 映射到 计算 的字典:
{'x': 1,
'y': 2,
'z': (add, 'x', 'y'),
'w': (sum, ['x', 'y', 'z']),
'v': [(sum, ['w', 'z']), 2]}
键 可以是 str、bytes、int、float 或它们的元组:
'x'
('x', 2, 3)
任务 是一个以可调用对象为第一个元素的元组。任务表示旨在由单个工作者运行的工作原子单位。示例:
(add, 'x', 'y')
我们将任务表示为一个元组,其中 第一个元素是一个可调用函数*(如 ``add``),后续元素是该函数的 *参数。一个 参数 可以是任何有效的 计算。
一个 计算 可以是以下之一:
Dask 图中的任何 键 ,例如
'x'
任何其他值,如
1
,将按字面意思解释一个像
(inc, 'x')
这样的 任务 (见下文)一个 计算 列表,例如
[1, 'x', (inc, 'x')]
因此,以下所有内容都是有效的 计算:
np.array([...])
(add, 1, 2)
(add, 'x', 2)
(add, (inc, 'x'), 2)
(sum, [1, 2])
(sum, ['x', (inc, 'x')])
(np.dot, np.array([...]), np.array([...]))
[(sum, ['x', 'y']), 'z']
要编码关键字参数,我们推荐使用 functools.partial
或 toolz.curry
。
应该期望哪些功能¶
在类似 (add, 'x', 'y')
的情况下,像 add
这样的函数接收的是具体值而不是键。Dask 调度器会在调用 add
函数之前,将键(如 'x'
和 'y'
)替换为它们的计算值(如 1
和 2
)。
入口点 - get
函数¶
get
函数是所有 调度器 的计算入口点。该函数获取与给定键关联的值。该键可能引用存储的数据,如 'x'
的情况,或引用一个任务,如 'z'
的情况。在后一种情况下,get
应执行所有必要的计算以检索计算值。
>>> from dask.threaded import get
>>> from operator import add
>>> dsk = {'x': 1,
... 'y': 2,
... 'z': (add, 'x', 'y'),
... 'w': (sum, ['x', 'y', 'z'])}
>>> get(dsk, 'x')
1
>>> get(dsk, 'z')
3
>>> get(dsk, 'w')
6
此外,如果给定一个 list
,get 应该同时获取多个键的值:
>>> get(dsk, ['x', 'y', 'z'])
[1, 2, 3]
因为我们接受键列表作为键,所以我们支持嵌套列表:
>>> get(dsk, [['x', 'y'], ['z', 'w']])
[[1, 2], [3, 6]]
在内部,get
可以任意复杂,调用分布式计算,使用缓存等。
为什么使用元组¶
通过 (add, 'x', 'y')
,我们希望编码调用 add
函数在键 'x'
和 'y'
对应的值上的结果。
我们打算表达以下含义:
add('x', 'y') # after x and y have been replaced
但这会出错,因为 Python 在知道 'x'
和 'y'
的值之前就立即执行了函数。
我们将执行延迟,通过将左括号向左移动一个项,创建一个元组:
Before: add( 'x', 'y')
After: (add, 'x', 'y')
这使我们能够将所需的计算作为数据存储,可以使用其他Python代码进行分析,而不是立即执行。
LISP 用户会将此识别为 s-表达式,或作为一种基本的引用形式。