从父文件夹导入模块
我正在运行Python 2.5。
这是我的文件夹树:(
ptdraft/
nib.py
simulations/
life/
life.py
每个文件夹中都有 __init__.py
,为了便于阅读,此处省略)
如何从 life 内部导入
nib
模块模块? 我希望无需修改 sys.path 即可完成。
注意:正在运行的主模块位于 ptdraft 文件夹中。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(30)
您可以使用相对导入(Python >= 2.5):
(Python 2.5 中的新增功能)PEP 328:绝对和相对导入
You could use relative imports (Python >= 2.5):
(What’s New in Python 2.5) PEP 328: Absolute and Relative Imports
我也针对有关从兄弟包导入的问题发布了类似的答案。 您可以在此处查看它。
没有
sys.path
hacks 的解决摘要
packaged_stuff
)pyproject.toml
(较旧的替代方案:setup.py
)pip install -e
from packaged_stuff.modulename import function_name
导入设置
问题中的文件夹结构相同
我假设与我称为
的 。 根文件夹,在我的例子中,它位于 C:\tmp\test_imports
中。步骤
1) 将
pyproject.toml
添加到根文件夹pyproject.toml
的内容可以是简单的基本上“任何”有效的
pyproject.toml
> 会起作用的。 这只是一个最小的工作示例,它使用 flit 作为构建后端。2) 使用虚拟环境
如果您熟悉虚拟环境,请激活一个虚拟环境,然后跳到下一步。使用虚拟环境并不是绝对必需的,但它们会从长远来看,确实可以帮助您(当您有超过 1 个项目正在进行时......)。 最基本的步骤是(在根文件夹中运行)
python -m venv venv
./venv/Scripts/activate
(Win)停用
(Linux)要了解更多信息,只需 Google 出“python virtualenv 教程”或类似内容即可。 除了创建、激活和停用之外,您可能不需要任何其他命令。
创建并激活虚拟环境后,您的控制台应在括号中给出虚拟环境的名称
3) pip install 您的项目处于可编辑状态
使用
ptdraft
) >点。 诀窍是在安装时使用-e
标志。 这样它就会以可编辑状态安装,并且对 .py 文件所做的所有编辑都将自动包含在已安装的包中。 请注意,pyproject.toml 的-e
标志需要 pip 21.3 或更高版本。在根目录下,运行
pip install -e .
(注意点,它代表“当前目录”)您还可以看到它是使用
pip freeze
安装的4) 通过在每次导入前添加
mainfolder
进行导入在此示例中,
mainfolder
将为ptdraft
。 这样做的优点是您不会遇到与其他模块名称(来自 python 标准库或第 3 方模块)的名称冲突。用法示例
nib.py
life.py
运行 life.py
I posted a similar answer also to the question regarding imports from sibling packages. You can see it here.
Solution without
sys.path
hacksSummary
packaged_stuff
)pyproject.toml
(older alternative:setup.py
)pip install -e <myproject_folder>
from packaged_stuff.modulename import function_name
Setup
I assume the same folder structure as in the question
I call the
.
the root folder, and in my case it is located inC:\tmp\test_imports
.Steps
1) Add a
pyproject.toml
to the root folderThe contents of the
pyproject.toml
can be simplyBasically "any" valid
pyproject.toml
would work. This is just a minimal working example, which uses flit as build backend.2) Use a virtual environment
If you are familiar with virtual environments, activate one, and skip to the next step. Usage of virtual environments are not absolutely required, but they will really help you out in the long run (when you have more than 1 project ongoing..). The most basic steps are (run in the root folder)
python -m venv venv
. venv/bin/activate
(Linux) or./venv/Scripts/activate
(Win)deactivate
(Linux)To learn more about this, just Google out "python virtualenv tutorial" or similar. You probably never need any other commands than creating, activating and deactivating.
Once you have made and activated a virtual environment, your console should give the name of the virtual environment in parenthesis
3) pip install your project in editable state
Install your top level package (here
ptdraft
) usingpip
. The trick is to use the-e
flag when doing the install. This way it is installed in an editable state, and all the edits made to the .py files will be automatically included in the installed package. Note that the-e
flag with pyproject.toml requires pip 21.3 or newer.In the root directory, run
pip install -e .
(note the dot, it stands for "current directory")You can also see that it is installed by using
pip freeze
4) Import by prepending
mainfolder
to every importIn this example, the
mainfolder
would beptdraft
. This has the advantage that you will not run into name collisions with other module names (from python standard library or 3rd party modules).Example Usage
nib.py
life.py
Running life.py
相对导入(如
from .. import mymodule
)仅在包中有效。要导入当前模块的父目录中的“mymodule”:
注意:并不总是给出
__file__
属性。 我建议使用检查模块来检索当前文件的文件名(和路径),而不是使用 os.path.abspath(__file__) 。Relative imports (as in
from .. import mymodule
) only work in a package.To import 'mymodule' that is in the parent directory of your current module:
Note: The
__file__
attribute is not always given. Instead of usingos.path.abspath(__file__)
I suggest using the inspect module to retrieve the filename (and path) of the current file.看来问题与模块位于父目录或类似目录中无关。
您需要将包含
ptdraft
的目录添加到 PYTHONPATH您说
import nib
可以与您一起使用,这可能意味着您添加了ptdraft
本身(不是它的父级)到 PYTHONPATH。It seems that the problem is not related to the module being in a parent directory or anything like that.
You need to add the directory that contains
ptdraft
to PYTHONPATHYou said that
import nib
worked with you, that probably means that you addedptdraft
itself (not its parent) to PYTHONPATH.您可以在“模块搜索路径”中使用sys.path中列出的与操作系统相关的路径。
因此,您可以轻松添加父目录,如下所示:
如果您想添加父目录,
这在 Python 2 和 Python 3。
You can use an OS-dependent path in "module search path" which is listed in sys.path.
So you can easily add the parent directory like the following:
If you want to add the parent-parent directory,
This works both in Python 2 and Python 3.
我对 Python 2 不太了解。
在 Python 3 中,可以按如下方式添加父文件夹:
...然后人们可以从中导入模块。
I don't know much about Python 2.
In Python 3, the parent folder can be added as follows:
...and then one is able to import modules from it.
如果将模块文件夹添加到 PYTHONPATH 不起作用,您可以修改程序中的 sys.path 列表,Python 解释器在其中搜索要导入的模块,即 Python 文档 说:
知道这一点后,您可以在程序中执行以下操作:
If adding your module folder to the PYTHONPATH didn't work, You can modify the sys.path list in your program where the Python interpreter searches for the modules to import, the Python documentation says:
Knowing this, you can do the following in your program:
这是一个简单的答案,因此您可以了解它是如何工作的,小型且跨平台的。 它仅使用内置模块(
os
、sys
和inspect
),因此它应该可以在任何操作系统 (OS) 上运行,因为 Python 是为此而设计。更短的答案代码 - 更少的行和变量
对于比这更少的行,请将第二行替换为
import os.path as path, sys, inform
。 在getsourcefile
开头添加inspect.
(第 3 行)并删除第一行。我的答案的代码(较长版本)
它使用 Stack Overflow 答案中的示例如何在Python中获取当前执行文件的路径?来查找使用内置工具运行代码的源(文件名)。
我的代码将文件路径添加到
sys.path
,Python 路径列表因为这允许 Python 从该文件夹导入模块。
在代码中导入模块后,如果添加的文件夹具有与导入的另一个模块同名的模块,最好在新行上运行
sys.path.pop(0)
稍后在节目中。 您需要删除导入之前添加的列表项,而不是其他路径。如果您的程序不导入其他模块,则不要删除文件路径是安全的,因为在程序结束(或重新启动 Python shell)后,对 sys.path 所做的任何编辑都会消失。
关于文件名变量的注释
我的答案不使用 __file__ 变量来获取正在运行的代码的文件路径/文件名,因为这里的用户经常将其描述为不可靠。 您不应该在其他人使用的程序中使用它从父文件夹导入模块。
一些它不起作用的示例(引用自这个堆栈溢出问题):
• 它在某些平台上找不到。 有时不是完整的文件路径
Here is an answer that's simple so you can see how it works, small and cross-platform. It only uses built-in modules (
os
,sys
andinspect
), so it should work on any operating system (OS) because Python is designed for that.Shorter code for answer - fewer lines and variables
For fewer lines than this, replace the second line with
import os.path as path, sys, inspect
. Addinspect.
at the start ofgetsourcefile
(line 3) and remove the first line.The code for my answer (longer version)
It uses an example from a Stack Overflow answer How do I get the path of the current executed file in Python? to find the source (filename) of running code with a built-in tool.
My code adds a file path to
sys.path
, the Python path listbecause this allows Python to import modules from that folder.
After importing a module in the code, it's a good idea to run
sys.path.pop(0)
on a new line when that added folder has a module with the same name as another module that is imported later in the program. You need to remove the list item added before the import, not other paths.If your program doesn't import other modules, it's safe to not delete the file path because after a program ends (or restarting the Python shell), any edits made to
sys.path
disappear.Notes about a filename variable
My answer doesn't use the
__file__
variable to get the file path/filename of running code because users here have often described it as unreliable. You shouldn't use it for importing modules from parent folder in programs used by other people.Some examples where it doesn't work (quote from this Stack Overflow question):
• it can't be found on some platforms. It sometimes isn't the full file path
pathlib 库(包含在 >= Python 3.4 中)使得将父目录的路径附加到 PYTHONPATH 变得非常简洁和直观:
The pathlib library (included with >= Python 3.4) makes it very concise and intuitive to append the path of the parent directory to the PYTHONPATH:
这是一个更通用的解决方案,它将父目录包含到 sys.path 中(它对我有用):
Here is a more generic solution that includes the parent directory into sys.path (it works for me):
在 Jupyter Notebook 中(使用 JupyterLab 或 Jupyter Notebook)
只要您在 Jupyter Notebook 中工作,这个简短的解决方案可能会很有用:
即使没有
__init__.py
文件。我在 Linux 和 Windows 7 上使用 Anaconda 3 进行了测试。
In a Jupyter Notebook (opened with JupyterLab or Jupyter Notebook)
As long as you're working in a Jupyter Notebook, this short solution might be useful:
It works even without an
__init__.py
file.I tested it with Anaconda 3 on Linux and Windows 7.
我发现以下方法适用于从脚本的父目录导入包。 在示例中,我想从
app.db
包中导入env.py
中的函数。I found the following way works for importing a package from the script's parent directory. In the example, I would like to import functions in
env.py
fromapp.db
package.前面提到的解决方案也很好。 此问题的另一个解决方案是:
如果您想从顶级目录导入任何内容。 然后,
另外,如果您想从父目录导入任何模块。 然后,
另外,如果您想从父目录导入任何模块。 然后,
这样您就可以根据需要导入任何特定方法。
The previous mentioned solutions are also fine. Another solution to this problem is:
If you want to import anything from top level directory. Then,
Also, if you want to import any module from the parent directory. Then,
Also, if you want to import any module from the parent directory. Then,
This way you can import any particular method if you want to.
两行最简单的解决方案
如果父级是您的工作目录并且您想从子脚本调用另一个子模块。
您可以在任何脚本中从父目录导入所有子模块并将其执行为
Two line simplest solution
If parent is your working directory and you want to call another child modules from child scripts.
You can import all child modules from parent directory in any scripts and execute it as
为了完整起见,有一个简单的解决方案。 它将life.py作为模块运行,如下所示:
这样您就可以从nib.py导入任何内容,因为ptdraft目录位于路径中。
For completeness, there one simple solution. It's to run life.py as a module like this:
This way you can import anything from nib.py as ptdraft directory is in the path.
我认为您可以在该特定示例中尝试此操作,但在 Python 3.6.3 中:
I think you can try this in that specific example, but in Python 3.6.3:
对我来说,访问父目录的最短也是我最喜欢的 oneliner 是:
或者:
os.getcwd() 返回当前工作目录的名称, os.path.dirname(directory_name) 返回传递的目录名称。
实际上,在我看来,Python 项目架构应该以这样的方式完成:子目录中的任何模块都不会使用父目录中的任何模块。 如果发生类似的事情,就值得重新考虑项目树。
另一种方法是将父目录添加到 PYTHONPATH 系统环境变量中。
For me the shortest and my favorite oneliner for accessing to the parent directory is:
or:
os.getcwd() returns the name of the current working directory, os.path.dirname(directory_name) returns the directory name for the passed one.
Actually, in my opinion Python project architecture should be done the way where no one module from child directory will use any module from the parent directory. If something like this happens it is worth to rethink about the project tree.
Another way is to add parent directory to PYTHONPATH system environment variable.
我有一个专门针对 Git 存储库的解决方案。
首先,我使用了 sys.path.append('..') 和类似的解决方案。 如果您导入的文件本身是使用
sys.path.append('..')
导入文件,这会导致特别的问题。然后我决定始终附加 Git 存储库的根目录。 在一行中,它看起来像这样:
或者更详细地这样:
对于最初的问题:根据存储库的根目录是什么,导入将是
或
I have a solution specifically for Git repositories.
First I used
sys.path.append('..')
and similar solutions. This causes especially problems if you are importing files which are themselves importing files withsys.path.append('..')
.I then decided to always append the root directory of the Git repository. In one line it would look like this:
Or in more details like this:
For the original question: Based on what the root directory of the repository is, the import would be
or
这与过去的答案具有相同的风格,但行数较少:P
file 返回您正在工作的位置。
This is the same sort of style as the past answers, but in fewer lines :P
file returns the location you are working in.
在Linux系统中,您可以创建从“life”文件夹到nib.py文件的软链接。 然后,您可以简单地导入它,如下所示:
In a Linux system, you can create a soft link from the "life" folder to the nib.py file. Then, you can simply import it like:
我们的文件夹结构:
我理解的方式是有一个以包为中心的视图。
包根目录是 ptdraft,因为它是包含 __init__.py 的最顶层。
包中的所有文件都可以使用绝对路径(相对于包根目录)进口,例如
在
life.py
中,我们只需:但是,为了包开发/测试目的而运行
life.py
,而不是python life.py
,我们需要使用:请注意,此时我们根本不需要摆弄任何路径。
进一步的困惑是,当我们完成 ptdraft 包时,我们想要在驱动程序脚本中使用它,该脚本必须位于 ptdraft 包文件夹(又名 project_using_ptdraft)之外/main.py,我们需要摆弄路径:
并使用
python main.py
来毫无问题地运行脚本。有用的链接:
__init__.py
)Our folder structure:
The way I understand this is to have a package-centric view.
The package root is
ptdraft
, since it's the top most level that contains__init__.py
All the files within the package can use absolute paths (that are relative to package root) for imports, for example
in
life.py
, we have simply:However, to run
life.py
for package dev/testing purposes, instead ofpython life.py
, we need to use:Note that we didn't need to fiddle with any path at all at this point.
Further confusion is when we complete the
ptdraft
package, and we want to use it in a driver script, which is necessarily outside of theptdraft
package folder, akaproject_using_ptdraft/main.py
, we would need to fiddle with paths:and use
python main.py
to run the script without problem.Helpful links:
__init__.py
can be used)与图书馆合作。
创建一个名为 nib 的库,使用 setup.py 安装它,让它驻留在站点包中,您的问题就解决了。
您不必将制作的所有东西都装在一个包装中。 把它打碎。
Work with libraries.
Make a library called nib, install it using setup.py, let it reside in site-packages and your problems are solved.
You don't have to stuff everything you make in a single package. Break it up to pieces.
我遇到了一个问题,我必须导入 Flask 应用程序,该应用程序的导入还需要导入单独文件夹中的文件。 这部分使用了 Remi 的答案,但假设我们有一个如下所示的存储库:
然后在从
app.py
文件中,我们将目录向上一级更改,因此当我们导入应用程序(导入pipeline.py
)时,我们还可以读取其他文件,例如一个 csv 文件。导入 Flask 应用后,您可以使用 os.chdir('./test') 这样您的工作目录就不会更改。
I had a problem where I had to import a Flask application, that had an import that also needed to import files in separate folders. This is partially using Remi's answer, but suppose we had a repository that looks like this:
Then before importing the app object from the
app.py
file, we change the directory one level up, so when we import the app (which imports thepipeline.py
), we can also read in miscellaneous files like a csv file.After having imported the Flask app, you can use
os.chdir('./test')
so that your working directory is not changed.在我看来,您实际上并不需要导入父模块。 假设在 nib.py 中你有 func1() 和 data1,你需要在 life.py 中使用
nib.py
life.py
现在你可以在 life.py 中访问 func1 和 data。 当然,在尝试使用它们之前,您必须小心地将它们填充到 life.py 中,
It's seems to me that you don't really need to import the parent module. Let's imagine that in nib.py you have func1() and data1, you need to use in life.py
nib.py
life.py
And now you have the access to func1 and data in life.py. Of course you have to be careful to populate them in life.py before you try to use them,
删除一些系统路径黑客后,我认为添加
我的首选解决方案可能很有价值。
注意:这是一个框架挑战 - 没有必要在代码中进行。
假设一棵树,
其中
test.py
包含使用路径执行仅包含包目录
但使用模块参数包含项目目录
现在,所有导入都可以是绝对的,从项目目录。 不需要进一步的诈骗。
After removing some sys path hacks, I thought it might be valuable to add
My preferred solution.
Note: this is a frame challenge - it's not necessary to do in-code.
Assuming a tree,
Where
test.py
containsExecuting using the path only includes the package directory
But using the module argument includes the project directory
Now, all imports can be absolute, from the project directory. No further skullduggery required.
使用pathlib.Path
Using pathlib.Path
在开发新模块时,我经常遇到这个问题,但采用了将目录附加到 os.path 的解决方案,所以我所做的就是添加到 every
__init__.py 在我的项目中:
其中“p”是我的项目文件夹,例如:
c:/p/
当向其他人开放项目时,此解决方案不会对您有帮助,您将不得不(像我一样)找到另一个解决方案(当我完成关于我如何从这里开始的代码时,我可能会将其发布在这里),但至少这将帮助您更快地开始原型设计。
我知道这个解决方案已经被讨论过,我只是想分享我的复制粘贴解决方案。
我的结构是这样的:
在所有地方都有一个
__init__.py
并不能帮助我从 p/project/tests/tests.py 运行我的tests.py我现在尝试了很多解决方案,但大多数其中一些让我害怕,或者根本不适合我。 为什么 python 这么复杂? 我选择的解决方案也不能让我满意。
i struggled with this a lot when developing a new module, but went with the solution to append the directories to os.path, so what i did was to add to every
__init__.py
in my project this:where "p" is my project folder, like:
c:/p/
this solution won't help you when opening the project to others and you will have to (like me) find another solution (i probably will post it here when i'm done with my code on how i went from here) but at least this will help you start into prototyping faster.
i know this solution has been discussed, i just wanted to share my copy-and-paste solution.
My structure was this:
having an
__init__.py
in all places did not help me running my tests.py from p/project/tests/tests.pyi tried a lot of solutions by now, but most of them scared me or simply didnt work for me. Why is this so complicated with python? The solution i chose did not satisfy me either.
首先是代码:
根据文档,
因此,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)
将(大)父目录放在最后作为后备。First the code:
Per the docs,
Hence,
sys.path
is a list like["script/directory/path", "pythonpath/element/1", "pythonpath/element/2", ..., ".../lib/python3.x", ..., ".../lib/python3.x/site-packages"]
The path to the script directory is always in position 0 of that list. Since OP has the module (
nib.py
) that he wants to import two directories up, he can get its directory withPath(sys.path[0].parent.parent)
.sys.path
only works with strings, so the result must be wrapped instr()
.The next step is to insert this path into the correct location in
sys.path
. The location only matters if multiple modules with the same name can be found anywhere in thesys.path
directories. There are three sensible choices for this:.insert(0, ptdraft)
inserts at the start of the list, hence the highest priority..insert(1, ptdraft)
puts the (grand)parent directory after the script directory and before the directories on thePYTHONPATH
..append(ptdraft)
puts the (grand)parent directory at the very end as a fall-back.这是对我有用的最简单的解决方案:
This is the simplest solution that works for me: