在python中,改变工作目录时如何处理相对路径变化

问题描述 投票:0回答:1

我知道我们可以使用绝对路径来处理问题,但我想知道是否有更好的方法,特别是当我们考虑分发代码时

详细问题来了

项目结构

/a
utils.py
        /b/b.json
        /c/c.ipynb

a.py 中,有一行

open("./b/b.jon", "r")

c.ipynb 中,有一行

import utils.py
(该行自 pycharm 广告
/a
进入系统路径以来一直有效)

但是,如果我在

c.ipynb
中运行 import utils.py,则会出现错误
FileNotFoundError: [Errno 2] No such file or directory: ./b/b.json

我明白这是预期的,因为当前工作目录是

/a/c
,而不是
/a
。所以解析
a.py
中的相对路径时,就会变成
/a/c/b/b.json
。但是有没有更优雅的方法,而不是设置像
PROJECT_ROOT
这样的全局变量来处理这个问题呢? (
__file__
在这种情况下不起作用,因为它是jupyter笔记本。)

一般来说,开发项目和处理相对导入的最佳实践是什么,特别是在处理 jupyter 笔记本时(我目前的理解是修改 PYTHONPATH 进行相对导入,但同样,文件读取问题怎么办?)

python-3.x python-import relative-path
1个回答
0
投票

初步分析

你的问题有很严重的问题,很不清楚。 (也许,一些可重现的代码片段会对您的情况有所帮助?)


有了这个,我认为按照你的建议设置一个全局变量将是正确的方法,因为Jupyter Notebook只会在当前目录中查找通常创建的包/库/模块导入(特别是像简单如另一个 Python 文件)。[1]

现在解决问题:

  1. 您的项目结构图没有所谓的“a.py”,因此此时它是一个幻影。但是,除非另有说明,我假设它位于
    /a
    目录下:
/a
𝗮.𝗽𝘆
utils.py
     /b/b.json
     /c/c.ipynb
  1. 这条线应该是
    open("./b/b.𝗷𝘀𝗼𝗻", "r")

a.py中,有一行

open("./b/b.jon", "r")

  1. c.ipynb正在调用神秘的a.py吗?除非另有说明,否则我将假设如此。尽管如此,尚不清楚为什么
    c.ipynb
    会抛出 FileNotFoundError: [Errno 2] No such file or directory: ./b/b.json(根据您的问题),是 a.py 使用
    open("./b/b.json", "r")
    创建文件流请求。

如果我在 c.ipynb 中运行 import utils.py,则会出现错误

FileNotFoundError: [Errno 2] No such file or directory: ./b/b.json

  1. 我们必须在笔记本目录之外的文件中使用动态绝对导入,然后根据需要附加
    /.
    /..
    以使笔记本可以访问它。然后手动将外部目录添加到笔记本中的系统路径(这是这个答案中建议的技术)。

我理解这是预期的,因为当前工作目录是

/a/c
,而不是/a。所以解析a.py中的相对路径时,就会变成
/a/c/b/b.json
.

  1. 在这种情况下,优雅是主观的,但在这种情况下,
    pathlib.Path
    os.path()
    是你的(最好的)朋友。

但是有没有更优雅的方式[...]?

  1. 最后,我专业地构建笔记本的方式是始终将我想要导入的文件夹和目录放置在与笔记本相同的级别中。只要它们组织良好,这种方式比将它们放在笔记本目录之外更好。

建议的解决方案

由于您没有提供最小的代码示例,我的实验设置如下:

Experimental Setup

  • a.py 将包含一个打印 b.json 内容的函数(如前所述,这是假设 a.py 在被 c.ipynb 调用之前请求文件流) ).
# \a -> a.py

import os
import json
from pathlib import Path


def print_json():
    # Relative path; Mr. Jupyter hates this so don't do it
    # json_file = open("./b/b.json")

    # Absolute JSON file path
    json_file_path = Path(os.getcwd() + "/.././/b/b.json").__str__()
    json_file = open(json_file_path)
    json_object = json.load(json_file)

    return json.dumps(json_object, indent=1)

  • utils.py 将包含一个简单的算术函数,我们将尝试在 c.ipynb 中导入该函数。
# \a -> utils.py

def add_mult(x, y, z):
    return x * y + z
  • b.json 将包含一些简单的文本,如 JSON.org 上所示(或简单地构成您的文本)。

  • 然后在c.ipynb中,我们有:

# \a\c -> c.ipynb

import sys, os

# From \c, step out to \a, step out yet again outside \a
import_path = os.path.abspath(os.path.join("..\.."))

# Add the obtained parent path to sys path to force-expose \a contents
if import_path not in sys.path: sys.path.append(import_path + "\\")

# Import away!
from a.utils import add_mult
from a.a import print_json

# 1 * 2 + 3
ans = add_mult(1,2,3)
print(f"1 * 2 + 3 = {ans}\n")

# Print formatted contents of \a\b\b.json
json_text = print_json()
print(json_text)

输出:

Output of c.ipynb

© www.soinside.com 2019 - 2024. All rights reserved.