我正在运行 Python 2.5。
这是我的文件夹树:
ptdraft/
nib.py
simulations/
life/
life.py
(我__init__.py
在每个文件夹中也有,为了便于阅读,这里省略了)
如何nib
从模块内部导入life
模块?我希望无需修改 sys.path 就可以做到。
注意:正在运行的主模块位于ptdraft
文件夹中。
我正在运行 Python 2.5。
这是我的文件夹树:
ptdraft/
nib.py
simulations/
life/
life.py
(我__init__.py
在每个文件夹中也有,为了便于阅读,这里省略了)
如何nib
从模块内部导入life
模块?我希望无需修改 sys.path 就可以做到。
注意:正在运行的主模块位于ptdraft
文件夹中。
相对导入(如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__)
我现在建议使用检查模块来检索当前文件的文件名(和路径)
对于从同级包导入的问题,我也发布了类似的答案。你可以在这里看到它。
sys.path
黑客的解决方案packaged_stuff
)setup.py
使用setuptools.setup()的脚本。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
.
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)要了解更多信息,只需谷歌搜索“python virtualenv 教程”或类似内容。除了创建、激活和停用之外,您可能永远不需要任何其他命令。
一旦你创建并激活了一个虚拟环境,你的控制台应该在括号中给出虚拟环境的名称
PS C:\tmp\test_imports> python -m venv venv
PS C:\tmp\test_imports> .\venv\Scripts\activate
(venv) PS C:\tmp\test_imports>
myproject
使用pip
. _ 诀窍是-e
在安装时使用标志。这样它以可编辑状态安装,对 .py 文件所做的所有编辑都将自动包含在安装的包中。
在根目录下,运行
pip install -e .
(注意点,它代表“当前目录”)
您还可以看到它是通过使用安装的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
mainfolder
在每次导入之前导入在此示例中,mainfolder
将是ptdraft
。这样做的好处是您不会遇到与其他模块名称(来自 python 标准库或 3rd 方模块)的名称冲突。
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()
(venv) PS C:\tmp\test_imports> python .\ptdraft\simulations\life\life.py
I am the return value from function_from_nib!
似乎问题与位于父目录或类似目录中的模块无关。
您需要将包含的目录添加ptdraft
到 PYTHONPATH
您说这import nib
与您合作,这可能意味着您将ptdraft
自己(而不是其父级)添加到 PYTHONPATH。
您可以在sys.path中列出的“模块搜索路径”中使用取决于操作系统的路径。因此,您可以轻松添加父目录,如下所示
import sys
sys.path.insert(0,'..')
如果要添加父父目录,
sys.path.insert(0,'../..')
这适用于 python 2 和 3。
对python 2不太了解,
在python 3中可以添加父文件夹如下:
import sys
sys.path.append('..')
...然后可以从中导入模块
如果将模块文件夹添加到 PYTHONPATH 不起作用,您可以在 Python 解释器搜索要导入的模块的程序中修改sys.path列表, 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
这是一个简单的答案,因此您可以了解它是如何工作的,小型且跨平台。
它只使用内置模块(和) os
,因此应该适用
于任何操作系统(OS),因为 Python 就是为此而设计的。sys
inspect
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
,在(第 3 行)的开头
添加并删除第一行。
- 但是这会导入所有模块,因此可能需要更多时间、内存和资源。inspect.
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))
我的代码将文件路径添加到sys.path
python路径列表
,因为这允许 Python 从该文件夹导入模块。
在代码中导入模块后,当添加的文件夹具有与
稍后在程序
中导入的另一个模块同名的模块时,最好sys.path.pop(0)
在新行上运行。您需要删除导入之前添加的列表项,而不是其他路径。
如果您的程序不导入其他模块,则不删除文件路径是安全的,因为
在程序结束(或重新启动 Python shell)后,所做的任何编辑都会消失。sys.path
我的答案没有使用__file__
变量来获取运行代码的文件路径/文件名,
因为这里的用户经常将其描述为unreliable。您不应该使用它
来从其他人使用的程序 的父文件夹中导入模块。
它不起作用的一些示例(引用this Stack Overflow question):
py2exe
没有__file__
属性,但有一个解决方法- 当您从
execute()
没有__file__
属性的 IDLE 运行时- 我得到的 OS X 10.6
NameError: global name '__file__' is not defined
这是更通用的解决方案,将父目录包含在 sys.path 中(对我有用):
import os.path, sys
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))
pathlib 库(包含在 >= Python 3.4 中)使得将父目录的路径附加到 PYTHONPATH 变得非常简洁和直观:
import sys
from pathlib import Path
sys.path.append(str(Path('.').absolute().parent))
我发现以下方法适用于从脚本的父目录导入包。在示例中,我想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)
在 Jupyter Notebook 中(使用 Jupyter LAB 或 Jupyter Notebook 打开)
只要您在 Jupyter Notebook 中工作,这个简短的解决方案可能会很有用:
%cd ..
import nib
即使没有__init__.py
文件,它也可以工作。
我在 Linux 和 Windows 7 上使用 Anaconda3 对其进行了测试。
上面提到的解决方案也很好。这个问题的另一个解决方案是
如果您想从顶级目录导入任何内容。然后,
from ...module_name import *
此外,如果您想从父目录导入任何模块。然后,
from ..module_name import *
此外,如果您想从父目录导入任何模块。然后,
from ...module_name.another_module import *
这样,您可以根据需要导入任何特定方法。
对我来说,访问父目录的最短且我最喜欢的 oneliner 是:
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 系统环境变量。
import sys
sys.path.append('../')
虽然原作者可能不再寻找解决方案,但为了完整性,有一个简单的解决方案。将life.py作为一个模块运行,如下所示:
cd ptdraft
python -m simulations.life.life
这样,您可以从nib.py导入任何内容,因为ptdraft目录位于路径中。
与过去的答案相同的风格 - 但行数更少:P
import os,sys
parentdir = os.path.dirname(__file__)
sys.path.insert(0,parentdir)
文件返回您正在工作的位置
在 Linux 系统中,您可以创建从“life”文件夹到 nib.py 文件的软链接。然后,您可以像这样简单地导入它:
import nib
两行最简单的解决方案
import os, sys
sys.path.insert(0, os.getcwd())
如果 parent 是您的工作目录,并且您想从子脚本中调用另一个子模块。
您可以在任何脚本中从父目录导入所有子模块并将其执行为
python child_module1/child_script.py
我们的文件夹结构:
/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
毫无问题地运行脚本。
有用的网址:
__init__.py
用)与图书馆合作。创建一个名为 nib 的库,使用 setup.py 安装它,让它驻留在站点包中,您的问题就解决了。你不必把你做的所有东西都塞进一个包里。把它分解成碎片。
我遇到了一个问题,我必须导入一个 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')
,这样您的工作目录就不会更改。
在我看来,您实际上并不需要导入父模块。假设在 nib.py 中有 func1() 和 data1,需要在 life.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 中的 func1 和数据了。当然,在尝试使用它们之前,你必须小心在 life.py 中填充它们,
我制作了这个库来做到这一点。
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
虽然违反了所有规则,但我还是要提一下这种可能性:
您可以先将文件从父目录复制到子目录。接下来导入它并随后删除复制的文件:
例如在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 尝试使用相对导入/路径导入/读取其他文件时,可能会出现一些问题。
这对我来说可以从更高的文件夹中导入东西。
import os
os.chdir('..')