从相对路径导入模块

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

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

这是一个可视化表示:

dirFoo\Foo.pydirBar\Bar.py

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

931023 次浏览

请确保迪尔巴有__init__.py文件——这会将一个目录放入Python包中。

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

src\__init__.pymain.pydirFoo\__init__.pyFoo.pydirBar\__init__.pyBar.py

然后从Foo.py你应该能够做到:

import dirFoo.Foo

或:

from dirFoo.Foo import FooObject

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

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

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

这是相关的PEP:

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

特别是,假设disfoo是一个目录,从DirBar…

在difoo\Foo.py:

from ..dirBar import Bar

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

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

# mod_name is the filename without the .py/.pyc extentionpy_mod = imp.load_source(mod_name,filename_path) # Loads .py filepy_mod = imp.load_compiled(mod_name,filename_path) # Loads .pyc file

当您不知道模块的名称时,这可用于动态加载模块。

我过去曾使用它来创建应用程序的插件类型接口,用户可以在其中编写具有应用程序特定功能的脚本,并将其脚本放入特定目录。

此外,这些函数可能有用:

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

添加__init__. py文件:

dirFoo\Foo.pydirBar\__init__.pyBar.py

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

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

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

from dirBar.Bar import *

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

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

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

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

lib/abc.py

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

__init__.py

然后使用

from lib.abc import <Your Module name>

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

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

from .dirBar import Bar

而不是:

from dirBar import Bar

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

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

import os, sysfrom subprocess import Popen, PIPEtry: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!")

当然,除非您打算将它们打包在一起。但如果是这样的话,无论如何您都不需要两个单独的文件。

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

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

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

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

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

ln -s (path)/module_name.py

ln -s (path)/package_name

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

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

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

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

基于这个的答案。

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

dirFoo\__init__.pyFoo.pydirBar\__init__.pyBar.py

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

from .dirBar import Bar

如果我希望名称空间看起来像Bar.无论,或者

from . import dirBar

如果我想要命名空间的dirbar. bar。无论。如果您在dirbar包下有更多模块,第二种情况很有用。

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

解决方案:

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

import sys,sitesite.addsitedir(sys.path[0] + '\\includes')print (sys.path)  # Just verify it is thereimport oldies

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

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

另一种解决方案是安装自定义需求包,然后在Foo.py中使用以下内容

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

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

project\module_1.pymodule_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.pymodule_2.pymain.py

main.py

from package_1.module_2 import func_2
def func_3():func_2()
if __name__ == '__main__':func_3()

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

def func_1():print(__name__)

__name__记录了谁调用了func_1。现在我们把.放在module_1之前,运行main.py,它会打印package_1.module_1,而不是module_1。它表明调用func_1()的人和main.py在同一个层次上,.暗示module_1.0本身在同一个层次上。所以如果没有点,main.py会识别module_1和自己在同一个层次上,它可以识别.3,但不能识别它“下面”的东西。

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

project\package_1\module_1.pymodule_2.pyconfig.pyconfig.inimain.py

由于某些不可避免的原因,您必须使用module_2.py调用它,因此它必须从上层导入。module_2.py

 import ..configpass

两点表示从上层导入(三个点访问上层而不是上层,依此类推)。现在我们运行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.pymodule_2.pyconfig.pyconfig.inimain.py

我认为这与排列项目文件层次结构的原则不谋而合,您应该将不同功能的模块排列在不同的文件夹中,只需在外面留下一个顶部调用者,您就可以导入任何您想要的内容。

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

示例:

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

代码:

from Desktop.test import *

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

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

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