787

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

例如,如果dirFoocontains Foo.pyanddirBardirBarcontains Bar.py,我该如何Bar.py导入Foo.py

这是一个视觉表示:

dirFoo\
    Foo.py
    dirBar\
        Bar.py

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

4

22 回答 22

348

假设您的两个目录都是真正的 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文件中时会发生什么。它可能也失败了。

于 2011-05-23T14:00:43.530 回答
331

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

于 2008-11-10T21:33:33.887 回答
264

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

import sys
sys.path.insert(0, <path to dirFoo>)
import Bar
于 2008-11-10T22:04:27.223 回答
117
import os
import sys
lib_path = os.path.abspath(os.path.join(__file__, '..', '..', '..', 'lib'))
sys.path.append(lib_path)

import mymodule
于 2010-11-26T10:21:28.800 回答
109

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

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

lib/abc.py

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

__init__.py

然后使用

from lib.abc import <Your Module name>

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

于 2010-12-09T10:40:09.677 回答
77

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

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

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

于 2008-11-10T21:46:04.953 回答
45

最简单的方法是使用 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)
于 2008-11-12T01:56:51.203 回答
43

这是相关的 PEP:

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

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

在目录 Foo\Foo.py 中:

from ..dirBar import Bar
于 2008-11-10T22:22:47.093 回答
23

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

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

赶紧跑:

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']
于 2013-02-10T23:21:51.977 回答
13

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

from dirBar.Bar import *

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

于 2010-09-15T05:02:52.807 回答
12

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

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

ln -s (path)/module_name.py

或者

ln -s (path)/package_name

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

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

于 2012-12-20T01:07:43.913 回答
11
from .dirBar import Bar

代替:

from dirBar import Bar

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

于 2011-08-31T20:02:15.783 回答
11

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

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

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

from .dirBar import Bar

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

from . import dirBar

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

于 2014-10-29T19:49:53.793 回答
6

添加一个__init__.py文件:

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

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

import sys
sys.path.append('dirBar')
import Bar
于 2010-02-28T20:34:59.760 回答
4

相对 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

基于这个答案。

于 2014-07-29T07:27:39.437 回答
4

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

解决方案:

我有脚本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

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

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

于 2015-04-01T22:29:39.357 回答
3

另一种解决方案是安装py-require包,然后在Foo.py

import require
Bar = require('./dirBar/Bar')
于 2016-08-06T21:26:58.767 回答
3

您可以简单地使用:from Desktop.filename import something

例子:

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

编码:

from Desktop.test import *

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

于 2018-01-26T19:30:49.757 回答
1

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

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

#to import from one level above:
cwd = os.getcwd()
os.chdir("..")
below_path =  os.getcwd()
sys.path.append(below_path)
os.chdir(cwd)
于 2012-01-27T17:43:59.530 回答
0

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

project\
    module_1.py 
    module_2.py

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

from module_1 import func_1

def func_2():
    func_1()

if __name__ == '__main__':
    func_2()

python module_2.py在 cmd 中运行,它会运行func_1()定义的内容。这通常是我们导入相同层次结构文件的方式。但是当你写from .module_1 import func_1进去的时候module_2.py,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()

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

def func_1():
    print(__name__)

记录__name__谁调用了 func_1。现在我们保留.之前的module_1,运行main.py,它会打印package_1.module_1,不会module_1。它表示呼叫者与func_1()处于同一层次main.py.暗示module_1与自身处于同一层次module_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,因此它必须从上层结构中导入。模块_2.py

 import ..config
 pass

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

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

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

于 2017-11-09T14:38:21.210 回答
-4

这也有效,并且比模块中的任何内容都简单得多sys

with open("C:/yourpath/foobar.py") as f:
    eval(f.read())
于 2018-07-25T20:23:39.240 回答
-15

称我过于谨慎,但我喜欢让我的更便携,因为假设文件总是在每台计算机上的同一个位置是不安全的。我个人让代码首先查找文件路径。我使用 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!")

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

于 2011-09-22T19:33:39.083 回答