我正在运行Python 2.5。
这是我的文件夹树:
ptdraft/
nib.py
simulations/
life/
life.py
(我还在__init__.py
每个文件夹中,为便于阅读,在此省略)
如何nib
从模块内部导入life
模块?我希望无需修补sys.path就可以做到。
注意:正在运行的主模块在ptdraft
文件夹中。
__init__.py
。S.Lott:我不知道该怎么检查...
我正在运行Python 2.5。
这是我的文件夹树:
ptdraft/
nib.py
simulations/
life/
life.py
(我还在__init__.py
每个文件夹中,为便于阅读,在此省略)
如何nib
从模块内部导入life
模块?我希望无需修补sys.path就可以做到。
注意:正在运行的主模块在ptdraft
文件夹中。
__init__.py
。S.Lott:我不知道该怎么检查...
Answers:
看来问题与该模块位于父目录或类似目录中无关。
您需要将包含的目录添加ptdraft
到PYTHONPATH
您说过import nib
与您合作,这可能意味着您将ptdraft
自身(而不是其父项)添加到了PYTHONPATH中。
import sys
然后sys.path.append("..\<parent_folder>")
ValueError: Attempted relative import in non-package
__init__.py
文件。
__init__.py
是不是你所要做的唯一事情:stackoverflow.com/questions/11536764/...
相对导入(如中的from .. import mymodule
)仅在包中起作用。要导入当前模块的父目录中的“ mymodule”:
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__
属性并不总是给定的。os.path.abspath(__file__)
我现在建议不要使用Inspect模块来检索当前文件的文件名(和路径),而不要使用它
sys.path.insert(1, os.path.join(sys.path[0], '..'))
parentdir
,但是在中已经指定的路径之一中sys.path
,有另一个名为“ mymodule”的模块。插入parentdir
作为sys.path
列表的第一个元素可确保从中parentdir
导入模块。
sys.path.insert(1, os.path.realpath(os.path.pardir))
也可以。
对于同级软件包的导入问题,我也发表了类似的答案。你可以在这里看到它。
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
都可以。这只是一个最小的工作示例。
如果您熟悉虚拟环境,请激活一个,然后跳到下一步。虚拟环境的使用不是绝对必需的,但从长远来看(当您正在进行多个项目时),它们确实可以帮助您。最基本的步骤是(在根文件夹中运行)
python -m venv venv
. /venv/bin/activate
(Linux)或./venv/Scripts/activate
(Win)要了解更多有关此的信息,只需在Google上搜索“ 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 party模块)发生名称冲突。
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!
sys.path
存在,最终用户通常会将代码安装在该站点中。这比sys.path
黑客更好(并且您可以在路径黑客的类别中包括使用PYTHONPATH),因为这意味着开发中的代码对您的行为应与对其他用户的行为相同。相反,在使用路径修改时,将以不同的方式解析import语句。
如果无法将模块文件夹添加到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
/path/to/ptdraft
则必须进行编辑。有一些解决方案可以计算出文件的当前目录,也可以通过这种方式从父文件夹中将其导入。
对python 2不太了解。
在python 3中,可以按以下方式添加父文件夹:
import sys
sys.path.append('..')
...然后可以从中导入模块
'..'
指向包含相关模块的目录时,此方法才有效。
这是一个简单的答案,因此您可以了解它的工作原理(小型和跨平台)。
它仅使用内置模块(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
,在(第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__
变量来获取正在运行的
代码的文件路径/文件名,因为此处的用户经常将其描述为不可靠的。您不应将其
用于从其他人使用的程序中从父文件夹导入模块。
一些不起作用的示例(引用此 Stack Overflow问题):
py2exe
没有__file__
属性,但是有一种解决方法- 当您从IDLE运行时,
execute()
没有__file__
属性- 我得到的OS X 10.6
NameError: global name '__file__' is not defined
sys.path.pop(0)
在导入所需模块后立即删除了新路径条目 。但是,随后的进口仍运往该地点。例如,我有app/config
,app/tools
和app/submodule/config
。从中submodule
,我插入app/
导入tools
,然后删除app/
并尝试导入config
,但是我得到app/config
了app/submodule/config
。
tools
的导入之一也是config
从父目录导入的。所以当我以后尝试在sys.path.pop(0); import config
室内做submodule
,期望得到时 app/submodule/config
,我实际上是在得到app/config
。显然,Python返回具有相同名称的模块的缓存版本,而不是实际检查sys.path
与该名称匹配的模块的。sys.path
在这种情况下,正确地进行了更改,由于已经加载了相同名称的模块,Python并未对其进行检查。
os.path.realpath(getsourcefile(lambda:0) + "/../..")
。将获得当前源文件,并附加两个父目录符号。我没有使用os.path.sep
as getsourcefile
,/
即使在Windows上也使用返回字符串。realpath
将负责从完整路径(在本例中为文件名,然后是当前目录)中弹出两个目录条目,从而给出父目录。
这是更通用的解决方案,其中将父目录包含在sys.path中(对我有用):
import os.path, sys
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))
import os, sys\n sys.path.insert(0,os.path.pardir)
同样的事情,但排序:)(注释中没有换行)
os.path.pardir
话,将不会获得父母的sys.path.insert()
__file__
变量可能不可靠(并不总是完整的文件路径,不适用于所有操作系统等)。将答案更改为不包括答案,将减少问题,并且更加兼容。有关更多信息,请参见stackoverflow.com/a/33532002/3787376。
我发现以下方法可用于从脚本的父目录导入包。在示例中,我想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)
sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
sys.path
通常根本不建议进行修改。你需要某种方式(例如,PYTHONPATH
)你的库是可访问的其他代码(或者它不是一个真正的图书馆); 一直使用它!
上述解决方案也很好。解决此问题的另一种方法是
如果要从顶层目录导入任何内容。然后,
from ...module_name import *
另外,如果要从父目录导入任何模块。然后,
from ..module_name import *
另外,如果要从父目录导入任何模块。然后,
from ...module_name.another_module import *
这样,您可以根据需要导入任何特定方法。
sys.path.append
上面的技巧不同,因此我无法像这样导入我的库。这是我在开始使用Google之前首先尝试做的事情:)好的,就是这个ValueError: attempted relative import beyond top-level package
对我来说,访问父目录最短和最喜欢的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系统环境变量。
与过去的答案相同的风格-但行数较少:P
import os,sys
parentdir = os.path.dirname(__file__)
sys.path.insert(0,parentdir)
文件返回您正在工作的位置
在Linux系统中,您可以创建一个从“ life”文件夹到nib.py文件的软链接。然后,您可以像这样简单地导入它:
import nib