ChatGPT解决这个技术问题 Extra ChatGPT

从相对路径导入模块

如何在给定相对路径的情况下导入 Python 模块?

例如,如果 dirFoo 包含 Foo.pydirBar,并且 dirBar 包含 Bar.py,我如何将 Bar.py 导入 Foo.py

这是一个视觉表示:

dirFoo\
    Foo.py
    dirBar\
        Bar.py

Foo 希望包括 Bar,但重组文件夹层次结构不是一种选择。

看起来像 stackoverflow.com/questions/72852/…,也许?
检查我的答案,这是迄今为止最完整的,其他人在特殊情况下不起作用,例如当您从另一个目录或另一个 python 脚本调用脚本时。请参阅stackoverflow.com/questions/279237/…
我有一个类似的问题,我发现了这个并且它有效! apt-get 安装 python-profiler
以防万一有人想静态地做它并到达这里(就像我做的那样:)你也可以设置 PYTHONPATH 环境变量
更好的是针对每种情况遵循 Lib/site.py 中的说明

P
Peter Mortensen

假设您的两个目录都是真正的 Python 包(其中确实有 __init__.py 文件),这是相对于脚本位置包含模块的安全解决方案。

我假设您想要这样做,因为您需要在脚本中包含一组模块。我在多个产品的生产中使用它,并在许多特殊场景中工作,例如:从另一个目录调用的脚本或使用 python 执行的脚本,而不是打开一个新的解释器。

 import os, sys, inspect
 # realpath() will make your script run, even if you symlink it :)
 cmd_folder = os.path.realpath(os.path.abspath(os.path.split(inspect.getfile( inspect.currentframe() ))[0]))
 if cmd_folder not in sys.path:
     sys.path.insert(0, cmd_folder)

 # Use this if you want to include modules from a subfolder
 cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"subfolder")))
 if cmd_subfolder not in sys.path:
     sys.path.insert(0, cmd_subfolder)

 # Info:
 # cmd_folder = os.path.dirname(os.path.abspath(__file__)) # DO NOT USE __file__ !!!
 # __file__ fails if the script is called in different ways on Windows.
 # __file__ fails if someone does os.chdir() before.
 # sys.argv[0] also fails, because it doesn't not always contains the path.

作为奖励,这种方法确实让您可以强制 Python 使用您的模块,而不是安装在系统上的模块。

警告!当当前模块位于 egg 文件中时,我真的不知道发生了什么。它可能也失败了。


我能解释一下这是如何工作的吗?我遇到了类似的问题,我很想强制使用 python 模块 DIR 而不是运行搜索
运行 Win 7 Pro 64x 和 Python 2.7 我得到了一些错误。 1)我必须将检查添加到导入列表中。 2) 元组中的第一个值 [0] 是一个空字符串。第二个,[1],显示文件名。我猜第一个应该是路径......有什么想法吗?
如果您要创建子文件夹,请像这样使用它:os.path.realpath(os.path.abspath(os.path.split(inspect.getfile( inspect.currentframe() ))[0]) + "/subfolder")NOTabspath 之前添加子文件夹,因为这会导致严重的错误。
@scr4ve 您应该改用 os.path.join() ,欢迎您将案例(cmd_subfolder)直接添加到我的答案中。谢谢!
对我来说 realpath 已经生成了绝对路径,因此我不需要 abspath。还可以使用 os.path.dirname 代替 split,从而使索引 [0] 过时。该行将是:os.path.realpath(os.path.dirname(inspect.getfile(inspect.currentframe())))
S
S.Lott

确保 dirBar 具有 __init__.py 文件——这会将目录变成 Python 包。


请注意,此文件可以完全为空。
如果 dirBar 的父目录不在 sys.path 中,那么 __init__.pydirBar 目录中的存在没有多大帮助。
-1,仅当目录已经在 sys.path 中时添加 __init.py__ 才有效,而在我的情况下它不是。 “索林”(已接受)的解决方案总是有效的。
“当目录已经在 sys.path 中时”。虽然完全正确,但我们怎么能从问题中猜出该目录不在 sys.path 中?也许有一些我们没有看到或不知道的遗漏?
这绝不是问题的答案:无论初始化文件是否存在,这都不会让 python 查看子目录。它是如何获得数百个赞成票的?
L
Louis Maddox

您还可以将子目录添加到 Python 路径中,以便将其作为普通脚本导入。

import sys
sys.path.insert(0, <path to dirFoo>)
import Bar

看起来您的答案不适用于相对路径,请参阅 stackoverflow.com/questions/279237/…
这确实适用于相对路径——您只需要了解相对路径将取决于您从哪个目录运行,这使得除了快速破解之外的任何其他解决方案都是一个糟糕的解决方案。
您可以执行 sys.path.append(os.path.dirname(__file__) + "/relative/path/to/module") 之类的操作
sys.path.append(__name__ if __name__ != '__main__' else __loader__.fullname)
考虑使用 sys.path.insert(0, <path to dirFoo>),因为它将在存储在其他地方的同名模块之前加载此模块。
B
Brian
import os
import sys
lib_path = os.path.abspath(os.path.join(__file__, '..', '..', '..', 'lib'))
sys.path.append(lib_path)

import mymodule

我喜欢这个,因为你可以灵活地上一个目录。
您应该使用 os.path.join() 而不是通过“/”加入,这会破坏(蹩脚的)窗口。
这不可靠。它取决于当前工作目录是什么,而不是脚本所在的目录。
如果路径不在路径中,可能只想添加。 lib_path = os.path.abspath('../functions') 如果 lib_path 不在 sys.path 中: sys.path.append(lib_path)
作为对@jamesdlin 的回复,结合了几个答案:os.path.abspath(os.path.join(__file__,'..','lib')) 呢?
P
Peter Mortensen

只需做一些简单的事情即可从不同的文件夹导入 .py 文件。

假设您有一个目录,例如:

lib/abc.py

然后只需在 lib 文件夹中保留一个名为的空文件

__init__.py

然后使用

from lib.abc import <Your Module name>

__init__.py 文件保存在导入模块层次结构的每个文件夹中。


P
Peter Mortensen

如果您以这种方式构建项目:

src\
  __init__.py
  main.py
  dirFoo\
    __init__.py
    Foo.py
  dirBar\
    __init__.py
    Bar.py

然后从 Foo.py 你应该能够做到:

import dirFoo.Foo

或者:

from dirFoo.Foo import FooObject

根据 Tom 的评论,这确实要求可以通过 site_packages 或您的搜索路径访问 src 文件夹。此外,正如他所提到的,当您第一次在该包/目录中导入模块时,会隐式导入 __init__.py。通常 __init__.py 只是一个空文件。


还要提到 init.py 是在导入该包中的第一个模块时导入的。此外,您的示例仅在 src 位于 site_packages (或搜索路径)中时才有效
这是我一直在寻找的最简单的解决方案。如果要导入的文件肯定位于其中一个子目录中,则此解决方案是一个 gem。
我尝试过同样的事情并失败了。我不知道为什么。 ImportError:没有名为 customMath 的模块
为什么有人想从 Foo.py 本身中导入 Foo 。我猜应该来自 Bar.py。
你如何从 Foo.py 导入 Bar.py?
m
monkut

最简单的方法是使用 sys.path.append()。

但是,您可能也对 imp 模块感兴趣。它提供对内部导入功能的访问。

# mod_name is the filename without the .py/.pyc extention
py_mod = imp.load_source(mod_name,filename_path) # Loads .py file
py_mod = imp.load_compiled(mod_name,filename_path) # Loads .pyc file 

当您不知道模块的名称时,这可用于动态加载模块。

我过去曾使用它来创建应用程序的插件类型接口,用户可以在其中编写具有应用程序特定功能的脚本,然后将脚本放在特定目录中。

此外,这些功能可能很有用:

imp.find_module(name[, path])
imp.load_module(name, file, pathname, description)

请注意,在文档中 imp.load_source 和 imp.load_compiled 被列为过时。建议使用 imp.find_module 和 imp.load_module。
@amicitas,您能否为此提供任何参考(我需要它,并且我正在使用python 2.6。我知道2.7文档对此有何评论,但找不到有关2.6的任何参考)
@0xc0de 您可以在 python 2.7.3python 2.6.7 的 imp 模块的文档中找到该语句。看起来这些函数甚至不在 python 3.2 的文档中。
要在 python 3.3 中导入这样的源文件,请参阅 Import abitrary python source file. (Python 3.3+) - Stack Overflow。谢谢,这里的提示。
P
Peter Crabtree

这是相关的 PEP:

http://www.python.org/dev/peps/pep-0328/

特别是,假设 dirFoo 是从 dirBar 向上的目录...

在目录 Foo\Foo.py 中:

from ..dirBar import Bar

它对我有用,只需在每个文件夹中添加 init.py 并成功导入
我认为应该是from .dirBar import Bar
P
Peter Mortensen

无需修改脚本的最简单方法是设置 PYTHONPATH 环境变量。因为 sys.path 是从这些位置初始化的:

包含输入脚本的目录(或当前目录)。 PYTHONPATH(目录名称列表,与 shell 变量 PATH 具有相同的语法)。安装相关的默认值。

赶紧跑:

export PYTHONPATH=/absolute/path/to/your/module

您的 sys.path 将包含上述路径,如下所示:

print sys.path

['', '/absolute/path/to/your/module', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/pymodules/python2.7', '/usr/lib/python2.7/dist-packages/ubuntu-sso-client', '/usr/lib/python2.7/dist-packages/ubuntuone-client', '/usr/lib/python2.7/dist-packages/ubuntuone-control-panel', '/usr/lib/python2.7/dist-packages/ubuntuone-couch', '/usr/lib/python2.7/dist-packages/ubuntuone-installer', '/usr/lib/python2.7/dist-packages/ubuntuone-storage-protocol']

j
jhana

在我看来,最好的选择是将 __ init __.py 放在文件夹中并使用

from dirBar.Bar import *

不建议使用 sys.path.append() ,因为如果使用与现有 python 包相同的文件名,可能会出现问题。我没有对此进行测试,但这将是模棱两可的。


奇怪的是 from dirBar.Bar import * 有效,但 from dirBar.Bar import Bar 无效。你知道为什么 * 有效吗?如果我在 dirBar/ 中有多个文件并且只想获取其中的几个文件(使用您在此处发布的方法)怎么办?
@tester:使用 from dirBar import Bar
@tester 这是因为 from 表示来源,而 import 之后的所有内容都是从该来源获取的内容。 from dirBar.Bar import Bar 表示“从源,导入源本身”,这没有意义。 * 的意思是“从源头给我一切”
B
Brent Bradburn

Linux用户的快速而肮脏的方式

如果您只是在修补并且不关心部署问题,则可以使用符号链接(假设您的文件系统支持它)使模块或包在请求模块的文件夹中直接可见。

ln -s (path)/module_name.py

或者

ln -s (path)/package_name

注意:“模块”是扩展名为 .py 的任何文件,“包”是包含文件 __init__.py(可以是空文件)的任何文件夹。从使用的角度来看,模块和包是相同的 - 都按照 import 命令的请求公开其包含的“定义和语句”。

请参阅:http://docs.python.org/2/tutorial/modules.html


j
jgomo3
from .dirBar import Bar

代替:

from dirBar import Bar

以防万一可能安装了另一个 dirBar 并混淆了 foo.py 阅读器。


我无法在 Windows 上执行此操作。这是在Linux上吗?
它将通过导入 Foo 的脚本工作。即:main.py 导入 dirFoo.Foo。如果您尝试将 Foo.py 作为脚本运行,它将失败。请参阅stackoverflow.com/questions/72852/…
P
Peter Mortensen

对于这种将 Bar.py 导入 Foo.py 的情况,首先我会将这些文件夹转换为 Python 包,如下所示:

dirFoo\
    __init__.py
    Foo.py
    dirBar\
        __init__.py
        Bar.py

然后我会在 Foo.py 中这样做:

from .dirBar import Bar

如果我希望命名空间看起来像 Bar.whatever,或者

from . import dirBar

如果我想要命名空间 dirBar.Bar.whatever。如果 dirBar 包下有更多模块,则第二种情况很有用。


F
Frederick The Fool

添加一个 __init__.py 文件:

dirFoo\
    Foo.py
    dirBar\
        __init__.py
        Bar.py

然后将此代码添加到 Foo.py 的开头:

import sys
sys.path.append('dirBar')
import Bar

如果 dirBar 已经是 Python 包(通过 dirBar/__init__.py 的存在),则无需将 dirBar 附加到 sys.path,不是吗? Foo.py 中的语句 import Bar 应该足够了。
C
Community

相对 sys.path 示例:

# /lib/my_module.py
# /src/test.py


if __name__ == '__main__' and __package__ is None:
    sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../lib')))
import my_module

基于 this 答案。


P
Peter Mortensen

好吧,正如您所提到的,通常您希望访问与您的主脚本运行位置相关的包含模块的文件夹,因此您只需导入它们。

解决方案:

我在 D:/Books/MyBooks.py 中有脚本和一些模块(如 oldies.py)。我需要从子目录 D:/Books/includes 导入:

import sys,site
site.addsitedir(sys.path[0] + '\\includes')
print (sys.path)  # Just verify it is there
import oldies

oldies.py 中放置一个 print('done'),以便您验证一切正常。这种方式总是有效的,因为根据程序启动时初始化的 Python 定义 sys.path,此列表的第一项 path[0] 是包含用于调用 Python 解释器的脚本的目录。

如果脚本目录不可用(例如,如果交互调用解释器或从标准输入读取脚本),path[0] 是空字符串,它指示 Python 首先搜索当前目录中的模块。请注意,脚本目录是在作为 PYTHONPATH 的结果插入的条目之前插入的。


在我的第一个简单 Python 程序 break_time.py: https://github.com/ltfschoen/PythonTest 中,我不得不使用一个正斜杠而不是两个反斜杠(即 site.addsitedir(sys.path[0]+'/includes'))。我使用系统:MacOS v10.11.5、Python 2.7.12、IDLE IDE 2.7.12、Tk 8.5.9
N
Niklas R

另一种解决方案是安装 py-require 软件包,然后在 Foo.py 中使用以下内容

import require
Bar = require('./dirBar/Bar')

该 URL 实际上似乎是 pypi.org/project/require.py,并注意它需要通过 Pip 安装。
@FlashSheridan 不,这是一个不同的项目。我删除了 py-require,因为我确定没有人在使用它,但我没有想到这篇文章。如果还需要 require() 函数,可以看看我的 Node.py 项目:github.com/nodepy/nodepy
0
0x1996

您只需使用:from Desktop.filename import something

例子:

鉴于该文件在目录 Users/user/Desktop 中的名称为 test.py ,并且将导入所有内容。

编码:

from Desktop.test import *

但请确保在该目录中创建一个名为“__init__.py”的空文件


建议不要使用已加星标的导入。见:stackoverflow.com/questions/2386714/why-is-import-bad
我知道这就是为什么我首先写 import something 然后我说让它更容易 * 基本上它对 ram 不利,而且如果 2 个函数具有相同的名称,它会堆积你的代码
J
Justin Muller

这是一种使用相对路径从上一层导入文件的方法。

基本上,只需将工作目录向上移动一个级别(或任何相对位置),将其添加到您的路径中,然后将工作目录移回它开始的位置。

#to import from one level above:
cwd = os.getcwd()
os.chdir("..")
below_path =  os.getcwd()
sys.path.append(below_path)
os.chdir(cwd)

我不明白你这里的逻辑。太复杂了
F
Findon Fassbender

我对python没有经验,所以如果我的话有任何错误,请告诉我。如果您的文件层次结构是这样排列的:

project\
    module_1.py 
    module_2.py

module_1.py 定义了一个名为 func_1() 的函数 module_2.py

from module_1 import func_1

def func_2():
    func_1()

if __name__ == '__main__':
    func_2()

并且您在 cmd 中运行 python module_2.py,它会运行 func_1() 定义的内容。这通常是我们导入相同层次结构文件的方式。但是当你在 module_2.py 中写 from .module_1 import func_1 时,python 解释器会说 No module named '__main__.module_1'; '__main__' is not a package。所以要解决这个问题,我们只需保留我们刚刚所做的更改,并将两个模块都移动到一个包中,并让第三个模块作为调用者来运行 module_2.py

project\
    package_1\
        module_1.py
        module_2.py
    main.py

主要.py:

from package_1.module_2 import func_2

def func_3():
    func_2()

if __name__ == '__main__':
    func_3()

但是我们在module_2.py中的module_1之前添加一个.的原因是如果我们不这样做并运行main.py,python解释器会说No module named 'module_1',这有点棘手,module_1.py是对的module_2.py 旁边。现在我让 module_1.py 中的 func_1() 做一些事情:

def func_1():
    print(__name__)

__name__ 记录谁调用了 func_1。现在我们将 . 保留在 module_1 之前,运行 main.py,它将打印 package_1.module_1,而不是 module_1。它表示调用 func_1() 的人与 main.py 处于同一层次结构,. 暗示 module_1module_2.py 本身处于同一层次结构。因此,如果没有点,main.py 将识别与自身相同层次结构的 module_1,它可以识别 package_1,但不能识别它“下”的内容。

现在让我们让它变得有点复杂。您有一个 config.ini 并且一个模块定义了一个函数来在与“main.py”相同的层次结构中读取它。

project\
    package_1\
        module_1.py
        module_2.py
    config.py
    config.ini
    main.py

由于某些不可避免的原因,您必须使用 module_2.py 调用它,因此它必须从上层结构中导入。module_2.py

 import ..config
 pass

两个点表示从上层导入(三个点访问上层,等等)。现在我们运行 main.py,解释器会说:ValueError:attempted relative import beyond top-level package。这里的“顶级包”是 main.py。正因为 config.pymain.py 旁边,所以它们处于同一层次结构中,config.py 不是“在”main.py“之下”,或者它没有被 main.py“引导”,所以它在 main.py 之外.要解决此问题,最简单的方法是:

project\
    package_1\
        module_1.py
        module_2.py
    config.py
    config.ini
main.py

我认为这与排列项目文件层次结构的原则不谋而合,您应该将具有不同功能的模块排列在不同的文件夹中,并在外面留下一个顶级调用者,您可以随心所欲地导入。


c
californium

这也有效,并且比使用 sys 模块的任何东西都简单得多:

with open("C:/yourpath/foobar.py") as f:
    eval(f.read())

如果 OP 要对路径进行硬编码,他们无论如何都可以将该路径插入 PYTHONPATH 中。我认为重点是这样做的方式不会硬编码路径,因为它会在其他任何地方中断。
P
Peter Mortensen

称我过于谨慎,但我喜欢让我的更便携,因为假设文件总是在每台计算机上的同一个位置是不安全的。我个人让代码首先查找文件路径。我使用 Linux,所以我的看起来像这样:

import os, sys
from subprocess import Popen, PIPE
try:
    path = Popen("find / -name 'file' -type f", shell=True, stdout=PIPE).stdout.read().splitlines()[0]
    if not sys.path.__contains__(path):
        sys.path.append(path)
except IndexError:
    raise RuntimeError("You must have FILE to run this program!")

当然,除非您打算将这些打包在一起。但如果是这种情况,您实际上并不需要两个单独的文件。


给定相对路径,文件将始终位于每台计算机上的相同位置。