从相对路径导入模块

问题:从相对路径导入模块

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

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

这是一个视觉表示:

dirFoo\
    Foo.py
    dirBar\
        Bar.py

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

How do I import a Python module given its relative path?

For example, if dirFoo contains Foo.py and dirBar, and dirBar contains Bar.py, how do I import Bar.py into Foo.py?

Here’s a visual representation:

dirFoo\
    Foo.py
    dirBar\
        Bar.py

Foo wishes to include Bar, but restructuring the folder hierarchy is not an option.


回答 0

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

Assuming that both your directories are real Python packages (do have the __init__.py file inside them), here is a safe solution for inclusion of modules relatively to the location of the script.

I assume that you want to do this, because you need to include a set of modules with your script. I use this in production in several products and works in many special scenarios like: scripts called from another directory or executed with python execute instead of opening a new interpreter.

 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.

As a bonus, this approach does let you force Python to use your module instead of the ones installed on the system.

Warning! I don’t really know what is happening when current module is inside an egg file. It probably fails too.


回答 1

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

Be sure that dirBar has the __init__.py file — this makes a directory into a Python package.


回答 2

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

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

You could also add the subdirectory to your Python path so that it imports as a normal script.

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

回答 3

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

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

import mymodule

回答 4

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

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

lib/abc.py

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

__init__.py

然后用

from lib.abc import <Your Module name>

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

Just do simple things to import the .py file from a different folder.

Let’s say you have a directory like:

lib/abc.py

Then just keep an empty file in lib folder as named

__init__.py

And then use

from lib.abc import <Your Module name>

Keep the __init__.py file in every folder of the hierarchy of the import module.


回答 5

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

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

If you structure your project this way:

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

Then from Foo.py you should be able to do:

import dirFoo.Foo

Or:

from dirFoo.Foo import FooObject

Per Tom’s comment, this does require that the src folder is accessible either via site_packages or your search path. Also, as he mentions, __init__.py is implicitly imported when you first import a module in that package/directory. Typically __init__.py is simply an empty file.


回答 6

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

The easiest method is to use sys.path.append().

However, you may be also interested in the imp module. It provides access to internal import functions.

# 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 

This can be used to load modules dynamically when you don’t know a module’s name.

I’ve used this in the past to create a plugin type interface to an application, where the user would write a script with application specific functions, and just drop thier script in a specific directory.

Also, these functions may be useful:

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

回答 7

这是相关的PEP:

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

特别是,假定dirFoo是dirBar的目录。

在dirFoo \ Foo.py中:

from ..dirBar import Bar

This is the relevant PEP:

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

In particular, presuming dirFoo is a directory up from dirBar…

In dirFoo\Foo.py:

from ..dirBar import Bar

回答 8

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

The easiest way without any modification to your script is to set PYTHONPATH environment variable. Because sys.path is initialized from these locations:

  1. The directory containing the input script (or the current directory).
  2. PYTHONPATH (a list of directory names, with the same syntax as the shell variable PATH).
  3. The installation-dependent default.

Just run:

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

You sys.path will contains above path, as show below:

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

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

from dirBar.Bar import *

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

In my opinion the best choice is to put __ init __.py in the folder and call the file with

from dirBar.Bar import *

It is not recommended to use sys.path.append() because something might gone wrong if you use the same file name as the existing python package. I haven’t test that but that will be ambiguous.


回答 10

Linux用户的快捷方式

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

ln -s (path)/module_name.py

要么

ln -s (path)/package_name

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

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

The quick-and-dirty way for Linux users

If you are just tinkering around and don’t care about deployment issues, you can use a symbolic link (assuming your filesystem supports it) to make the module or package directly visible in the folder of the requesting module.

ln -s (path)/module_name.py

or

ln -s (path)/package_name

Note: A “module” is any file with a .py extension and a “package” is any folder that contains the file __init__.py (which can be an empty file). From a usage standpoint, modules and packages are identical — both expose their contained “definitions and statements” as requested via the import command.

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


回答 11

from .dirBar import Bar

代替:

from dirBar import Bar

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

from .dirBar import Bar

instead of:

from dirBar import Bar

just in case there could be another dirBar installed and confuse a foo.py reader.


回答 12

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

For this case to import Bar.py into Foo.py, first I’d turn these folders into Python packages like so:

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

Then I would do it like this in Foo.py:

from .dirBar import Bar

If I wanted the namespacing to look like Bar.whatever, or

from . import dirBar

If I wanted the namespacing dirBar.Bar.whatever. This second case is useful if you have more modules under the dirBar package.


回答 13

添加__init__.py文件:

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

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

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

Add an __init__.py file:

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

Then add this code to the start of Foo.py:

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

回答 14

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

基于答案。

Relative sys.path example:

# /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

Based on this answer.


回答 15

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

解:

我有脚本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

Well, as you mention, usually you want to have access to a folder with your modules relative to where your main script is run, so you just import them.

Solution:

I have the script in D:/Books/MyBooks.py and some modules (like oldies.py). I need to import from subdirectory D:/Books/includes:

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

Place a print('done') in oldies.py, so you verify everything is going OK. This way always works because by the Python definition sys.path as initialized upon program startup, the first item of this list, path[0], is the directory containing the script that was used to invoke the Python interpreter.

If the script directory is not available (e.g. if the interpreter is invoked interactively or if the script is read from standard input), path[0] is the empty string, which directs Python to search modules in the current directory first. Notice that the script directory is inserted before the entries inserted as a result of PYTHONPATH.


回答 16

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

例:

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

编码:

from Desktop.test import *

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

Simply you can use: from Desktop.filename import something

Example:

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

the code:

from Desktop.test import *

But make sure you make an empty file called “__init__.py” in that directory


回答 17

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

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

Another solution would be to install the py-require package and then use the following in Foo.py

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

回答 18

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

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

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

Here’s a way to import a file from one level above, using the relative path.

Basically, just move the working directory up a level (or any relative location), add that to your path, then move the working directory back where it started.

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

回答 19

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

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

I’m not experienced about python, so if there is any wrong in my words, just tell me. If your file hierarchy arranged like this:

project\
    module_1.py 
    module_2.py

module_1.py defines a function called func_1(), module_2.py:

from module_1 import func_1

def func_2():
    func_1()

if __name__ == '__main__':
    func_2()

and you run python module_2.py in cmd, it will do run what func_1() defines. That’s usually how we import same hierarchy files. But when you write from .module_1 import func_1 in module_2.py, python interpreter will say No module named '__main__.module_1'; '__main__' is not a package. So to fix this, we just keep the change we just make, and move both of the module to a package, and make a third module as a caller to run 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()

But the reason we add a . before module_1 in module_2.py is that if we don’t do that and run main.py, python interpreter will say No module named 'module_1', that’s a little tricky, module_1.py is right beside module_2.py. Now I let func_1() in module_1.py do something:

def func_1():
    print(__name__)

that __name__ records who calls func_1. Now we keep the . before module_1 , run main.py, it will print package_1.module_1, not module_1. It indicates that the one who calls func_1() is at the same hierarchy as main.py, the . imply that module_1 is at the same hierarchy as module_2.py itself. So if there isn’t a dot, main.py will recognize module_1 at the same hierarchy as itself, it can recognize package_1, but not what “under” it.

Now let’s make it a bit complicated. You have a config.ini and a module defines a function to read it at the same hierarchy as ‘main.py’.

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

And for some unavoidable reason, you have to call it with module_2.py, so it has to import from upper hierarchy.module_2.py:

 import ..config
 pass

Two dots means import from upper hierarchy (three dots access upper than upper,and so on). Now we run main.py, the interpreter will say:ValueError:attempted relative import beyond top-level package. The “top-level package” at here is main.py. Just because config.py is beside main.py, they are at same hierarchy, config.py isn’t “under” main.py, or it isn’t “leaded” by main.py, so it is beyond main.py. To fix this, the simplest way is:

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

I think that is coincide with the principle of arrange project file hierarchy, you should arrange modules with different function in different folders, and just leave a top caller in the outside, and you can import how ever you want.


回答 20

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

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

This also works, and is much simpler than anything with the sys module:

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

回答 21

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

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

Call me overly cautious, but I like to make mine more portable because it’s unsafe to assume that files will always be in the same place on every computer. Personally I have the code look up the file path first. I use Linux so mine would look like this:

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

That is of course unless you plan to package these together. But if that’s the case you don’t really need two separate files anyway.