从相对路径导入模块


759

给定相对路径,如何导入Python模块?

例如,如果dirFoo包含Foo.pydirBar,和dirBar包含Bar.py,我怎么导入Bar.pyFoo.py

这是一个视觉表示:

dirFoo\
    Foo.py
    dirBar\
        Bar.py

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


2
看起来像stackoverflow.com/questions/72852/…,也许吗?
Joril

3
检查我的答案,到目前为止,它是最完整的,其他情况在特殊情况下不起作用,例如,当您从另一个目录或另一个python脚本调用该脚本时。见stackoverflow.com/questions/279237/...
索林

我有一个类似的问题,我发现了这一点并且有效!apt-get install python-profiler
ldcl289 2011年

5
万一有人想静态地做它并到达这里(就像我一样:),您还可以设置PYTHONPATH环境变量
okaram 2015年

对于每种情况,最好遵循Lib / site.py中的说明
Avenida Gez'Bug

Answers:


333

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


5
我可以对此进行解释吗?我遇到了类似的问题,我希望强制使用python模块DIR而不是运行搜索
carl crott

运行Win 7 Pro 64x和Python 2.7时,出现一些错误。1)我必须将检查添加到导入列表。2)元组中的第一个值[0]是一个空字符串。第二个[1]显示文件名。我想第一个应该是道路...有什么想法吗?
亚当·刘易斯

2
如果你要一个子文件夹,使用这样的:os.path.realpath(os.path.abspath(os.path.split(inspect.getfile( inspect.currentframe() ))[0]) + "/subfolder")待办事项NOT前添加子文件夹abspath,因为这会导致严重的错误。
Maximilian Hils 2012年

4
@ scr4ve您应该改用os.path.join(),欢迎您将case(cmd_subfolder)直接添加到我的答案中。谢谢!
索林2012年

7
因为我realpath已经生成了绝对路径,所以我不需要abspath。也os.path.dirname可以代替拆分使用,从而使索引[0]过时。该行如下:os.path.realpath(os.path.dirname(inspect.getfile(inspect.currentframe())))
ted

328

确保dirBar具有__init__.py文件-这会将目录创建到Python包中。


204
请注意,此文件可以完全为空。
哈雷·霍尔科姆

47
如果dirBar的父目录不在sys.path那么存在 __init__.pydirBar目录并没有多大帮助。
jfs

7
-1,__init.py__仅当目录已经在sys.path中并且在我而言不是的情况下,添加才起作用。通过“ sorin”(已接受)解决方案始终有效。
Czarek Tomczak

12
“目录已经在sys.path中时”。虽然完全正确,但我们怎么能猜出该目录不在sys.path问题中呢?也许有一些我们没有看到或不知道的遗漏?
S.Lott

4
这绝不是问题的答案:初始化文件是否存在,这不会使python查看子目录。它是如何获得数百票支持的?
gented

261

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

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

6
它看起来你的回答不工作相对路径,看到stackoverflow.com/questions/279237/...
波格丹

24
确实适用于相对路径-您只需要了解相对路径将取决于您从哪个目录运行,这使得它对于快速黑客攻击以外的其他任何情况都不适用。
nobar 2012年

12
您可以执行类似操作sys.path.append(os.path.dirname(__file__) + "/relative/path/to/module")
Falko 2015年

sys.path.append(__name__ if __name__ != '__main__' else __loader__.fullname)
vim

1
考虑使用,sys.path.insert(0, <path to dirFoo>)因为它将在其他位置存储的同名模块之前加载此模块。
安东·塔拉森科

117
import os
import sys
lib_path = os.path.abspath(os.path.join(__file__, '..', '..', '..', 'lib'))
sys.path.append(lib_path)

import mymodule

2
我之所以这样,是因为您可以灵活地转到目录。
Charles L.

21
您应该使用os.path.join()而不是通过'/'进行连接,否则会在(层)窗口中中断。
0xc0de 2012年

18
这是不可靠的。它取决于当前工作目录是什么,而不取决于脚本所在的目录。
jamesdlin 2014年

可能只想在路径尚不在路径中时添加。如果lib_path不在sys.path中,则lib_path = os.path.abspath('../ functions'):sys.path.append(lib_path)
Brent

回复@jamesdlin并结合了一些答案:那os.path.abspath(os.path.join(__file__,'..','lib'))呢?
马修·戴维斯

107

只需执行简单的操作即可从其他文件夹导入.py文件。

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

lib/abc.py

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

__init__.py

然后用

from lib.abc import <Your Module name>

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


79

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

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的评论,这确实要求src可以通过site_packages或您的搜索路径访问该文件夹。而且,正如他所提到的,__init__.py当您首次在该包/目录中导入模块时,它是隐式导入的。通常__init__.py只是一个空文件。


还要提到的是,在导入该包中的第一个模块时,会导入init .py。另外,您的示例仅在src在site_packages(或在搜索路径中)时才有效
Tom Leys,

3
这是我一直在寻找的最简单的解决方案。如果确定要导入的文件位于子目录之一中,则此解决方案是gem。
Deepak GM

我尝试过同样的事情,但失败了。我不知道为什么。ImportError:没有名为customMath的模块
Ming Li

9
为什么有人要从Foo.py本身导入Foo。我猜应该来自Bar.py。
bhaskarc 2015年

from dirFoo import FooFoo.bla()。如果使用import dirFoo.Foo一个应该使用dirFoo.Foo.bla()-即使没有骆驼盒也很丑陋。
Cees Timmerman

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)

请注意,在文档imp.load_source和imp.load_compiled中已过时列出。建议改用imp.find_module和imp.load_module。
2011年

@amicitas,能否请您提供任何参考(我需要它,并且我正在使用python 2.6。我知道2.7文档对此有何评论,但找不到有关2.6的任何参考)
0xc0de 2012年

@ 0xc0de您可以在文档中为python 2.7.3python 2.6.7的imp模块找到该语句。看起来这些功能甚至不在python 3.2的文档中。
amicitas 2012年

1
有关在python 3.3中导入此类源文件的信息,请参见导入任意python源文件。(Python 3.3+)-堆栈溢出。谢谢大家在这里的提示。
nealmcb 2014年


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']

13

我认为最好的选择是将__ init __.py放在文件夹中,然后使用

from dirBar.Bar import *

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


这很奇怪from dirBar.Bar import *,但是没有效果from dirBar.Bar import Bar。你知道为什么*有效吗?如果我在dirBar /中有多个文件并且只想抓取其中几个文件(使用类似于您在此处发布的方法)怎么办?
测试人员

2
@tester:使用from dirBar import Bar
nobar 2012年

@tester是因为from指示了源,之后的所有内容import都是从该源获取的内容。from dirBar.Bar import Bar表示“从源导入本身”,这没有任何意义。在*虽然手段,“给我的一切,从源头”
FuriousFolder

11

Linux用户的快捷方式

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

ln -s (path)/module_name.py

要么

ln -s (path)/package_name

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

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


10
from .dirBar import Bar

代替:

from dirBar import Bar

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


2
我无法在Windows上执行此操作。这在Linux上吗?
加百利

1
它可以从导入Foo的脚本中工作。即:main.py导入dirFoo.Foo。如果您尝试将Foo.py作为脚本运行,它将失败。见stackoverflow.com/questions/72852/...
jgomo3

9

对于这种情况,要将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包下有更多模块,则第二种情况很有用。


7

添加__init__.py文件:

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

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

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

5
如果dirBar已经是一个Python包(由存在dirBar/__init__.py),也没有必要追加dirBarsys.path,不是吗?import Bar来自的声明Foo.py就足够了。
圣诞老人

5

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

基于答案。


5

好了,正如您提到的,通常您希望访问一个包含您的模块的文件夹,该模块相对于您运行主脚本的位置,因此您只需导入它们即可。

解:

我有脚本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')放在中oldies.py,以便您确认一切正常。这种方法始终有效,因为sys.path根据程序启动时初始化的Python定义,此列表的第一项path[0]是包含用于调用Python解释器的脚本的目录。

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


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

4

只需使用即可: from Desktop.filename import something

例:

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

编码:

from Desktop.test import *

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


1
建议不要给加星标的商品加注。参见:stackoverflow.com/questions/2386714/why-is-import-bad
axolotl

我知道那就是为什么我首先写import something然后为什么我说要使其变得更简单,* 基本上它对ram
不利

3

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

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

该URL实际上似乎是pypi.org/project/require.py,请注意,它需要通过Pip安装。
Flash Sheridan

1
@FlashSheridan不,那是一个不同的项目。我删除了py-require,因为我确定没有人在使用它,尽管我没有想到这篇文章。如果仍然需要require()函数,可以看一下我的Node.py项目:github.com/nodepy/nodepy
Niklas R

2

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

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

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

1
我不明白你的逻辑。这太复杂了
transilvlad

0

我对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()

并且您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

main.py

from package_1.module_2 import func_2

def func_3():
    func_2()

if __name__ == '__main__':
    func_3()

而增加了的原因.之前module_1module_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,因此必须从上层结构导入。module_2.py

 import ..config
 pass

两点表示从上级结构导入(三个点访问上层而不是上层,依此类推)。现在运行main.py,解释器将说:ValueError:attempted relative import beyond top-level package。这里的“顶级程序包”是main.py。仅仅因为config.py在旁边main.py,它们处于相同的层次结构,config.py不在“下面” main.py,或者不在“前面” main.py,所以它超出了main.py。要解决此问题,最简单的方法是:

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

我认为这与安排项目文件层次结构的原理是一致的,您应该将具有不同功能的模块安排在不同的文件夹中,而仅在外部放置一个顶级调用方,然后可以随心所欲地导入。


-5

这也可行,并且比使用该sys模块的任何事情都要简单得多:

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

1
如果OP要对路径进行硬编码,则无论如何,他们都可以将该路径插入PYTHONPATH中。我认为关键是这样做的方式不会硬编码路径,因为它会破坏其他任何位置。
马修

-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!")

当然,除非您计划将它们打包在一起。但是,在这种情况下,您实际上并不需要两个单独的文件。


11
超级低效!
0xc0de 2012年

1
给定相对路径,文件将始终在每台计算机上的同一位置。
Sagar Hatekar
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.