导入路径 - 正确的方法?

war*_*iuc 27 python path python-import

我知道有很多相似或相同的问题,但我仍然无法理解/找到适合我使用模块的正确方法.Python是我最喜欢的语言,我喜欢其中的一切,除了使用导入:递归导入(当你尝试引用一个尚未存在的名称时),导入路径等.

所以,我有这种项目结构:

my_project/
    package1/
        __init__.py
        module1
        module2
    package2/
        __init__.py
        module1
        module2
Run Code Online (Sandbox Code Playgroud)

Package1可以使用独立单元,但也可以通过它导入package2.我现在在做什么,例如,在package1.module1我写from package1 import module2,即使用导入模块的完整路径.我这样做是因为如果我使用import module2- 当从另一个包(package2)导入模块时,这将不起作用.我也无法使用from . import module2- module1直接运行时这不起作用.

好的,所以为了from package1 import module2package1.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)
Run Code Online (Sandbox Code Playgroud)

对我来说,这是有效的,但我觉得这不是pythonic.难道我做错了什么?

相反,我应该始终package1.module1从项目根运行吗?如果是这样,这使得从IDE运行它变得不方便 - 我需要以某种方式在其中设置路径.

更新:我试图添加一个文件root.pthpackage1与内容DIR ...但它不起作用 - 我猜它是用于别的东西.

结论:

  1. 始终使用绝对导入: import package1.module1

  2. 将引导程序添加到根文件夹以将某些模块作为独立脚本启动.这解决了从IDE运行脚本并且是一种pythonic方法.

2007年4月22日,Brett Cannon写道:

此PEP将更改if __name__ == "__main__": ...成语, if __name__ == sys.main: ... 以便您至少有机会在使用相对导入的包中执行模块.

这个PEP过去的python-ideas.当提出太多新想法时,停止了那里的讨论.=)我已经在Rejected Ideas部分中列出了所有这些内容,但是如果对一个人的压倒性支持出现,PEP可以转移到其中一个.

我在这个以及任何其他提议的__main__ 机械装置上都是-1 .唯一的用例似乎是运行脚本,这些脚本恰好位于模块的目录中,我一直将其视为反模式.为了让我改变主意,你必须让我相信它不是.

- Guido van Rossum

Jos*_*ton 26

您的计划的切入点是什么?通常,程序的入口点将位于项目的根目录中.由于它位于根目录,因此根目录中的所有模块都是可导入的,只要其中包含__init__.py文件即可.

所以,使用你的例子:

my_project/
    main.py
    package1/
        __init__.py
        module1
        module2
    package2/
        __init__.py
        module1
        module2
Run Code Online (Sandbox Code Playgroud)

main.py将是您的计划的切入点.因为作为主要执行的文件会被自动的PYTHONPATH,双方package1package2可以从顶层进口.

# 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 *
Run Code Online (Sandbox Code Playgroud)

请注意,在上面,package1和package2相互依赖.绝不应该这样.但这只是能够从任何地方导入的一个例子.

main.py也不必是任何花哨的东西.它可以很简单:

# main.py

if __name__ == '__main__':
    from package1.module1 import SomeClass
    SomeClass().start()
Run Code Online (Sandbox Code Playgroud)

我想说的是,如果一个模块需要其他模块可以访问,那么该模块应该可以作为顶级导入使用.模块不应该尝试将自己作为顶级导入(直接在PYTHONPATH上).

如果模块直接包含在项目中,则应确保所有进口都能得到满足,这应该是项目的责任.有两种方法可以做到这一点.第一种方法是创建一个bootstrapper文件,例如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
Run Code Online (Sandbox Code Playgroud)

要带走的主要事情是,模块应该放在路径上.路径应由进入程序的入口点自动确定,或由知道所有相关模块所​​在位置的安装脚本明确定义.

  • 我有两个入口点。有时我使用“ package1”作为独立程序。有时,“ package1”是从另一个入口点导入的。 (2认同)
  • 我的建议是使用一个bootstrapper文件,比如我在回答中提到的main.py.不要从项目的根目录以外的任何地方启动程序.另一种选择是项目中其他地方的引导程序,它将所有必需的模块添加到`sys.path`中.如果你有一个可供其他人使用的模块,那么ELSE负责确保该模块作为顶级导入使用是很重要的. (2认同)

Mat*_*kel 5

我通常将每个包创建为可安装的包(即创建一个setup.py文件),然后使用pip将它们安装到virtualenv中,仅用于此项目.

如果仍在开发中,您甚至可以安装使用pip -e.