什么是沙漏导入以及为什么它们会在代码库中被避免?

时间:2017-04-05 04:32:39

标签: python coding-style

我在Python代码库中看到了some commits,删除了"沙漏导入。"我之前从未见过这个词,我无法通过Python文档或网络搜索找到任何相关内容。

什么是沙漏导入以及何时使用或不使用它们?我最好的猜测是删除它们会生成子模块easier to find,但还有其他原因吗?

从一个链接提交中删除沙漏导入的示例更改:

diff --git a/tensorflow/contrib/slim/python/slim/nets/vgg.py b/tensorflow/contrib/slim/python/slim/nets/vgg.py
index 3c29767f2..d4eb43cbb 100644
--- a/tensorflow/contrib/slim/python/slim/nets/vgg.py
+++ b/tensorflow/contrib/slim/python/slim/nets/vgg.py
@@ -37,13 +37,20 @@ Usage:
 @@vgg_16
 @@vgg_19
 """
+
 from __future__ import absolute_import
 from __future__ import division
 from __future__ import print_function

-import tensorflow as tf
-
-slim = tf.contrib.slim
+from tensorflow.contrib import layers
+from tensorflow.contrib.framework.python.ops import arg_scope
+from tensorflow.contrib.layers.python.layers import layers as layers_lib
+from tensorflow.contrib.layers.python.layers import regularizers
+from tensorflow.contrib.layers.python.layers import utils
+from tensorflow.python.ops import array_ops
+from tensorflow.python.ops import init_ops
+from tensorflow.python.ops import nn_ops
+from tensorflow.python.ops import variable_scope


 def vgg_arg_scope(weight_decay=0.0005):

顶级张量流__init__.py从子模块中导出符号。

# tensorflow/python/__init__.py
...
from tensorflow.python.ops.standard_ops import *
...
# tensorflow/python/ops/standard_ops.py
...
from tensorflow.python.ops.array_ops import *
from tensorflow.python.ops.check_ops import *
from tensorflow.python.ops.clip_ops import *
...

1 个答案:

答案 0 :(得分:4)

这里的TensorFlow贡献者:wave :.我们使用术语沙漏 import 指的是从其他模块导入一堆东西的模块 模块并重新导出。您提供了一个很好的例子 问题。

我们关心这一点的原因以及我们称其为 沙漏都与构建图形的形状有关。整体 沙漏模块的重点是,很多用户将依赖它 一个方便的入口点。它本身取决于很多内部 符号。因此,您的依赖图在此过程中有很多优势 一个节点,通过hourglass的中心进行漏斗:

Diagram of a simple build graph with three end-user binaries
depending on :standard_ops, and :standard_ops depending on three
internal targets.

在现实世界中,沙漏会更宽更深 双方。最终用户可以定义依赖于 :standard_ops和依赖于这些库的二进制文件,以及 内部操作本身可能具有依赖关系。

这个问题是,它使得难以便宜且正确地进行 根据变化进行重建。如果我们更改:check_ops的一部分,则 :standard_ops似乎需要重新构建,因为其中之一 依赖关系已更改。并且由于:standard_ops已被重建, 因此,它的依赖性也必须如此。但是现在我们重新构建了所有最终用户 程序,即使它们实际上并未使用该功能 由:check_ops提供。我们说构建图 过度逼近实际依赖图。过度近似为 声音-构造仍然是正确的-但可能很浪费。

这在TensorFlow之类的大型代码库上是一个问题,在那里我们有很多 成千上万的测试,当您更改任何代码时,我们都会运行所有受影响的测试, 而且测试可能很昂贵。如果您对“哪些测试是 受此更改影响了吗?”是由于 沙漏依赖性,您在测试上浪费了大量计算能力, 而且您的开发人员还必须等待更长的时间才能合并他们的更改。

您原始问题中的补丁显示了我们如何删除 沙漏依赖性并重写客户端以直接指向那些 实际使用的构建图部分:

Diagram of a more precise build graph, with edges from end-user
programs to just those targets that they actually need

这样,如果更改了:check_ops,我们可以看到我们只需要 重新构建并重新测试一位客户。

这有优点也有缺点。对于真正的最终用户,必须 直接导入很多内部元素很烦人。那不是一个很好的API, 不及import numpy as npimport tensorflow as tf好。 此外,它公开了实现细节,这使我们更难 在这些模块周围移动。因此,由于这些原因,我们 do 向用户公开和在Google内部提供沙漏导入。 但是,我们尝试在我们自己的 中使用沙漏进口 代码库。在我们自己的存储库中,重大更改不是问题, 因为如果我们想重命名,我们可以重命名所有客户 与此同时。我们有tools for working with our build graphs,并且很乐意这样做,这是 大多数Python程序员都不需要担心。这些工具是 不过非常不错-除了生成美观的视觉图形(如 以上)作为您真正的代码库,它们是强大的查询引擎的基础, 您可以在其中询问系统问题,例如“ 暂时依赖:foo仍在Python 2上运行并属于 给我的团队?”。当您的构建图更多时,此功能将更强大 精确。

TL; DR:沙漏模块是将许多商品的进口捆绑在一起的模块 子模块,并将它们公开给许多客户端模块。我们避免他们 因为它过分逼近了构建图,这使它更加 运行测试成本高昂,而分析代码更困难。