相对导入和模块重新加载
Relative importing and module reloading
我有一个机器学习项目,我必须进行 m.l。实验。每个实验都是用一个强烈依赖于存储库中其他文件的模块进行的。为了可重复性,我将每个实验视为存储库的快照。所以稍后我想重现该实验并将其与 ONE 脚本中的另一个实验进行比较。为此,我在特殊文件夹 reproduce/experiment_name
中恢复实验时存储库的状态并组织以下结构:
├── launcher.py
├── package
│ ├── dependency.py
│ └── subpackage
│ └── module.py
└── reproduce
├── experiment_23
│ └── package
│ ├── dependency.py
│ └── subpackage
│ ├── module.py
└── experiment_24
└── package
├── dependency.py
└── subpackage
└── module.py
所以在 launcher.py
中,我想导入 experiment_23
的内容,给定 importlib.utils
所需模块的路径。但我需要保留它的依赖关系,以便旧代码在它自己的领域中工作。我想出了一个想法来修改 sys.path
并在导入之前添加路径,然后将其删除。这里是launcher.py
.
的内容
import sys
import importlib.util
path_to_module = "/package/subpackage/module.py"
experiment_path = "/reproduce/exp23"
project_path = "/home/user/PycharmProjects/PathTest"
# Here I add path to experiment directory
sys.path.insert(0, project_path + experiment_path)
spec = importlib.util.spec_from_file_location("", project_path + experiment_path + path_to_module, )
exp23 = importlib.util.module_from_spec(spec)
spec.loader.exec_module(exp23)
# Clear path
sys.path = sys.path[1:]
# This runs successfully
exp23.run()
# So now i want to import my newest version of the module.py
# And this fails, meaning it executes code from exp23
import package.subpackage.module as exp
exp.run()
module.py
的内容
from package.dependency import function
def run():
function()
和dependency.py
是成功标志:
def function():
print("23")
所以问题是一旦完成导入并加载依赖项 from package.dependency import function
,python 将不再在 sys.path
中查找它并使用某种缓存。我的问题是在这种情况下如何避免使用缓存,或者至少如何使用不同的方法实现类似的功能?
因此,首先,我假设您在每个 package
和 subpackage
目录中都有一个 __init__.py
,以使它们成为实际的 "packages";按照指定 here.
现在,您应该注意到包含 module.py
的 subpackage
是 package
的实际子包。因此,当您执行 from package.dependency import ...
时,您正在尝试从父包导入。这不仅是一种不好的做法,而且您在 launcher.py
中使用的路径仅直接针对 module.py
,它不知道 package
是什么,甚至 subpackage
就此而言。
您应该定位的是 package
,它(再次假设您在其中有一个 __init__.py
)将作为实际 "package" 加载。使其内部的所有内容都可以正常访问...经过一些调整。
在您的 __init__.py
中,您应该公开您希望此模块包含的所有内容。例如,在您的 package/__init__.py
中,您应该有行 from . import subpackage
。这不仅可以让您执行 package.subpackage
之类的操作,而且对于加载 subpackage
.
的内容也至关重要
按照同样的逻辑,在你的subpackage/__init__.py
中应该有from . import module
,加载和暴露module.py
,允许你做package.subpackage.module
。
这应该是您正确加载和访问所有所需内容所需的全部...除了您还将所有内容包装在 experiment_n
文件夹中。在这里你有两个选择:你可以制作 experiment_n
一个带有 __init__.py
的包,公开它的 package
并用 importlib
定位它,或者你可以重命名 package
experiment_n
并删除顶级文件夹。从这里开始:
experiment_23
└── package
├── __init__.py
├── dependency.py
└── subpackage
├── __init__.py
└── module.py
为此:
experiment_23
├── __init__.py
├── dependency.py
└── subpackage
├── __init__.py
└── module.py
让我们从文件夹编辑中休息一下,看看您将如何在代码中使用我们当前的迭代。考虑以下 launcher.py
:
import sys, os
import importlib.util
current_directory = os.path.dirname(os.path.realpath(__file__))
path_to_experiment = os.path.join(current_directory, 'reproduce/experiment_23')
path_to_experiment = os.path.join(path_to_experiment, '__init__.py') # package!
spec = importlib.util.spec_from_file_location('exp23', path_to_experiment, submodule_search_locations = [])
exp23 = importlib.util.module_from_spec(spec)
sys.modules[exp23.__name__] = exp23
# old one
spec.loader.exec_module(exp23)
exp23.subpackage.module.run()
# new one
import package.subpackage.module as exp
exp.run()
主要功能打印 main
和复制一个打印 exp23
。
首先,我们编写 path_to_experiment
,指向我们包的 __init__.py
。 module_from_file_location
需要 文件 ,而不是文件夹,因此我们不直接以 /expriment_23
为目标。之后,使用实际名称 (exp23
),我们得到带有 spec_from_file_location
的 spec
,将 submodule_search_locations
设置为空列表以指示它是指定的包 here.最后,使用 module_from_spec
.
获取我们的模块
将新模块添加到 sys.modules
很重要,这样相对导入(我们使用的)就可以找到它们的父模块。
我们让我们的模块执行并调用它和我们新模块的 run
函数:
main
main
好像不行,我们希望第一个是exp23
罪魁祸首是我们 experiment_23/subpackage/module.py
中的 from package.dependency import function
行。这里的导入是 absolute
,而不是 relative
,这意味着它会在 / 中查找 / 或导入 "package"
到 sys.modules
并使用它。在本例中,this 直接指向我们的主包。将其更改为 from ..dependency import function
并重新 运行 launcher.py
给我们:
exp23
main
成功:)
导入系统可能会非常混乱,花点时间在 import system docs and importlib docs 上会让您在遇到类似情况时省去很多麻烦。
作为可选建议,您可以将 module.py
移入 experiment_23
并移出 subpackage
并删除 subpackage
。现在你可以做 from .dependency import function
。有些人会争辩说,进行超相对导入(使用多个 .
)是不好的做法,或者至少表明对包结构的理解不足。
编辑:应用于 experiment_23
的所有更改都对应于主要实验的包和所有其他 experiment_n
...
我有一个机器学习项目,我必须进行 m.l。实验。每个实验都是用一个强烈依赖于存储库中其他文件的模块进行的。为了可重复性,我将每个实验视为存储库的快照。所以稍后我想重现该实验并将其与 ONE 脚本中的另一个实验进行比较。为此,我在特殊文件夹 reproduce/experiment_name
中恢复实验时存储库的状态并组织以下结构:
├── launcher.py
├── package
│ ├── dependency.py
│ └── subpackage
│ └── module.py
└── reproduce
├── experiment_23
│ └── package
│ ├── dependency.py
│ └── subpackage
│ ├── module.py
└── experiment_24
└── package
├── dependency.py
└── subpackage
└── module.py
所以在 launcher.py
中,我想导入 experiment_23
的内容,给定 importlib.utils
所需模块的路径。但我需要保留它的依赖关系,以便旧代码在它自己的领域中工作。我想出了一个想法来修改 sys.path
并在导入之前添加路径,然后将其删除。这里是launcher.py
.
import sys
import importlib.util
path_to_module = "/package/subpackage/module.py"
experiment_path = "/reproduce/exp23"
project_path = "/home/user/PycharmProjects/PathTest"
# Here I add path to experiment directory
sys.path.insert(0, project_path + experiment_path)
spec = importlib.util.spec_from_file_location("", project_path + experiment_path + path_to_module, )
exp23 = importlib.util.module_from_spec(spec)
spec.loader.exec_module(exp23)
# Clear path
sys.path = sys.path[1:]
# This runs successfully
exp23.run()
# So now i want to import my newest version of the module.py
# And this fails, meaning it executes code from exp23
import package.subpackage.module as exp
exp.run()
module.py
from package.dependency import function
def run():
function()
和dependency.py
是成功标志:
def function():
print("23")
所以问题是一旦完成导入并加载依赖项 from package.dependency import function
,python 将不再在 sys.path
中查找它并使用某种缓存。我的问题是在这种情况下如何避免使用缓存,或者至少如何使用不同的方法实现类似的功能?
因此,首先,我假设您在每个 package
和 subpackage
目录中都有一个 __init__.py
,以使它们成为实际的 "packages";按照指定 here.
现在,您应该注意到包含 module.py
的 subpackage
是 package
的实际子包。因此,当您执行 from package.dependency import ...
时,您正在尝试从父包导入。这不仅是一种不好的做法,而且您在 launcher.py
中使用的路径仅直接针对 module.py
,它不知道 package
是什么,甚至 subpackage
就此而言。
您应该定位的是 package
,它(再次假设您在其中有一个 __init__.py
)将作为实际 "package" 加载。使其内部的所有内容都可以正常访问...经过一些调整。
在您的 __init__.py
中,您应该公开您希望此模块包含的所有内容。例如,在您的 package/__init__.py
中,您应该有行 from . import subpackage
。这不仅可以让您执行 package.subpackage
之类的操作,而且对于加载 subpackage
.
按照同样的逻辑,在你的subpackage/__init__.py
中应该有from . import module
,加载和暴露module.py
,允许你做package.subpackage.module
。
这应该是您正确加载和访问所有所需内容所需的全部...除了您还将所有内容包装在 experiment_n
文件夹中。在这里你有两个选择:你可以制作 experiment_n
一个带有 __init__.py
的包,公开它的 package
并用 importlib
定位它,或者你可以重命名 package
experiment_n
并删除顶级文件夹。从这里开始:
experiment_23
└── package
├── __init__.py
├── dependency.py
└── subpackage
├── __init__.py
└── module.py
为此:
experiment_23
├── __init__.py
├── dependency.py
└── subpackage
├── __init__.py
└── module.py
让我们从文件夹编辑中休息一下,看看您将如何在代码中使用我们当前的迭代。考虑以下 launcher.py
:
import sys, os
import importlib.util
current_directory = os.path.dirname(os.path.realpath(__file__))
path_to_experiment = os.path.join(current_directory, 'reproduce/experiment_23')
path_to_experiment = os.path.join(path_to_experiment, '__init__.py') # package!
spec = importlib.util.spec_from_file_location('exp23', path_to_experiment, submodule_search_locations = [])
exp23 = importlib.util.module_from_spec(spec)
sys.modules[exp23.__name__] = exp23
# old one
spec.loader.exec_module(exp23)
exp23.subpackage.module.run()
# new one
import package.subpackage.module as exp
exp.run()
主要功能打印 main
和复制一个打印 exp23
。
首先,我们编写 path_to_experiment
,指向我们包的 __init__.py
。 module_from_file_location
需要 文件 ,而不是文件夹,因此我们不直接以 /expriment_23
为目标。之后,使用实际名称 (exp23
),我们得到带有 spec_from_file_location
的 spec
,将 submodule_search_locations
设置为空列表以指示它是指定的包 here.最后,使用 module_from_spec
.
将新模块添加到 sys.modules
很重要,这样相对导入(我们使用的)就可以找到它们的父模块。
我们让我们的模块执行并调用它和我们新模块的 run
函数:
main
main
好像不行,我们希望第一个是exp23
罪魁祸首是我们 experiment_23/subpackage/module.py
中的 from package.dependency import function
行。这里的导入是 absolute
,而不是 relative
,这意味着它会在 / 中查找 / 或导入 "package"
到 sys.modules
并使用它。在本例中,this 直接指向我们的主包。将其更改为 from ..dependency import function
并重新 运行 launcher.py
给我们:
exp23
main
成功:)
导入系统可能会非常混乱,花点时间在 import system docs and importlib docs 上会让您在遇到类似情况时省去很多麻烦。
作为可选建议,您可以将 module.py
移入 experiment_23
并移出 subpackage
并删除 subpackage
。现在你可以做 from .dependency import function
。有些人会争辩说,进行超相对导入(使用多个 .
)是不好的做法,或者至少表明对包结构的理解不足。
编辑:应用于 experiment_23
的所有更改都对应于主要实验的包和所有其他 experiment_n
...