从父文件夹导入模块
- 2024-11-26 08:37:00
- admin 原创
- 174
问题描述:
我正在运行 Python 2.5。
这是我的文件夹树:
ptdraft/
nib.py
simulations/
life/
life.py
(每个文件夹中都有__init__.py
,为了方便阅读这里省略)
如何nib
从模块内部导入模块life
?我希望可以不用修改 sys.path 来实现。
注意:正在运行的主模块位于ptdraft
文件夹中。
解决方案 1:
您可以使用相对导入(Python >= 2.5):
from ... import nib
(Python 2.5 中的新功能)PEP 328:绝对导入和相对导入
解决方案 2:
我还针对有关从兄弟包导入的问题发布了类似的答案。您可以在此处看到它。
无需sys.path
破解的解决方案
概括
将代码包装到一个文件夹中(例如
packaged_stuff
)创建
pyproject.toml
(旧替代方案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: mp est_imports
。
步骤
pyproject.toml
1)在根文件夹中添加
的内容pyproject.toml
可以简单
[project]
name = "ptdraft"
version = "0.1.0"
description = "My small project"
[build-system]
build-backend = "flit_core.buildapi"
requires = ["flit_core >=3.2,<4"]
基本上“任何”有效方法pyproject.toml
都可以。这只是一个最小工作示例,使用 flit 作为构建后端。
2)使用虚拟环境
如果您熟悉虚拟环境,请激活一个,然后跳到下一步。虚拟环境的使用不是绝对必要的,但从长远来看,它们确实会对您有所帮助(当您有多个正在进行的项目时……)。最基本的步骤是(在根文件夹中运行)
创建虚拟环境
python -m venv venv
激活虚拟环境
. venv/bin/activate
(Linux) 或./venv/Scripts/activate
(Win)
停用虚拟环境
deactivate
(Linux)
要了解更多信息,只需在 Google 上搜索“python virtualenv tutorial”或类似内容。除了创建、激活和停用之外,您可能不需要任何其他命令。
创建并激活虚拟环境后,控制台应在括号中显示虚拟环境的名称
PS C: mp est_imports> python -m venv venv
PS C: mp est_imports> .envScriptsactivate
(venv) PS C: mp est_imports>
3)pip 安装你的项目到可编辑状态
使用安装顶级包(此处ptdraft
)pip
。诀窍是-e
在安装时使用标志。这样它就会以可编辑状态安装,并且对 .py 文件所做的所有编辑都将自动包含在已安装的包中。请注意,-e
带有 pyproject.toml 的标志需要pip 21.3 或更新版本。
在根目录中运行
pip install -e .
(注意点,它代表“当前目录”)
您还可以使用以下命令安装它pip freeze
(venv) PS C: mp est_imports> pip install -e .
Obtaining file:///home/user/projects/ptdraft
Installing build dependencies ... done
Checking if build backend supports build_editable ... done
Getting requirements to build editable ... done
Preparing editable metadata (pyproject.toml) ... done
....
Successfully built ptdraft
Installing collected packages: ptdraft
Successfully installed ptdraft-0.1.0
(venv) PS C: mp est_imports> pip freeze
ptdraft==0.1.0
4)通过mainfolder
在每个导入前面添加导入
在这个例子中,mainfolder
将是ptdraft
。这样做的好处是你不会遇到与其他模块名称(来自python标准库或第三方模块)的名称冲突。
示例用法
nib.py
def function_from_nib():
print('I am the return value from function_from_nib!')
生活.py
from ptdraft.nib import function_from_nib
if __name__ == '__main__':
function_from_nib()
运行 life.py
(venv) PS C: mp est_imports> python .ptdraftsimulationslifelife.py
I am the return value from function_from_nib!
解决方案 3:
相对导入(如from .. import mymodule
)仅在包中有效。要导入当前模块父目录中的“mymodule”,请执行以下操作:
import os
import sys
import 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__)
我建议使用检查模块来检索当前文件的文件名(和路径),而不是使用。
解决方案 4:
看起来该问题与模块位于父目录中或类似的东西无关。
您需要将包含以下内容的目录添加ptdraft
到 PYTHONPATH
您说这import nib
对您有用,这可能意味着您将ptdraft
它自己(而不是它的父级)添加到了 PYTHONPATH。
解决方案 5:
您可以在sys.path中列出的“模块搜索路径”中使用与操作系统相关的路径。
因此您可以轻松地添加父目录,如下所示:
import sys
sys.path.insert(0, '..')
如果你想添加父级目录,
sys.path.insert(0, '../..')
这在Python 2和Python 3中都有效。
解决方案 6:
我对Python 2不太了解。
在Python 3中,可以按如下方式添加父文件夹:
import sys
sys.path.append('..')
...然后就可以从中导入模块。
解决方案 7:
如果将模块文件夹添加到 PYTHONPATH 不起作用,您可以修改程序中的sys.path列表,Python 解释器在其中搜索要导入的模块,Python 文档说:
当导入名为spam 的模块时,解释器首先搜索具有该名称的内置模块。如果未找到,它会在变量 sys.path 给出的目录列表中搜索名为spam.py的文件。sys.path 从以下位置初始化:
包含输入脚本的目录(或当前目录)。
PYTHONPATH(目录名称列表,语法与 shell 变量 PATH 相同)。
安装相关的默认值。
初始化后,Python 程序可以修改sys.path。包含正在运行的脚本的目录将放在搜索路径的开头,位于标准库路径之前。这意味着将加载该目录中的脚本,而不是库目录中同名的模块。除非有意替换,否则这是一个错误。
了解了这一点,您可以在程序中执行以下操作:
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
解决方案 8:
pathlib 库(包含在 >= Python 3.4 中)使得将父目录的路径附加到 PYTHONPATH 变得非常简洁直观:
import sys
from pathlib import Path
sys.path.append(str(Path(__file__).absolute().parent))
解决方案 9:
这是一个简单的答案,您可以看到它是如何工作的,小巧且跨平台。它仅使用内置模块(os
、sys
和inspect
),因此它应该可以在任何操作系统 (OS) 上运行,因为 Python 是为此设计的。
答案的简短代码——更少的行数和变量
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
。inspect.
在(第 3 行)开头添加getsourcefile
并删除第一行。
但是这会导入所有模块,因此可能需要更多的时间、内存和资源。
我的答案的代码(较长的版本)
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.
它使用 Stack Overflow 答案“如何获取 Python 中当前执行文件的路径?”中的示例,使用内置工具查找正在运行的代码的源(文件名)。
from inspect import getsourcefile from os.path import abspath
接下来,无论您想从哪里找到源文件,只需使用:
abspath(getsourcefile(lambda:0))
我的代码将文件路径添加到Python 路径列表sys.path
中,
因为这允许 Python 从该文件夹导入模块。
sys.path.pop(0)
在代码中导入模块后,如果添加的文件夹中有一个模块与程序中稍后导入的另一个模块同名,则最好在新行上运行。您需要删除导入前添加的列表项,而不是其他路径。
如果您的程序没有导入其他模块,那么不删除文件路径是安全的,因为在程序结束后(或重新启动 Python shell),所做的任何编辑都会sys.path
消失。
关于文件名变量的注释
我的答案不使用__file__
变量来获取正在运行的代码的文件路径/文件名,因为这里的用户经常将其描述为不可靠的。 您不应该在其他人使用的程序中使用它从父文件夹导入模块。
一些不起作用的例子(引用自这个Stack Overflow 问题):
•在某些平台上找不到。有时它不是完整的文件路径
py2exe
没有__file__
属性,但有一个解决方法当您从 IDLE 运行时,
execute()
没有__file__
属性OS X 10.6 我在哪里得到
NameError: global name '__file__' is not defined
解决方案 10:
在Jupyter Notebook中(使用JupyterLab或 Jupyter Notebook 打开)
只要你在 Jupyter Notebook 中工作,这个简短的解决方案可能会有用:
%cd ..
import nib
即使没有__init__.py
文件它也能工作。
我使用 Linux 和 Windows 7 上的Anaconda 3对其进行了测试。
解决方案 11:
这是一个更通用的解决方案,将父目录包含到sys.path中(对我有用):
import os.path, sys
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))
解决方案 12:
我发现以下方法适用于从脚本的父目录导入包。在示例中,我想env.py
从app.db
包中导入函数。
.
└── 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)
解决方案 13:
前面提到的解决方案也可以。此问题的另一种解决方案是:
如果你想从顶层目录导入任何内容。那么,
from ...module_name import *
另外,如果你想从父目录导入任何模块。那么,
from ..module_name import *
另外,如果你想从父目录导入任何模块。那么,
from ...module_name.another_module import *
这样,如果您愿意,您可以导入任何特定的方法。
解决方案 14:
两行最简单的解决方案
import os, sys
sys.path.insert(0, os.getcwd())
如果父级是您的工作目录,并且您想从子脚本调用另一个子模块。
您可以在任何脚本中从父目录导入所有子模块并将其执行为
python child_module1/child_script.py
解决方案 15:
我认为您可以在该特定示例中尝试此操作,但在 Python 3.6.3 中:
解决方案 16:
为了完整性,有一个简单的解决方案。就是将life.py作为模块运行,如下所示:
cd ptdraft
python -m simulations.life.life
这样,您可以从nib.py导入任何内容,因为ptdraft目录位于路径中。
解决方案 17:
对于我来说,访问父目录最短也是我最喜欢的一行代码是:
sys.path.append(os.path.dirname(os.getcwd()))
或者:
sys.path.insert(1, os.path.dirname(os.getcwd()))
os.getcwd() 返回当前工作目录的名称,os.path.dirname(directory_name) 返回传递的目录名称。
实际上,我认为 Python 项目架构应该这样设计:子目录中的任何模块都不会使用父目录中的任何模块。如果发生这种情况,就值得重新考虑项目树。
另一种方法是将父目录添加到 PYTHONPATH 系统环境变量。
解决方案 18:
import sys
sys.path.append('../')
解决方案 19:
这与过去的答案风格相同,但行数更少:P
import os, sys
parentdir = os.path.dirname(__file__)
sys.path.insert(0, parentdir)
文件返回您正在工作的位置。
解决方案 20:
我有一个专门针对 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
解决方案 21:
在 Linux 系统中,你可以从“life”文件夹创建到 nib.py 文件的软链接。然后,你可以简单地将其导入,如下所示:
import nib
解决方案 22:
我们的文件夹结构:
/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
但是,为了运行life.py
包开发/测试目的,python life.py
我们需要使用:
cd /myproject
python -m ptdraft.simulations.life.life
请注意,此时我们根本不需要摆弄任何路径。
进一步的混乱是当我们完成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
运行脚本而没有问题。
有用的链接:
Python 幕后花絮 #11:Python 导入系统如何工作(了解如何
__init__.py
使用)运行包初始化代码
在 Python 3 中,从同一包内和包外导入模块
即使使用 __init__.py,如何修复“尝试在非包中相对导入”
解决方案 23:
使用 pathlib.Path
import sys
from pathlib import Path
sys.path.append(str(Path(f"{__file__}").parent.parent))
import my_module
解决方案 24:
使用库。创建一个名为 nib 的库,使用 setup.py 安装它,让它驻留在 site-packages 中,您的问题就解决了。您不必将您创建的所有东西都塞进一个包中。将其分解成几个部分。
解决方案 25:
我遇到了一个问题,我必须导入一个 Flask 应用程序,该应用程序的导入也需要导入单独文件夹中的文件。这部分使用了Remi 的答案,但假设我们有一个如下所示的存储库:
.
└── 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')
,这样您的工作目录就不会改变。
解决方案 26:
我觉得你其实不需要导入父模块。假设在 nib.py 中你有 func1() 和 data1,你需要在 life.py 中使用
nib.py
import simulations.life.life as life
def func1():
pass
data1 = {}
life.share(func1, data1)
生活.py
func1 = data1 = None
def share(*args):
global func1, data1
func1, data1 = args
现在,您可以访问 life.py 中的 func1 和数据。当然,在尝试使用它们之前,您必须小心地在 life.py 中填充它们,
解决方案 27:
在删除一些系统路径 hack 之后,我认为添加
我喜欢的解决方案。
注意:这是一个框架挑战 - 没有必要在代码中进行。
假设有一棵树,
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",
...
]
现在,所有导入都可以从项目目录绝对导入。无需进一步的欺骗。
解决方案 28:
在开发新模块时我遇到了很多困难,但最终还是选择了将目录附加到 os.path 来解决问题,所以我所做的就是在我的项目中的每个目录中添加以下内容: __init__.py
import os, sys
pardir = ""
current_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.dirname(current_dir))
while pardir != "p":
parent_dir = os.path.abspath(os.path.join(current_dir, os.pardir))
pardir = os.path.basename(parent_dir)
current_dir = parent_dir
sys.path.append(parent_dir)
其中“p”是我的项目文件夹,例如:c:/p/ 此解决方案在向其他人开放项目时不会对您有所帮助,您必须(像我一样)找到另一个解决方案(我可能会在完成代码后在此处发布我如何从这里开始)但至少这会帮助您更快地开始原型设计。我知道这个解决方案已经讨论过了,我只是想分享我的复制粘贴解决方案。我的结构是这样的:
p
|---project
|---src
| |----__init__.py
| |----module.py
|---tests
| |----__init__.py
| |----tests.py
|---__init__.py
在所有地方都有一个__init__.py
并没有帮助我从 p/project/tests/tests.py 运行我的 tests.py
到目前为止,我尝试了很多解决方案,但大多数都让我感到害怕或根本不起作用。为什么用 Python 会这么复杂?我选择的解决方案也没有让我满意。
解决方案 29:
首先是代码:
from pathlib import Path
import sys
ptdraft = str(Path(sys.path[0]).parent.parent)
sys.path.insert(0, ptdraft)
import nib
根据文档,
sys.path 从以下位置初始化:
包含输入脚本的目录(未指定文件时为当前目录)。
PYTHONPATH(目录名称列表,语法与 shell 变量 PATH 相同)。
安装相关的默认值(按照惯例包括站点包目录,由站点模块处理)。
因此,sys.path
像这样的列表["script/directory/path", "pythonpath/element/1", "pythonpath/element/2", ..., ".../lib/python3.x", ..., ".../lib/python3.x/site-packages"]
脚本目录的路径始终位于该列表的 0 位置。由于 OP 有nib.py
他想要导入两个目录的模块 ( ),因此他可以使用 来获取其目录Path(sys.path[0].parent.parent)
。sys.path
仅适用于字符串,因此结果必须包装在 中str()
。
下一步是将此路径插入到 中的正确位置sys.path
。仅当目录中的任何地方都可以找到具有相同名称的多个模块时,位置才重要sys.path
。对此有三种合理的选择:
.insert(0, ptdraft)
插入到列表的开头,因此具有最高优先级。.insert(1, ptdraft)
将(祖父)父目录放在脚本目录之后和目录之前PYTHONPATH
。.append(ptdraft)
将(祖)父目录放在最后作为后备。
解决方案 30:
这是对我来说最简单的解决方案:
from ptdraft import nib
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 项目管理必备:盘点2024年13款好用的项目管理软件
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)