关于python:从父文件夹导入模块

Importing modules from parent folder

我在运行python 2.5。

这是我的文件夹树:

1
2
3
4
5
ptdraft/
  nib.py
  simulations/
    life/
      life.py

(我在每个文件夹中也有__init__.py,为了可读性,这里省略了)

如何从life模块内部导入nib模块?我希望不修补sys.path就可以做到。

注:正在运行的主模块位于ptdraft文件夹中。


可以使用相对导入(python>=2.5):

1
from ... import nib

(python 2.5的新功能)pep 328:绝对和相对导入

编辑:添加了另一个点"."以向上移动两个包


相对进口(如from .. import mymodule中所述)只能在一个包中工作。要导入当前模块的父目录中的"mymodule",请执行以下操作:

1
2
3
4
5
6
import os,sys,inspect
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parentdir = os.path.dirname(currentdir)
sys.path.insert(0,parentdir)

import mymodule

编辑:并不总是给定__file__属性。我现在建议使用inspect模块来检索当前文件的文件名(和路径),而不是使用os.path.abspath(__file__)


这个问题似乎与模块在父目录或类似的目录中无关。

您需要将包含ptdraft的目录添加到pythonpath

你说import nib和你一起工作,这可能意味着你把ptdraft本身(而不是它的父母)加入了pythonpath。


我也发布了一个类似的答案,关于从兄弟包中导入的问题。你可以在这里看到。下面用python 3.6.5(anaconda,conda 4.5.1),windows 10机器进行测试。

没有sys.path hacks的解决方案安装程序

我假设与问题中的文件夹结构相同

1
2
3
4
5
6
7
8
9
.
└── ptdraft
    ├── __init__.py
    ├── nib.py
    └── simulations
        ├── __init__.py
        └── life
            ├── __init__.py
            └── life.py

我把.称为根文件夹,在我的例子中,它位于C:\tmp\test_imports中。

步骤1)将setup.py添加到根文件夹

setup.py的内容可以简单地

1
2
3
from setuptools import setup, find_packages

setup(name='myproject', version='1.0', packages=find_packages())

基本上,"any"setup.py可以工作。这只是一个最小的工作示例。

2)使用虚拟环境

如果您熟悉虚拟环境,请激活其中一个,然后跳到下一步。虚拟环境的使用并不是绝对必要的,但从长远来看,它们确实会帮助您(当您有一个以上的项目正在进行时)。最基本的步骤是(在根文件夹中运行)

  • 创建虚拟环境
    • python -m venv venv
  • 激活虚拟环境
    • . /venv/bin/activate(linux)或./venv/Scripts/activate(win)

要了解更多信息,只需谷歌出"python virtual env tutorial"或类似内容。除了创建、激活和停用之外,您可能永远不需要任何其他命令。

创建并激活虚拟环境后,控制台应在括号中给出虚拟环境的名称。

1
2
3
PS C:\tmp\test_imports> python -m venv venv
PS C:\tmp\test_imports> .\venv\Scripts\activate
(venv) PS C:\tmp\test_imports>

3)pip在可编辑状态下安装项目

使用pip安装顶级软件包myproject。技巧是在安装时使用-e标志。这样,它将以可编辑状态安装,并且对.py文件所做的所有编辑都将自动包含在已安装的包中。

在根目录中,运行

pip install -e .(注意点,它代表"当前目录")。

您还可以看到它是通过使用pip freeze安装的。

1
2
3
4
5
6
7
(venv) PS C:\tmp\test_imports> pip install -e .
Obtaining file:///C:/tmp/test_imports
Installing collected packages: myproject
  Running setup.py develop for myproject
Successfully installed myproject
(venv) PS C:\tmp\test_imports> pip freeze
myproject==1.0

4)每项进口附加mainfolder进口

在这个例子中,mainfolderptdraft。这样做的好处是,您不会与其他模块名(来自Python标准库或第三方模块)发生名称冲突。

示例用法尼比

1
2
def function_from_nib():
    print('I am the return value from function_from_nib!')

生命.Py

1
2
3
4
from ptdraft.nib import function_from_nib

if __name__ == '__main__':
    function_from_nib()

运行寿命

1
2
(venv) PS C:\tmp\test_imports> python .\ptdraft\simulations\life\life.py
I am the return value from function_from_nib!


如果将模块文件夹添加到python path不起作用,可以修改程序中的sys.path列表,其中python解释器搜索要导入的模块,python文档说:

When a module named spam is imported, the interpreter first searches for a built-in module with that name. If not found, it then searches for a file named spam.py in a list of directories given by the variable sys.path. sys.path is initialized from these locations:

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

After initialization, Python programs can modify sys.path. The directory containing the script being run is placed at the beginning of the search path, ahead of the standard library path. This means that scripts in that directory will be loaded instead of modules of the same name in the library directory. This is an error unless the replacement is intended.

< /块引用>

知道了这一点,您可以在程序中执行以下操作:

1
2
3
4
5
6
7
8
import sys
# Add the ptdraft folder path to the sys.path list
sys.path.append('/path/to/ptdraft/')

# Now you can import your module
from ptdraft import nib
# Or just
import ptdraft


您可以在sys.path中列出的"模块搜索路径"中使用OS依赖路径。这样您就可以轻松地添加父目录,如下所示

1
2
import sys
sys.path.insert(0,'..')

如果要添加父目录,

1
sys.path.insert(0,'../..')


不太了解python 2。
在python 3中,可以按如下方式添加父文件夹:

1
2
import sys
sys.path.append('..')

…然后可以从中导入模块


下面是更通用的解决方案,它将父目录包含到sys.path中(对我有效):

1
2
import os.path, sys
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))


这里有一个简单的答案,你可以看到它是如何工作的,小而跨平台。它只使用内置模块(ossysinspect,因此应该可以工作在任何操作系统(OS)上,因为python是为此而设计的。

更短的应答代码-更少的行和变量

1
2
3
4
5
6
from inspect import getsourcefile
import os.path as path, sys
current_dir = path.dirname(path.abspath(getsourcefile(lambda:0)))
sys.path.insert(0, current_dir[:current_dir.rfind(path.sep)])
import my_module  # Replace"my_module" here with the module name.
sys.path.pop(0)

对于少于此的线路,用import os.path as path, sys, inspect替换第二条线路。在getsourcefile的开头(第3行)加上inspect.,去掉第一行。-但是,这会导入所有模块,因此可能需要更多的时间、内存和资源。

我的答案代码(较长版本)

1
2
3
4
5
6
7
8
9
10
11
from inspect import getsourcefile
import os.path
import sys

current_path = os.path.abspath(getsourcefile(lambda:0))
current_dir = os.path.dirname(current_path)
parent_dir = current_dir[:current_dir.rfind(os.path.sep)]

sys.path.insert(0, parent_dir)

import my_module  # Replace"my_module" here with the module name.

它使用一个来自堆栈溢出应答的示例,如何获取当前的路径在python中执行文件?使用内置工具查找运行代码的源(文件名)。

1
2
from inspect import getsourcefile  
from os.path import abspath

Next, wherever you want to find the source file from you just use:

1
abspath(getsourcefile(lambda:0))

我的代码为sys.path添加了一个文件路径,即python路径列表因为这允许python从该文件夹导入模块。

在代码中导入模块后,最好在新行上运行sys.path.pop(0)。当添加的文件夹具有与导入的另一个模块同名的模块时在节目后期。您需要删除导入前添加的列表项,而不是其他路径。如果您的程序不导入其他模块,则不删除文件路径是安全的,因为程序结束(或重新启动python shell)后,对sys.path所做的任何编辑都将消失。

关于文件名变量的说明

我的答案不使用__file__变量获取运行的文件路径/文件名。因为这里的用户经常说它不可靠。你不应该用它用于从其他人使用的程序中的父文件夹导入模块。

一些不起作用的例子(引用这个堆栈溢出问题):

?在某些平台上找不到?有时不是完整的文件路径

  • py2exe doesn't have a __file__ attribute, but there is a workaround
  • When you run from IDLE with execute() there is no __file__ attribute
  • OS X 10.6 where I get NameError: global name '__file__' is not defined


对于我来说,访问父目录的最短和最喜欢的一行程序是:

1
sys.path.append(os.path.dirname(os.getcwd()))

或:

1
sys.path.insert(1, os.path.dirname(os.getcwd()))

getcwd()返回当前工作目录的名称,os.path.dirname(directory_name)返回传递的目录的名称。

实际上,在我看来,python项目体系结构应该以这样的方式完成:子目录中的任何模块都不会使用父目录中的任何模块。如果发生这样的事情,就值得重新考虑项目树。

另一种方法是将父目录添加到pythonpath系统环境变量。



import sys
sys.path.append('../')


当不在包含__init__.py文件的包环境中时,pathlib库(包含在>=python 3.4中)使将父目录的路径附加到python path变得非常简洁和直观:

1
2
3
import sys
from pathlib import Path
sys.path.append(str(Path('.').absolute().parent))


上述解决方案也可以。这个问题的另一个解决办法是

如果要从顶级目录导入任何内容。然后,

1
from ...module_name import *

另外,如果您想从父目录导入任何模块。然后,

1
from ..module_name import *

另外,如果您想从父目录导入任何模块。然后,

1
from ...module_name.another_module import *

这样,您可以导入任何特定的方法(如果需要)。


和过去的答案一样的风格-但是用更少的行:p

1
2
3
import os,sys
parentdir = os.path.dirname(__file__)
sys.path.insert(0,parentdir)

文件返回您工作的位置


使用库。创建一个名为nib的库,使用setup.py安装它,让它驻留在站点包中,您的问题就解决了。你不必把你做的每样东西都装在一个包裹里。把它分成几块。


在Linux系统中,您可以创建一个从"life"文件夹到nib.py文件的软链接。然后,您可以简单地导入它,如下所示:

1
import nib