从父文件夹导入模块

我正在运行Python 2.5。

这是我的文件夹树:

ptdraft/nib.pysimulations/life/life.py

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

如何从life模块内部导入nib模块?我希望这是可能的,而不是修补sys.path.

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

1246775 次浏览

这个问题似乎与模块位于父目录或类似的东西无关。

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

您说import nib与您一起工作,这可能意味着您将ptdraft本身(而不是其父级)添加到PYTHONPATH。

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

from ... import nib

(Python 2.5中的新功能)PEP 328:绝对和相对导入

编辑:添加另一个点'.'以增加两个包

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

当导入名为垃圾邮件的模块时,解释器首先搜索具有该名称的内置模块。如果没有找到,它会在变量给出的目录列表中搜索名为spam.py的文件sys.path.sys.path从以下位置初始化:

  • 包含输入脚本(或当前目录)的目录。
  • PYTHONPATH(目录名称列表,语法与shell变量PATH相同)。
  • 依赖于安装的默认值。

初始化后,Python程序可以修改sys.path。包含正在运行的脚本的目录放置在搜索路径的开头,在标准库路径之前。这意味着将加载该目录中的脚本而不是库目录中同名的模块。除非有意替换,否则这是一个错误。

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

import sys# Add the ptdraft folder path to the sys.path listsys.path.append('/path/to/ptdraft/')
# Now you can import your modulefrom ptdraft import nib# Or justimport ptdraft

相对导入(如from .. import mymodule)仅在包中工作。要导入当前模块的父目录中的“mymod”:

import osimport sysimport 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__属性。我现在建议使用检查模块来检索当前文件的文件名(和路径),而不是使用os.path.abspath(__file__)

与过去的答案相同的风格-但行较少:P

import os,sysparentdir = os.path.dirname(__file__)sys.path.insert(0,parentdir)

文件返回您正在工作的位置

以下是更通用的解决方案,将父目录包含在sys.path中(为我工作):

import os.path, syssys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))

您可以在sys.path中列出的“模块搜索路径”中使用操作系统依赖路径。因此,您可以轻松添加父目录,如下

import syssys.path.insert(0,'..')

如果要添加父-父目录,

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

这适用于python 2和3。

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

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

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

from inspect import getsourcefileimport os.path as path, syscurrent_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的开头添加inspect.(第3行)并删除第一行。
-但是这会导入所有模块,因此可能需要更多的时间,内存和资源。

我的答案代码(更长的版本

from inspect import getsourcefileimport os.pathimport 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.

它使用Stack Overflow答案中的一个示例如何获取当前
的路径在Python中执行文件?
使用内置工具查找正在运行的代码的源(文件名)。

from inspect import getsourcefilefrom os.path import abspath

接下来,无论您想从哪里找到源文件,只需使用:

abspath(getsourcefile(lambda:0))

我的代码将文件路径添加到sys.pathpython路径列表
因为这允许Python从该文件夹导入模块。

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

关于文件名变量的注释

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

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

•它不是完整的文件路径1158224">不能被在某些平台上找到•它有时不是完整的文件路径

  • py2exe没有__file__属性,但有一个解决方法
  • 当您使用execute()从IDLE运行时,没有__file__属性
  • OS X 10.6我得到NameError: global name '__file__' is not defined的地方

PYTHONPATH库(包含在>=Python 3.4中)使得将父目录的路径附加到PYTHONPATH非常简洁和直观:

import sysfrom pathlib import Pathsys.path.append(str(Path('.').absolute().parent))

导入系统sys.path.append('…/')

对python 2了解不多。
在python 3中,父文件夹可以添加如下:

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

…然后可以从中导入模块

在Linux系统中,您可以创建从“life”文件夹到nib.py文件的软链接。

import nib

对我来说,访问父目录的最短和我最喜欢的线条是:

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

或:

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

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

实际上,在我看来,Python 项目架构应该采用子目录中的任何模块都不会使用父目录中的任何模块的方式。如果发生了这样的事情,就值得重新考虑项目树。

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

关于从兄弟软件包导入的问题,我也发布了一个类似的答案。你可以看到它 给你

没有 sys.path缺陷的解决方案

摘要

  • 将代码打包到一个文件夹中(例如 packaged_stuff)
  • 创建一个使用 Setuptools.setup ()setup.py脚本。
  • Pip 使用 pip install -e <myproject_folder>安装处于可编辑状态的软件包
  • 使用 from packaged_stuff.modulename import function_name导入

设置

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

.
└── ptdraft
├── __init__.py
├── nib.py
└── simulations
├── __init__.py
└── life
├── __init__.py
└── life.py

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

步骤

  1. setup.py添加到根文件夹 -- setup.py的内容可以很简单
    from setuptools import setup, find_packages


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

基本上“任何”setup.py都可以工作,这只是一个最小的工作示例。

  1. 使用虚拟环境

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

  • 创建虚拟环境
    • python -m venv venv
  • 启动虚拟环境
    • . venv/bin/activate(Linux)或 ./venv/Scripts/activate(Win)
  • 关闭虚拟环境
    • deactivate(Linux)

要了解更多关于这方面的信息,只需 Google 出“ pythonviralenv 教程”或类似的内容。除了创建、激活和停用之外,您可能永远不需要任何其他命令。

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

PS C:\tmp\test_imports> python -m venv venv
PS C:\tmp\test_imports> .\venv\Scripts\activate
(venv) PS C:\tmp\test_imports>
  1. Pip 将项目安装在可编辑状态

使用 pip安装顶级软件包 myproject。诀窍是在进行安装时使用 -e标志。通过这种方式,它被安装在可编辑状态,并且对。Py 文件将自动包含在安装的包中。

在根目录中,运行

(注意这个点,它代表“工作目录”)

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

(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
  1. 通过在每次导入前添加 mainfolder来导入

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


示例用法

Nib.py

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

生活,快乐

from ptdraft.nib import function_from_nib


if __name__ == '__main__':
function_from_nib()

跑步生活 Py

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

上面提到的解决方案也很好。这个问题的另一个解决方案是

如果要从顶级目录导入任何内容,则,

from ...module_name import *

另外,如果要从父目录导入任何模块,

from ..module_name import *

另外,如果要从父目录导入任何模块,

from ...module_name.another_module import *

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

我发现以下方法可以从脚本的父目录导入一个包。在本例中,我想从 app.db包导入 env.py中的函数。

.
└── my_application
└── alembic
└── env.py
└── app
├── __init__.py
└── db
import os
import sys
currentdir = os.path.dirname(os.path.realpath(__file__))
parentdir = os.path.dirname(currentdir)
sys.path.append(parentdir)

在木星笔记本(打开与木星实验室或木星笔记本)

只要你在木星笔记本上工作,这个简短的解决方案可能是有用的:

%cd ..
import nib

即使没有 __init__.py文件,它也能工作。

我在 Linux 和 Windows7上用 Anaconda3进行了测试。

虽然这违反了所有的规则,但我还是想提一下这种可能性:

您可以首先将文件从父目录复制到子目录。下一步导入它,然后删除复制的文件:

例如 life.py:

import os
import shutil


shutil.copy('../nib.py', '.')
import nib
os.remove('nib.py')


# now you can use it just fine:
nib.foo()

当然,当 nibs 试图导入/读取具有相对导入/路径的其他文件时,可能会出现一些问题。

我建这个图书馆就是为了这个。

Https://github.com/fx-kirin/add_parent_path

# Just add parent path
add_parent_path(1)


# Append to syspath and delete when the exist of with statement.
with add_parent_path(1):
# Import modules in the parent path
pass

我有一个问题,我不得不导入一个 Flask 应用程序,这有一个导入,也需要导入文件在单独的文件夹。这在一定程度上使用了 雷米的回答,但是假设我们有一个这样的存储库:

.
└── service
└── misc
└── categories.csv
└── test
└── app_test.py
app.py
pipeline.py

然后,在从 app.py文件导入应用程序对象之前,我们将目录向上更改一级,因此当我们导入应用程序(导入 pipeline.py)时,我们也可以读取其他文件,比如 csv 文件。

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)


os.chdir('../')
from app import app

导入 Flask 应用程序后,你可以使用 os.chdir('./test'),这样你的工作目录就不会改变。

虽然原作者可能不再寻找解决方案,但为了完整性,有一个简单的解决方案。它将 生活,快乐作为这样的模块运行:

cd ptdraft
python -m simulations.life.life

这样,您可以从 Nib.py导入任何内容,因为路径中有 Ptdraft目录。

我们的文件夹结构:

/myproject
project_using_ptdraft/
main.py
ptdraft/
__init__.py
nib.py
simulations/
__init__.py
life/
__init__.py
life.py

我理解这一点的方式是采用以包为中心的视图。 包根是 ptdraft,因为它是包含 __init__.py的最高级别

例如,包中的所有文件都可以对导入使用绝对路径(相对于包根目录) 在 life.py中,我们只有:

import ptdraft.nib

但是,要为了包 dev/测试的目的运行 life.py,而不是 python life.py,我们需要使用:

cd /myproject
python -m ptdraft.simulations.life.life

注意,此时我们根本不需要修改任何路径。


美国圣约瑟的兽迷大会Further Confusion 就是当我们完成 ptdraft包的时候,我们想要在驱动程序脚本中使用它,这必然是在 ptdraft包文件夹(又名 project_using_ptdraft/main.py)之外,我们需要改变路径:

import sys
sys.path.append("/myproject")  # folder that contains ptdraft
import ptdraft
import ptdraft.simulations

并使用 python main.py运行脚本没有问题。

有用链接:

我认为您可以在那个特定的示例中尝试这种方法,但是在 python3.6.3中 enter image description here

对于我来说,从较高的文件夹导入东西是可行的。

import os
os.chdir('..')

在我看来,您实际上并不需要导入父模块。让我们假设在 nib.py 中有 fun1()和 data1,需要在 life.py 中使用它们

Nib.py

import simulations.life.life as life
def func1():
pass
data1 = {}
life.share(func1, data1)

生活,快乐

func1 = data1 = None


def share(*args):
global func1, data1
func1, data1 = args

现在您可以访问 life.py 中的 fun1和数据了。当然,在你尝试使用它们之前,你必须小心地在生活中填充它们,

两行最简解

import os, sys
sys.path.insert(0, os.getcwd())

如果你想从子脚本中调用另一个子模块,父模块是你的工作目录。

您可以从任何脚本的父目录中导入所有子模块,并将其作为

python child_module1/child_script.py

我有一个 解决方案专门为 Git-仓库

首先,我使用了 sys.path.append('..')和类似的解决方案。如果导入的文件本身就是用 sys.path.append('..')导入的文件,这会导致特别的问题。

然后,我决定始终追加 git 存储库的根目录。在一行中,它看起来像这样:

sys.path.append(git.Repo('.', search_parent_directories=True).working_tree_dir)

或者更多的细节如下:

import os
import sys
import git
def get_main_git_root(path):
main_repo_root_dir = git.Repo(path, search_parent_directories=True).working_tree_dir
return main_repo_root_dir
main_repo_root_dir = get_main_git_root('.')
sys.path.append(main_repo_root_dir)

对于最初的问题: 基于存储库的根目录是什么,导入应该是

import ptdraft.nib

或者

import nib

这是对我来说最简单的解决方案:

from ptdraft import nib

在删除了一些系统路径黑客之后,我认为添加这些代码可能很有价值

我的首选方案。

注意: 这是一个帧挑战-没有必要做 暗号

假设有棵树,

project
└── pkg
└── test.py

test.py包含哪些内容

import sys, json; print(json.dumps(sys.path, indent=2))

使用路径执行只包括包目录

python pkg/test.py
[
"/project/pkg",
...
]

但是使用模块参数包括项目目录

python -m pkg.test
[
"/project",
...
]

现在,所有的导入都可以是绝对的,从项目目录。不需要进一步的欺骗。