【问题标题】:Python Relative Path Import: Import packages from another project directoryPython 相对路径导入:从另一个项目目录导入包
【发布时间】:2020-10-23 14:04:31
【问题描述】:

我知道关于这个主题有很多问题,但没有一个对我有太大帮助。

我有一个 python 项目目录,即git_project(git 存储库)。我想要的是创建一个名为notebooks 的单独目录,我将在其中保留所有笔记本以使用 git_project 进行分析。我不想将笔记本放在 git_project 的根目录中。我将git_projectnotebooks 目录都保存在一个保存我所有项目的通用目录中。我有以下结构:

my_projects
│ 
├── notebooks
│   └── notebook.ipynb
└── git_project
    └── config
        └── cfg.json
    └── source
        └── config.py

config.py的内容:

import json
def get_cfg():
     with open('config/cfg.json', 'r') as f:
         cfg = json.load(f)
     return cfg

notebook.ipynb的内容:

import sys
import os

module_path = os.path.abspath(os.path.join('..'))
if module_path not in sys.path:
    sys.path.append(module_path)

from git_project.source.config import get_cfg
get_cfg()

现在,当我在 notebook.ipynb 中运行代码时,出现以下错误:

---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
<ipython-input-7-6796ee7f0100> in <module>
----> 1 get_cfg()

~/Documents/my_projects/git_project/source/config.py in get_cfg()
      1 def get_cfg():
----> 2     with open('config/cfg.json', 'r') as f:
      3         cfg = json.load(f)
      4     return cfg

FileNotFoundError: [Errno 2] No such file or directory: 'config/cfg.json'

但是,如果我将 notebook.ipynb 文件移动到 git_project 的根目录。然后我没有收到此错误。这只是一个例子。我在 git_project 的其他模块中有很多类似的问题,而 git_project 包含已经在生产环境中运行的代码。所以在这里改变 git_project 中的任何东西都是不可行的。 但正如我所说,我不想将笔记本移动到 git_project 中,而是希望将它们保存在并行目录中以进行分析。如果需要,我可以提供更多信息。

我正在使用 Python 3.6+,它甚至不需要再放入 init.py 文件来制作目录包。

我应该怎么做才能使它起作用?任何帮助将不胜感激。

【问题讨论】:

  • 尝试../git_project/config/cfg.json 获取get_cfg() 中的路径。但理想情况下,get_cfg(path) 会采用路径变量,这样您就可以轻松地从各个位置调用它,而无需编辑函数本身。
  • 确保您的git_project 在根目录中有__init__.py 文件
  • 即使使用相对路径的方法有效,但通常最好使用更明确的方式 - 例如 environment variable 与您的项目根目录,在这种情况下,您的笔记本真正独立于项目并且可以尽可能多地使用。只需以您喜欢的任何方式(stackoverflow.com/questions/37890898/…)设置MY_PROJECT_ROOT 环境变量,然后使用getenv('MY_PROJECT_ROOT') 而不是os.path.join('..')
  • @Ahmet:但是如果我改变路径,它不会依赖于笔记本目录位置吗?
  • @EugeneK:我不明白你的意思是如何使用 Jupyter notebook 中的环境变量?可以举个例子吗?

标签: python jupyter-notebook


【解决方案1】:

问题是当您调用open('config/cfg.json', 'r') 时,它打开的路径是相对于启动python 代码的目录的。在这种情况下,它是您的my_projects/notebook 目录。您可以通过在get_cfg() 内的config.py 中添加以下打印来查看这一点:

print(os.getcwd())  # this prints out the current working directory
print(__file__)     # this prints out the path of this script

正如 Ahmet 建议的那样,修改 ../git_project/config/cfg.json 的路径将起作用,但您的 python 实现将绑定到笔记本文件夹位置。如果您决定重组笔记本文件夹,它将再次中断。 一种可能的方法是解析脚本路径:__file__:

import json
import os
def get_cfg():
    script_dirname = os.path.dirname(__file__)
    config_path = os.path.join(script_dirname, '..', 'config', 'cfg.json')
    with open(config_path, 'r') as f:
        cfg = json.load(f)
    return cfg

类似的建议:(Reading file using relative path in python project)。这也是python-packaging docs中建议的方法:

您安装的库将使用的文件(例如数据文件 支持特定的计算方法)通常应该放置 在 Python 模块目录本身内部。 ... 这样,加载这些文件的代码可以很容易地指定一个相对的 来自消费模块的 __file__ 变量的路径。

如果你不想接触git_project 中的当前文件,你可以在你的python notebook 中运行一个更改目录命令来指向正确的位置:

In [1]: %cd ../git_project

每次重新启动笔记本内核时都需要调用此行。您也可以验证笔记本中的当前工作目录:

In [2]: %ls

【讨论】:

  • 实际上这种方法有效,但问题是我无法更改 git_project 中的代码,因为存在许多此类导入的实例。从分析的角度更改代码也会影响生产运行,因此需要进一步测试。所以我正在寻找一种可能的解决方案,它不会强迫我在 git_project 中进行太多更改,并让我将 git_project 用作​​外部包/库。
  • 这种情况下,更改笔记本中的目录可以吗?在运行您的代码之前,我刚刚在笔记本中运行了%cd ../git_project,并且路径解析得很好。将 git_project 移动到包时,您可能仍需要使用我原来的建议。
  • 按照您的原始建议,您的意思是更改 git_project 中 config.py 文件的内容吗?那是不可行的。但是您后来的建议%cd ../git_project 效果很好。我更喜欢它,尽管我不知道这是否是在某些人使用多个项目的情况下的标准工作方式,例如git_project1, git_project2 来自笔记本。
  • 这不是标准,但我用它来快速运行。建议的方法是编辑 config.py 文件(请参阅更新答案中的引用)。这也解决了多个项目的问题,因为文件加载将不再依赖于您当前工作目录的位置。
【解决方案2】:

作为 cmets 讨论的后续内容...

即使具有相对路径的方法有效,但通常最好使用更具可扩展性的方法 - 项目根目录的环境变量。

在这种情况下,您的笔记本真正独立于项目,并且可以与任意数量的笔记本一起使用。 这是一个很棒的 explanation 如何在 Jupiter 中使用 ENV 变量。 我更喜欢使用dotenv 方法。 在您的笔记本文件夹中创建 .env。在项目路径中添加变量:

MY_PROJECT_ROOT=/usr/any/path/you/want

然后在你的笔记本中

import os
from dotenv import load_dotenv
load_dotenv()  # this line loads .env file

然后是你的代码

module_path = os.path.abspath(os.getenv('MY_PROJECT_ROOT'))
if module_path not in sys.path:
    sys.path.append(module_path)

【讨论】:

  • 我用 os.path.abspath(os.path.join('..')) 尝试的不是几乎一样吗?但它只是将项目目录添加到 sys.path。但问题依然存在。
【解决方案3】:

一个简单的解决方案是更改工作目录。

最初的工作目录是 notebooks 目录:

from sys import path
import os
print("Current Working Directory " , os.getcwd())

输出:

Current Working Directory  /home/user/git_project/notebooks

然后将其更改为项目的根目录:

os.chdir(os.path.dirname(path[0]))
print("New Working Directory " , os.getcwd())

输出:

New Working Directory  /home/user/git_project

之后,所有在项目根目录上具有相对路径的导入都应该可以工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-15
    • 2014-02-21
    相关资源
    最近更新 更多