关于python:从相对路径导入模块

Import a module from a relative path

如何导入给定相对路径的python模块?

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

这是一个直观的表示:

1
2
3
4
dirFoo\
    Foo.py
    dirBar\
        Bar.py

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


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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 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文件中时会发生什么。它可能也失败了。


确保dirbar有__init__.py文件——这将使目录成为一个python包。


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

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


1
2
3
4
5
6
import os
import sys
lib_path = os.path.abspath(os.path.join(__file__, '..', '..', '..', 'lib'))
sys.path.append(lib_path)

import mymodule


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

假设您有如下目录:

1
lib/abc.py

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

1
__init__.py

然后使用

1
from lib.abc import <Your Module name>

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


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

1
2
3
4
5
6
7
8
9
src\
  __init__.py
  main.py
  dirFoo\
    __init__.py
    Foo.py
  dirBar\
    __init__.py
    Bar.py

那么从foo.py开始,你应该能够做到:

1
import dirFoo.Foo

或:

1
from dirFoo.Foo import FooObject

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


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

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

1
2
3
# 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

当您不知道模块的名称时,可以使用它动态加载模块。

我在过去用它创建了一个应用程序的插件类型接口,在那里用户将编写一个具有应用程序特定功能的脚本,并将其放到一个特定的目录中。

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

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


这是相关的政治公众人物:

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

特别是,假设dirfoo是从dirbar向上的目录…

在dirfoofoo.py中:

1
from ..dirBar import Bar


不修改脚本的最简单方法是设置pythonpath环境变量。因为sys.path是从以下位置初始化的:

  • 包含输入脚本(或当前目录)。
  • pythonpath(目录名列表,具有相同的语法作为外壳变量路径)。
  • 依赖于安装的默认值。
  • 只是运行:

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

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

    1
    2
    3
    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']


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

    1
    from dirBar.Bar import *

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


    Linux用户快速而肮脏的方法

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

    1
    ln -s (path)/module_name.py

    1
    ln -s (path)/package_name

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

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


    1
    from .dirBar import Bar

    而不是:

    1
    from dirBar import Bar

    以防安装了另一个dirbar并混淆了foo.py阅读器。


    对于这个案例,要将bar.py导入foo.py,首先我将把这些文件夹转换成python包,如下所示:

    1
    2
    3
    4
    5
    6
    dirFoo\
        __init__.py
        Foo.py
        dirBar\
            __init__.py
            Bar.py

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

    1
    from .dirBar import Bar

    如果我想让名称间距看起来像条形图的话。

    1
    from . import dirBar

    如果我想要名字间距dirbar.bar.whatever。如果dirbar包下有更多的模块,则第二种情况非常有用。


    添加一个uuu init_uuu.py文件:

    1
    2
    3
    4
    5
    dirFoo\
        Foo.py
        dirBar\
            __init__.py
            Bar.py

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

    1
    2
    3
    import sys
    sys.path.append('dirBar')
    import Bar


    相对sys.path示例:

    1
    2
    3
    4
    5
    6
    7
    # /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

    基于这个答案。


    嗯,正如您提到的,通常您希望能够访问一个文件夹,其中包含与主脚本运行位置相关的模块,所以您只需导入它们。

    解决方案:

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

    1
    2
    3
    4
    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而插入的条目之前插入的。


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

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


    只需使用:from Desktop.filename import something

    例子:

    given that the file is name test.py in directory
    Users/user/Desktop , and will import everthing.

    代码:

    1
    from Desktop.test import *

    但请确保在该目录中生成一个名为"EDOCX1"(43)的空文件


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


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

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

    1
    2
    3
    4
    5
    6
    #to import from one level above:
    cwd = os.getcwd()
    os.chdir("..")
    below_path =  os.getcwd()
    sys.path.append(below_path)
    os.chdir(cwd)


    我对python没有经验,所以如果我的话有任何错误,就告诉我。如果文件层次结构按如下方式排列:

    1
    2
    3
    project\
        module_1.py
        module_2.py

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

    1
    2
    3
    4
    5
    6
    7
    from module_1 import func_1

    def func_2():
        func_1()

    if __name__ == '__main__':
        func_2()

    在命令中运行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

    1
    2
    3
    4
    5
    project\
        package_1\
            module_1.py
            module_2.py
        main.py

    MY.PY:

    1
    2
    3
    4
    5
    6
    7
    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旁边。现在我让func_1()module_1.py中做点什么:

    1
    2
    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"相同的层次结构来读取它。

    1
    2
    3
    4
    5
    6
    7
    project\
        package_1\
            module_1.py
            module_2.py
        config.py
        config.ini
        main.py

    由于一些不可避免的原因,您必须使用module_2.py来调用它,因此它必须从upper hierarchy.module.py导入:

    1
    2
     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之外。要解决这个问题,最简单的方法是:

    1
    2
    3
    4
    5
    6
    7
    project\
        package_1\
            module_1.py
            module_2.py
        config.py
        config.ini
    main.py

    我认为这与排列项目文件层次结构的原则是一致的,您应该在不同的文件夹中排列具有不同功能的模块,只需在外部留下一个顶级调用方,您就可以随心所欲地导入。


    这同样有效,而且比使用sys模块更简单:

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


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

    1
    2
    3
    4
    5
    6
    7
    8
    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!")

    当然,除非你打算把这些包装在一起。但如果是这样的话,你也不需要两个单独的文件。