python 中的二阶导入问题:发生了什么?
Second order import issue in python: What is going on?
我有一个相当简单的设置:
[FOLDER]
|-> [Lib]
__init__.py (__all__=["modA","modB"])
modA.py (contains class named classA)
modB.py (contains class named classB + from modA import classA)
test1.py (from Lib.modA import classA
from Lib.modB import classB)
|-> [example]
test2.py (import sys
sys.path.append("../")
from Lib.modA import classA
from Lib.modB import classB)
来自 Lib 文件夹的 运行 test1.py 完美运行(与没有 warnings/errors/etc 一样)。另一方面,示例文件夹中的 运行 test2.py 需要系统补丁,因为 python 的工作方式很奇怪,并且从 from modA import classA
(在 modB.py 中)通过 from Lib.modB import classB
(在 test2.py 中)。
人们应该如何在模块中定义一个导入,以便它也可以工作,而不管可能 use/import 所述模块的任何未来脚本的可能位置如何?
据我了解,当您尝试手动 运行 时,没有将路径设置为环境,而当您只是这样做时
python file.py
python 不知道我们 运行ning 在哪个环境中,这正是我们 sys.path.append 将兄弟目录路径添加到 env.
的原因
如果您使用的是 IDE,如 spyder 或 pycharm。您可以选择设置程序所在的目录,或者在 pycharm 中将整个程序创建为一个包,其中所有路径都由 IDE.
处理
Python 程序应该被认为是 包 和 模块 ,而不是 目录 和 文件 。虽然有一些重叠,但包和模块的限制更多,但也因此封装得更好。
混合使用两者——比如手动修改 sys.path
——只能作为最后的手段。
TLDR:
- 使用完全限定的导入:
from Lib.modA import classA
而不是 from modA import classA
。
- 使用环境进行发现:通过
PYTHONPATH
而不是 sys.path
添加搜索路径。
首先确定哪些是 top-level 包。
这是我们从“directories/files”到“包”的地方。值得注意的是,稍后我们不能再“高于”top-level,因此它应该包含我们需要的一切。但是,我们也不能删除“下方”的任何内容,因此它应该是一个足够严格的选择。
在示例中,我们可以低至将 modA
和兄弟姐妹视为自己的 module-package,高至 FOLDER
包含整个项目。
[FOLDER]
|-> [Lib]
| |-> modA.py
: :
选择Lib
是合理的,因为它代表了self-contained部分。升到 FOLDER
会过分,降到 modA
和兄弟姐妹会打破他们属于一起的关系。
top-level 包文件夹下的所有内容现在都属于该包。
值得注意的是,modA.py
现在是模块 Lib.modA
。它不是 FOLDER.Lib.modA
,也不只是 modA
。
仅使用绝对完全限定名称或相对名称进行导入。
既然top-level成立了,所有的import
声明都得跟它说。仅通过 完全限定 名称引用模块,即使两个模块共享一个更常见的父模块也是如此:
# Lib.modB
# okay, fully qualified import
from Lib.modA import classA
# broken, unqualified import
from modA import classA
为了避免键入完整的完全限定名称,可以使用相对 名称代替。这会重新使用当前模块名称来派生要导入的模块的完全限定名称。
# Lib.modB
# okay, relative import
from .modA import classA
注意: 相对导入是 package 操作,而不是 filesystem 操作。不能超越 top-level,而是进入包命名空间。
模块内的所有内容现在都被封装并且self-contained。
无论包本身的位置如何,所有内部导入都将起作用。只要top-level可以导入,下面的都可以导入。
值得注意的是,这与可能 use/import 包的任何未来脚本的位置无关。
通过环境而不是程序启用包。
定义包的要点是获得一个代表库的 self-contained 实体。一个人可以压缩一个包裹或类似的东西,它仍然是一个包裹。
与其让脚本假定包的位置,不如让环境(脚本、用户或整台机器)公开包。基本上有两种方法可以做到这一点:
- 公布包裹位置为搜索位置。这适合开发,因为它灵活但难以维护。
PYTHONPATH
适用于此。
- 将包移动到Python使用的搜索位置。这适用于生产和分发,因为它需要付出努力但易于维护。 Packaging and using a package manager 适用于此。
我有一个相当简单的设置:
[FOLDER]
|-> [Lib]
__init__.py (__all__=["modA","modB"])
modA.py (contains class named classA)
modB.py (contains class named classB + from modA import classA)
test1.py (from Lib.modA import classA
from Lib.modB import classB)
|-> [example]
test2.py (import sys
sys.path.append("../")
from Lib.modA import classA
from Lib.modB import classB)
来自 Lib 文件夹的 运行 test1.py 完美运行(与没有 warnings/errors/etc 一样)。另一方面,示例文件夹中的 运行 test2.py 需要系统补丁,因为 python 的工作方式很奇怪,并且从 from modA import classA
(在 modB.py 中)通过 from Lib.modB import classB
(在 test2.py 中)。
人们应该如何在模块中定义一个导入,以便它也可以工作,而不管可能 use/import 所述模块的任何未来脚本的可能位置如何?
据我了解,当您尝试手动 运行 时,没有将路径设置为环境,而当您只是这样做时
python file.py
python 不知道我们 运行ning 在哪个环境中,这正是我们 sys.path.append 将兄弟目录路径添加到 env.
如果您使用的是 IDE,如 spyder 或 pycharm。您可以选择设置程序所在的目录,或者在 pycharm 中将整个程序创建为一个包,其中所有路径都由 IDE.
处理Python 程序应该被认为是 包 和 模块 ,而不是 目录 和 文件 。虽然有一些重叠,但包和模块的限制更多,但也因此封装得更好。
混合使用两者——比如手动修改 sys.path
——只能作为最后的手段。
TLDR:
- 使用完全限定的导入:
from Lib.modA import classA
而不是from modA import classA
。 - 使用环境进行发现:通过
PYTHONPATH
而不是sys.path
添加搜索路径。
首先确定哪些是 top-level 包。
这是我们从“directories/files”到“包”的地方。值得注意的是,稍后我们不能再“高于”top-level,因此它应该包含我们需要的一切。但是,我们也不能删除“下方”的任何内容,因此它应该是一个足够严格的选择。
在示例中,我们可以低至将 modA
和兄弟姐妹视为自己的 module-package,高至 FOLDER
包含整个项目。
[FOLDER]
|-> [Lib]
| |-> modA.py
: :
选择Lib
是合理的,因为它代表了self-contained部分。升到 FOLDER
会过分,降到 modA
和兄弟姐妹会打破他们属于一起的关系。
top-level 包文件夹下的所有内容现在都属于该包。
值得注意的是,modA.py
现在是模块 Lib.modA
。它不是 FOLDER.Lib.modA
,也不只是 modA
。
仅使用绝对完全限定名称或相对名称进行导入。
既然top-level成立了,所有的import
声明都得跟它说。仅通过 完全限定 名称引用模块,即使两个模块共享一个更常见的父模块也是如此:
# Lib.modB
# okay, fully qualified import
from Lib.modA import classA
# broken, unqualified import
from modA import classA
为了避免键入完整的完全限定名称,可以使用相对 名称代替。这会重新使用当前模块名称来派生要导入的模块的完全限定名称。
# Lib.modB
# okay, relative import
from .modA import classA
注意: 相对导入是 package 操作,而不是 filesystem 操作。不能超越 top-level,而是进入包命名空间。
模块内的所有内容现在都被封装并且self-contained。
无论包本身的位置如何,所有内部导入都将起作用。只要top-level可以导入,下面的都可以导入。
值得注意的是,这与可能 use/import 包的任何未来脚本的位置无关。
通过环境而不是程序启用包。
定义包的要点是获得一个代表库的 self-contained 实体。一个人可以压缩一个包裹或类似的东西,它仍然是一个包裹。
与其让脚本假定包的位置,不如让环境(脚本、用户或整台机器)公开包。基本上有两种方法可以做到这一点:
- 公布包裹位置为搜索位置。这适合开发,因为它灵活但难以维护。
PYTHONPATH
适用于此。 - 将包移动到Python使用的搜索位置。这适用于生产和分发,因为它需要付出努力但易于维护。 Packaging and using a package manager 适用于此。