【问题标题】:Import a module from a relative path从相对路径导入模块
【发布时间】:2010-09-21 17:01:57
【问题描述】:

如何在给定相对路径的情况下导入 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 install python-profiler
  • 万一有人想静态地做它并到达这里(就像我做的那样:)你也可以设置 PYTHONPATH 环境变量
  • 最好是按照 Lib/site.py 中的说明处理每种情况

标签: python relative-path python-import


【解决方案1】:

假设您的两个目录都是真正的 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") 不要abspath 之前添加子文件夹,因为这会导致严重的错误。
  • @scr4ve 您应该使用 os.path.join() 代替,欢迎您将案例 (cmd_subfolder) 直接添加到我的答案中。谢谢!
  • 对我来说realpath 已经生成了绝对路径,因此我不需要abspath。还可以使用 os.path.dirname 代替拆分,从而使索引 [0] 过时。然后该行将是:os.path.realpath(os.path.dirname(inspect.getfile(inspect.currentframe())))
【解决方案2】:

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

【讨论】:

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

您还可以将子目录添加到您的 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, &lt;path to dirFoo&gt;),因为它将在存储在其他地方的同名模块之前加载此模块。
【解决方案4】:
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')) 呢?
【解决方案5】:

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

假设你有一个类似的目录:

lib/abc.py

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

__init__.py

然后使用

from lib.abc import <Your Module name>

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

【讨论】:

    【解决方案6】:

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

    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??
    【解决方案7】:

    最简单的方法是使用 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。谢谢,这里的提示。
    【解决方案8】:

    这是相关的 PEP:

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

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

    在 dirFoo\Foo.py:

    from ..dirBar import Bar
    

    【讨论】:

    • 它对我有用,只是在每个文件夹上添加了 init.py 并成功导入
    • 我觉得应该是from .dirBar import Bar
    【解决方案9】:

    不修改脚本的最简单方法是设置 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']
    

    【讨论】:

      【解决方案10】:

      在我看来最好的选择是把 __ 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 的意思是“从源,导入源本身”,这没有意义。 * 的意思是“从源头给我一切”
      【解决方案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

      【讨论】:

        【解决方案12】:
        from .dirBar import Bar
        

        代替:

        from dirBar import Bar
        

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

        【讨论】:

        • 我无法在 Windows 上执行此操作。这是在 Linux 上吗?
        • 它将通过导入 Foo 的脚本工作。即:main.py 导入 dirFoo.Foo。如果您尝试将 Foo.py 作为脚本运行,它将失败。见stackoverflow.com/questions/72852/…
        【解决方案13】:

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

        【讨论】:

          【解决方案14】:

          添加一个 __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的存在),就不需要在sys.path后面加上dirBar,不是吗?来自Foo.py 的声明import Bar 就足够了。
          【解决方案15】:

          相对 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 的回答。

          【讨论】:

            【解决方案16】:

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

            解决方案:

            我在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
            【解决方案17】:

            另一种解决方案是安装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
            【解决方案18】:

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

            例子:

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

            代码:

            from Desktop.test import *
            

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

            【讨论】:

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

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

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

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

            【讨论】:

            • 我不明白你这里的逻辑。太复杂了
            【解决方案20】:

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

            ma​​in.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',这有点棘手,@987654338 @就在module_2.py旁边。现在我让func_1() in 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_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
            

            我觉得这和项目文件层次排列的原则是不谋而合的,你应该把不同功能的模块放在不同的文件夹里,在外面留一个top caller,你想怎么导入就怎么导入。

            【讨论】:

              【解决方案21】:

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

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

              【讨论】:

              • 如果 OP 打算硬编码一个路径,他们无论如何都可以将该路径插入到 PYTHONPATH 中。我认为这样做的重点是不要硬编码路径,因为它会在其他任何地方中断。
              【解决方案22】:

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

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

              【讨论】:

              • 在给定相对路径的情况下,文件将始终在每台计算机上的相同位置。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-12-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多