什么是沙漏导入以及为什么要在代码库中避免它们?
What are hourglass imports and why would they be avoided in a codebase?
我在 Python 代码库中看到 some commits 删除了 "hourglass imports." 我以前从未见过这个术语,而且我无法通过 [=27= 找到任何相关信息] 文档或网络搜索。
什么是沙漏导入,什么时候使用或不使用它们?我最好的猜测是删除它们会产生子模块 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):
顶级 tensorflow __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 *
...
此处为 TensorFlow 贡献者 :wave:。我们使用术语沙漏
import 指的是从其他模块导入一堆东西的模块
模块并重新导出它们。你在你的作品中提供了一个很好的例子
问题。
我们关心这个的原因以及我们称之为的原因
沙漏都与构建图的形状有关。整体
沙漏模块的要点是许多用户将依赖它作为
一个方便的入口点。它本身取决于很多内部
符号。所以你的依赖图有很多边通过这个
一个节点,通过 hourglass:
的中心汇集
在现实世界中,沙漏会更宽更深
比这更重要的是,双方。最终用户可以定义依赖于
:standard_ops
和依赖于这些库的二进制文件,以及
内部操作本身可能具有依赖层。
这样做的问题在于,它很难廉价且正确地
重建以响应变化。如果我们改变:check_ops
的一部分,那么
看起来 :standard_ops
需要重建,因为它的其中一个
依赖关系发生了变化。而且因为 :standard_ops
已经重建,
它的依赖性也必须如此。但是现在我们已经重新构建了所有最终用户
程序,即使他们甚至没有实际使用该功能
完全由 :check_ops
提供。我们说构建图
过于逼近 实际的依赖关系图。过度逼近是
声音——构建仍然是正确的——但它可能是浪费的。
这是像 TensorFlow 这样的大型代码库的问题,我们有很多
数以千计的测试,当您更改任何代码时,我们 运行 所有受影响的测试,
而且测试可能很昂贵。如果你估计“哪些测试是
受此变化影响?”是一个巨大的过度逼近,因为
沙漏依赖,你在测试上浪费了大量的计算能力,
并且您的开发人员还必须等待更长时间才能合并他们的更改。
您原始问题中的补丁显示了我们如何删除一个
沙漏依赖并重写客户端以直接指向那些
他们实际使用的构建图的部分:
这样,如果:check_ops
改了,我们可以看到只需要
重新构建并重新测试一个客户端。
这有利也有弊。对于真正的最终用户,必须
直接导入大量内部构件很烦人。不太好API,
不如 import numpy as np
或 import tensorflow as tf
好。
此外,它公开了实现细节,使我们更难
移动这些模块。因此,出于这些原因,我们 仍然
向用户提供沙漏导入,既公开又在 Google 内。
但是,我们尝试不在我们自己的中使用沙漏导入
代码库。在我们自己的存储库中,重大更改不是问题,
因为如果我们想重命名某些东西,我们可以重命名它的所有客户
同时。我们有 工具来处理我们的构建
图 并且很乐意这样做,这是
大多数 Python 程序员不想担心。这些工具是
非常好,虽然 - 除了生成漂亮的可视化图表(如
上面)对于你的真实代码库,它们是一个强大的查询引擎的基础,
在那里你可以问系统问题,比如“目标是什么
transitively dependen on :foo
are still 运行ning on Python 2 属于
到我的团队?”。当您的构建图更多时,这会更强大
精确。
TL;DR: 沙漏模块是一个将来自许多进口的东西捆绑在一起的模块
子模块并将它们暴露给许多客户端模块。我们避开他们
因为它过度逼近了构建图,这使得它更
运行 测试成本高,分析代码更难。
我在 Python 代码库中看到 some commits 删除了 "hourglass imports." 我以前从未见过这个术语,而且我无法通过 [=27= 找到任何相关信息] 文档或网络搜索。
什么是沙漏导入,什么时候使用或不使用它们?我最好的猜测是删除它们会产生子模块 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):
顶级 tensorflow __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 *
...
此处为 TensorFlow 贡献者 :wave:。我们使用术语沙漏 import 指的是从其他模块导入一堆东西的模块 模块并重新导出它们。你在你的作品中提供了一个很好的例子 问题。
我们关心这个的原因以及我们称之为的原因 沙漏都与构建图的形状有关。整体 沙漏模块的要点是许多用户将依赖它作为 一个方便的入口点。它本身取决于很多内部 符号。所以你的依赖图有很多边通过这个 一个节点,通过 hourglass:
的中心汇集在现实世界中,沙漏会更宽更深
比这更重要的是,双方。最终用户可以定义依赖于
:standard_ops
和依赖于这些库的二进制文件,以及
内部操作本身可能具有依赖层。
这样做的问题在于,它很难廉价且正确地
重建以响应变化。如果我们改变:check_ops
的一部分,那么
看起来 :standard_ops
需要重建,因为它的其中一个
依赖关系发生了变化。而且因为 :standard_ops
已经重建,
它的依赖性也必须如此。但是现在我们已经重新构建了所有最终用户
程序,即使他们甚至没有实际使用该功能
完全由 :check_ops
提供。我们说构建图
过于逼近 实际的依赖关系图。过度逼近是
声音——构建仍然是正确的——但它可能是浪费的。
这是像 TensorFlow 这样的大型代码库的问题,我们有很多 数以千计的测试,当您更改任何代码时,我们 运行 所有受影响的测试, 而且测试可能很昂贵。如果你估计“哪些测试是 受此变化影响?”是一个巨大的过度逼近,因为 沙漏依赖,你在测试上浪费了大量的计算能力, 并且您的开发人员还必须等待更长时间才能合并他们的更改。
您原始问题中的补丁显示了我们如何删除一个 沙漏依赖并重写客户端以直接指向那些 他们实际使用的构建图的部分:
这样,如果:check_ops
改了,我们可以看到只需要
重新构建并重新测试一个客户端。
这有利也有弊。对于真正的最终用户,必须
直接导入大量内部构件很烦人。不太好API,
不如 import numpy as np
或 import tensorflow as tf
好。
此外,它公开了实现细节,使我们更难
移动这些模块。因此,出于这些原因,我们 仍然
向用户提供沙漏导入,既公开又在 Google 内。
但是,我们尝试不在我们自己的中使用沙漏导入
代码库。在我们自己的存储库中,重大更改不是问题,
因为如果我们想重命名某些东西,我们可以重命名它的所有客户
同时。我们有 工具来处理我们的构建
图 并且很乐意这样做,这是
大多数 Python 程序员不想担心。这些工具是
非常好,虽然 - 除了生成漂亮的可视化图表(如
上面)对于你的真实代码库,它们是一个强大的查询引擎的基础,
在那里你可以问系统问题,比如“目标是什么
transitively dependen on :foo
are still 运行ning on Python 2 属于
到我的团队?”。当您的构建图更多时,这会更强大
精确。
TL;DR: 沙漏模块是一个将来自许多进口的东西捆绑在一起的模块 子模块并将它们暴露给许多客户端模块。我们避开他们 因为它过度逼近了构建图,这使得它更 运行 测试成本高,分析代码更难。