我知道有很多相似或相同的问题,但我仍然无法理解/找到适合我使用模块的正确方法。 Python是我最喜欢的语言,我喜欢其中的一切,除了使用导入:递归导入(当你尝试引用一个尚未存在的名称时),导入路径等。
所以,我有这种项目结构:
my_project/
package1/
__init__.py
module1
module2
package2/
__init__.py
module1
module2
Package1
可以用作独立单元,但也应由package2
导入。
我现在在做什么,例如,在package1.module1
我写from package1 import module2
,即使用导入模块的完整路径。我这样做是因为如果我使用import module2
- 当从另一个包(package2
)导入模块时,这将不起作用。我也无法使用from . import module2
- 这在直接运行module1
时无效。
好的,所以from package1 import module2
中的package1.module1
可以在两种情况下都有效(直接运行package1.module1
时和从package2
导入时)我在开头添加这些行package1.module1
:
import os, sys
currDir = os.path.dirname(os.path.realpath(__file__))
rootDir = os.path.abspath(os.path.join(currDir, '..'))
if rootDir not in sys.path: # add parent dir to paths
sys.path.append(rootDir)
对我而言,这是有效的,但我觉得这不是pythonic。我做错了吗?
相反,我应该始终从项目根运行package1.module1
吗?如果是这样,这使得从IDE运行它变得不方便 - 我需要以某种方式在其中设置路径。
更新:我尝试将文件root.pth
添加到package1
目录,其内容为..
。但它不起作用 - 我猜它是用于别的东西。
结论:
始终使用绝对导入:import package1.module1
将引导程序添加到根文件夹以将某些模块作为独立脚本启动。这解决了从IDE运行脚本并且是一种pythonic方法。
2007年4月22日,Brett Cannon写道:
此PEP将
if __name__ == "__main__": ...
成语更改为if __name__ == sys.main: ...
这样你至少有机会 在使用相对导入的包中执行模块。让这个PEP超越python-ideas。也停止了那里的讨论 正在提出许多新想法。 =)我列出了所有这些内容 拒绝的想法部分,虽然如果压倒性的支持一个 PEP可以转移到其中一个。
我在这个以及__main__
提出的任何其他提议中都是-1
机械。唯一的用例似乎是运行脚本
住在模块的目录中,我一直认为这是一个目录
反模式。为了让我改变主意,你必须让我相信
它不是。
答案 0 :(得分:26)
您的计划的切入点是什么?通常,程序的入口点将位于项目的根目录中。由于它位于根目录,因此根目录中的所有模块都是可导入的,只要其中包含__init__.py
文件。
所以,使用你的例子:
my_project/
main.py
package1/
__init__.py
module1
module2
package2/
__init__.py
module1
module2
main.py
将是您的计划的切入点。由于作为main执行的文件会自动放在PYTHONPATH上,因此可以从顶级导入中获取package1
和package2
。
# in main.py
from package1.module1 import *
from package1.module2 import *
# in package1.module1
import module2
from package2.module1 import *
# in package2.module1 import *
import module2
from package1.module1 import *
请注意,在上面,package1和package2相互依赖。绝不应该这样。但这只是能够从任何地方导入的一个例子。
main.py
也不一定是任何花哨的东西。它可以很简单:
# main.py
if __name__ == '__main__':
from package1.module1 import SomeClass
SomeClass().start()
我想说的是,如果某个模块需要其他模块可以访问,那么该模块应该可以作为顶级导入使用。模块不应该尝试将自己作为顶级导入(直接在PYTHONPATH上)。
项目的责任应该是确保如果模块直接包含在项目中,则可以满足所有导入。有两种方法可以做到这一点。第一种方法是在项目文件夹中创建一个引导文件,例如main.py
。另一种方法是创建一个文件,将所有相关路径添加到PYTHONPATH,由任何可能存在的入口点加载。
例如:
# setup.py
import sys
def load():
paths = ['/path1/','/path2/','/path3/']
for p in path:
sys.path.insert(0, p)
# entrypoint.py
from setup import load
load()
# continue with program
要带走的主要事情是,模块不应该将自己置于路径上。路径应由进入程序的入口点自动确定,或由知道所有相关模块所在位置的安装脚本明确定义。
答案 1 :(得分:5)
我通常将每个包创建为可安装包(即创建一个setup.py文件),然后使用pip将它们安装到virtualenv中,仅用于此项目。
如果仍在开发中,您甚至可以安装使用pip -e。
答案 2 :(得分:0)
我已经迟到了5年......但是只做export PYTHONPATH=/path1:/path2:
(注意尾随":") - 这样你的工作目录(从中运行python)将是在路上。