【问题标题】:How can I run properly python project from different directory如何从不同的目录正确运行 python 项目
【发布时间】:2020-05-07 12:49:01
【问题描述】:

python 项目文件层次结构:

parent/
    __init__.py
    one/
        __init__.py
        bar.py
    two/
       __init__.py
       foo.py

foo.py

from one import bar

我尝试在其他目录(例如用户/用户)中从终端运行 foo.py,但出现下一个错误:

没有名为一个的模块

当我尝试运行 foo.py 时,我猜它正在尝试从执行代码的目录中导入文件,我尝试了很多方法,但我找不到解决方案,最后我找到了解决方案,这个解决方案的问题是解决方案不够优雅,希望有一个优雅更好的解决方案。

foo.py

from pathlib import Path
import sys

sys.path.append(str(Path(__file__).parent.parent))
sys.path.append("..")

from one import bar
  • 此解决方案并不优雅,因为它阻止我将所有导入内容放在页面开头。

【问题讨论】:

    标签: python import directory


    【解决方案1】:

    您在parent 目录中有一个__init.py__ 的事实表明parent 是您的包结构的一部分,并且 父目录可能是,应该 在 PATH 中。因此,您的导入实际上应该是:

    from parent.one import bar
    

    对于应用程序目录结构来说,只有一个根目录会很有用。然后可以使用该单个根包中的__init.py__ 从子包中加载模块,但这当然不是必需的。如果这不是您的意图,那么您可能应该删除 parent 中的 __init__.py,因为它没有任何用途(并且令人困惑)并确保目录 parent 在您的 PATH 中。

    但是:只要您运行程序时所在的当前目录是包结构的根目录的父目录,Python 应该能够找到您的包您无需执行任何特殊操作,因为当前目录会自动添加到路径中。如果不方便,可以设置环境变量PYTHONPATH

    因此,根据哪些目录是包结构的一部分来确定是否应该更改导入语句。然后,您应该安排 Python 通过将当前目录、PYTHONPATH 或 sys.path 设置为所需目录来查找您的包——但只执行一次。 如果您必须设置sys.path,我会在您的主程序启动时执行此操作,然后才需要包含任何内容:

    如果foo.py 是你的主程序,那么在程序的顶部我会有:

    if __name__ == '__main__':
        from pathlib import Path
        import sys
    
        # if your import statement is: from parent.one import bar, then:
        sys.path.insert(0, str(Path(__file__).parent.parent))
        """
        # if your import statement is: from one import bar, then:
        sys.path.insert(0, str(Path(__file__).parent))
        """
    

    【讨论】:

      【解决方案2】:

      为什么不通过创建路径字典让父级充当子级的路径提供者?像这样:

      class parent:
      ...
          def createPathDict(self):
              self.path_dict = {}
              self.path_dict ['parent'] = self.parentPath
              self.path_dict ['one'] = os.path.join(self.parentPath, 'one')
              self.path_dict ['two'] = os.path.join(self.parentPath, 'two')
              # self.path_dict ['three'] = …
              # ...
      

      从孩子“二”你导入字典是这样的(我假设你使用类):

      class foo:
      
          def __init__(self, parent):
                self.parent = parent
      
          def addPathsToPythDirs(self):
               sys.path.insert(1, self.parent.path_dict ['one'])  # better
               # sys.path.insert(0, self.parent.path_dict [key])  
      ...
      

      这样你就可以把你的导入保存在 foo.py 中

      Why use sys.path.append(path) instead of sys.path.insert(1, path)?

      【讨论】:

      • 您评论中的链接似乎解决了使用sys.path.insert 处理多个环境的问题,而不是更合适的virtualenv 方法。此外,Ulf Rompe 说 sys.path.insert(1, path)sys.path.insert(0, path) 更受欢迎,只是因为“因为第 3 方代码可能依赖于 sys.path 文档”,即条目 0 将包含脚本所在的目录或 '',这表示当前目录。但我们在这里不处理 3rd 方代码。并且没有理由首先搜索这两个目录中的任何一个。
      • 此外,只有一个目录应该添加到路径中,即正确的目录。如果 OP 正在编码 from one import bar,则它是 parent;如果 OP 正在编码 from parent.one import bar,则它是 parent 的父级。没有理由将one 添加到路径中。
      猜你喜欢
      • 1970-01-01
      • 2012-05-03
      • 2011-04-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-13
      相关资源
      最近更新 更多