从相对路径导入模块

时间:2008-11-10 21:28:48

标签: python relative-path python-import

如何根据相对路径导入Python模块?

例如,如果dirFoo包含Foo.pydirBardirBar包含Bar.py,如何将Bar.py导入{{} 1}}?

这是一个直观的表示:

Foo.py

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

23 个答案:

答案 0 :(得分:325)

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

答案 1 :(得分:320)

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

答案 2 :(得分:252)

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

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

答案 3 :(得分:114)

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

import mymodule

答案 4 :(得分:97)

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

假设您有一个目录,如:

lib/abc.py

然后在lib文件夹中保留一个名为

的空文件
__init__.py

然后使用

from lib.abc import <Your Module name>

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

答案 5 :(得分:78)

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

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只是一个空文件。

答案 6 :(得分:44)

最简单的方法是使用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)

答案 7 :(得分:43)

这是相关的PEP:

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

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

在dirFoo \ Foo.py中:

from ..dirBar import Bar

答案 8 :(得分:22)

没有对脚本进行任何修改的最简单方法是设置PYTHONPATH环境变量。因为sys.path是从这些位置初始化的:

  1. 包含输入脚本(或当前的脚本)的目录 目录)。
  2. PYTHONPATH(目录名列表,相同 语法作为shell变量PATH)。
  3. 依赖于安装的默认值。
  4. 跑步:

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

答案 9 :(得分:12)

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

调用该文件
from dirBar.Bar import *

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

答案 10 :(得分:11)

Linux用户的快捷方式

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

ln -s (path)/module_name.py

ln -s (path)/package_name

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

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

答案 11 :(得分:9)

from .dirBar import Bar

而不是:

from dirBar import Bar

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

答案 12 :(得分:8)

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

答案 13 :(得分:6)

添加 __ init __。py 文件:

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

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

的开头
import sys
sys.path.append('dirBar')
import Bar

答案 14 :(得分: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

基于this回答。

答案 15 :(得分: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')中放置oldies.py,以便验证一切正常。这种方式始终有效,因为在程序启动时初始化的Python定义sys.path,此列表的第一项path[0]是包含用于调用Python解释器的脚本的目录。

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

的结果插入条目之前插入了脚本目录

答案 16 :(得分:3)

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

中使用以下内容
import require
Bar = require('./dirBar/Bar')

答案 17 :(得分:3)

只需使用:field1 field2 field3 ---------- ---------- ---------- value1 value2 value3

即可

示例:

  

鉴于该文件在目录中是名称from Desktop.filename import something   test.py,并将导入everthing。

代码:

Users/user/Desktop

但请确保在该目录中创建一个名为“from Desktop.test import * ”的空文件

答案 18 :(得分:2)

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

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

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

答案 19 :(得分:2)

从标准库中查看pkgutil模块。它可以帮助你做你想做的事。

答案 20 :(得分: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()

并且您在cmd中运行python module_2.py,它将运行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_1之前添加module_2.py的原因是,如果我们不这样做并运行main.py,python解释器会说{{1} },这有点棘手,No module named 'module_1'就在module_1.py旁边。现在我让module_2.py func_1()做一些事情:

module_1.py

调用func_1的def func_1(): print(__name__) 条记录。现在我们在__name__之前保持.,运行module_1,它将打印main.py,而不是package_1.module_1。它表示调用module_1的人与func_1()处于同一层次结构,main.py表示.module_1本身处于同一层次结构。因此,如果没有点,module_2.py会识别与自身相同的层次结构中的main.py,它可以识别module_1,但不能识别它下面的“{1}}。

现在让它变得有点复杂。你有一个package_1,一个模块定义了一个函数来读取与'main.py'相同的层次结构。

config.ini

出于某些不可避免的原因,您必须使用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不在“{1}}之下”,或者config.py没有“引导” }},所以超出main.py。要解决这个问题,最简单的方法是:

main.py

我认为这与安排项目文件层次结构的原则是一致的,你应该在不同的文件夹中安排具有不同功能的模块,只需在外面留下一个顶级调用者,你就可以导入你想要的。

答案 21 :(得分:-2)

这也有效,并且比使用sys模块的任何事情都要简单:

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

答案 22 :(得分:-14)

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

除非您打算将这些包装在一起,否则当然是这样。但如果是这种情况,你无论如何都不需要两个单独的文件。