导入一个位于当前文件夹的兄弟文件夹中的库。

9

使用文件夹结构

lib/
    abcd/
        __init.py__
        lib.py
app.py

代码
from lib.abcd import lib

这个工作。但是有了这个文件结构:

bin/
    app.py
lib/
    abcd/
        __init.py__
        lib.py

代码
from ..lib.abcd import lib     

出现了导入错误。

当库在当前文件夹的兄弟文件夹中时,应该如何正确地进行import(或者在兄弟文件夹的子文件夹中)

我知道可能有一些hack方法可以将lib/添加到PATH中,但是是否有优雅的Pythonic解决方案呢?

如果没有,那么是否有真正的内部原因阻止用户以简单的方式进行这个简单的导入操作呢?


2
通常情况下你是做不到的。因为你的顶级目录是 bin。要实现它,你需要将父目录添加到 sys.path 中。然后使用 from lib.abcd import lib 进行导入。 - Sraw
1
感谢您的评论@Sraw。难道没有更简单的解决方案吗? - Basj
4
当然有的,把 lib 文件夹移动到 bin 文件夹里。我明白你的项目结构。如果你想要坚持这个结构,我建议你添加一个用于启动的 bash 脚本。在脚本中,你可以使用 PYTHONPATH=your_parent_directory python app.py 来启动你的应用程序。 - Sraw
只是想澄清一下,根据代码和这些注释:这里没有父文件夹吗?这是你项目的完整文件夹结构吗? - Chris Larson
2
在我看来,你做错了。你应该区分源代码的布局和已安装软件的布局。当你安装软件时,lib 将被安装在 Python 解释器的 site-packages 中,然后你就完成了,你可以将 app.py 放在标准的 bin 目录中,它将始终独立于其所在位置工作。在开发过程中,你只需将根目录标记为源根目录,或者使用虚拟环境,在其中执行 pip install -e 即可。 - Bakuriu
显示剩余2条评论
2个回答

2

实现方法


方法一:使用sys模块:您可以轻松地使用sys模块来完成您想要做的事情。要导入lib包,您可以使用以下两个代码之一:

import sys
sys.path.append('<PATH_TO_LIB_FOLDER>')

from lib.abcd import lib

或者

import sys
sys.path.insert(0, '<PATH_TO_LIB_FOLDER>')

方法二:使用os模块:另一种方法是使用os模块。以下是一个示例代码,它使用os模块通过调用os.path.join方法导入lib模块:


import os

path = os.path.join("<PATH>/lib/abcd", "lib")
from lib.abcd import lib

方法三:将模块添加到PYTHONPATH中:在大多数情况下,这不是最佳方法,但如果您不想继续使用sysos模块来导入lib,这是理想的。您只需要在bash终端中键入以下内容:

export PYTHONPATH=/path/to/your/lib:$PYTHONPATH

请注意,/path/to/your/lib应替换为您实际的库路径。

export PYTHONPATH=<PATH_TO_LIB> python lib.py

最初的回答: 在Python shell中,您可以像这样导入它:

然后在您的Python shell中,您可以像这样导入它:

from lib.abcd import lib

方法 #4:结合sys和os模块(推荐):这是最有效的方法,将为您节省大量时间。此代码将ossys模块组合在一起,如下所示:

最初的回答:要结合使用ossys模块,可以使用以下代码:

import sys, os
sys.path.append(os.path.abspath(os.path.join('..', 'lib')))

然后,您可以轻松地导入您的模块,就像这样:

然后您可以像这样轻松导入您的模块:

from lib.abcd import lib

所有代码的工作原理:

以上所有代码都非常简单。除了"方法#3"之外,所有示例都会临时将您的模块添加到PYTHONPATH中。"方法#3"则会永久将该模块添加到您的PYTHONPATH中。



你加入了哪些新的信息?这两个都是路径操作,我已经提到了,而且不是OP所要求的内容。 - Salvatore
方法#2并没有将任何内容添加到您的PYTHONPATH中。它所做的只是创建一个字符串path,例如在*nix上,"<PATH>/lib/abcd/lib"。至于方法#3,您可能不应该覆盖PYTHONPATH,而是应该在其前面添加。 - Nathan
方法2与问题完全无关(至少os部分如此)。 - CristiFati
@CristiFati -- 方法2只是让它工作的一种方式。 - xilpex
也许吧,但是os.path.join在那里完全没有用处,因此需要使用os和该方法本身。 - CristiFati
这些都是在运行时添加到路径中的,而您的第三个答案假定了一个特定配置的Bash环境,这不是OP的问题的一部分。 - Salvatore

1

表面层次的看法

通常情况下,你不能这样做。当导入一个文件时,Python只会搜索当前目录、入口脚本所在的目录和包含位置(例如包安装目录)的sys.path路径(实际上比这个更复杂,但这覆盖了大多数情况)。

你不会看到导入已安装模块的问题,因为它们安装在已经在您的路径上的位置,或者安装工具(例如pip)将其位置添加到路径中。

你可以在运行时添加Python路径:

    import sys
    sys.path.insert(0, '../lib')

    import file  

你也可以使用 sys.path.append('../lib'),但是它会在你的路径中最后被搜索,并可能被之前路径中的条目覆盖。
我已经仔细阅读了导入文档,就我所知,答案是否定的,没有一种纯粹的“Pythonic”方法来做到这一点。
更深入的探讨:
深入研究导入文档可以解释这一点:
引入语句结合了两个操作; 它搜索命名模块,然后将该搜索结果绑定到本地作用域中的名称。 导入语句的搜索操作被定义为调用__import __()函数,带有适当的参数。__import__()的返回值用于执行导入语句的名称绑定操作。
更仔细地查看__import__

__import__(name, globals=None, locals=None, fromlist=(), level=0)

注意:这是一个高级函数,在日常Python编程中不需要使用,与importlib.import_module()不同。

该函数由import语句调用。可以通过导入builtins模块并分配给builtins.__import__来替换它以更改import语句的语义,但强烈不建议这样做,因为通常使用导入钩子(参见PEP 302)实现相同目标更简单,并且不会对假定默认导入实现正在使用的代码造成问题。也不鼓励直接使用__import__(),而应优先使用importlib.import_module()

level指定是否使用绝对或相对导入。 0(默认值)表示仅执行绝对导入。 level为正数表示要搜索的父目录数量,相对于调用__import__()的模块的目录(有关详细信息,请参见PEP 328)。

这让我想到你可以以某种方式指定一个级别,使导入自动查找父级路径。我猜当从不在自己目录下的app.py中调用import时,level被设置为0,它会搜索相同级别及更深的级别。

当你从子文件夹中的app.py调用import时,level仍然设置为0,因此无法找到其上方的其他目录。我正在研究一种“Python式”方法来在运行脚本时将此级别设置为1,这似乎可以解决这个问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接