从外部调用时无法找到模块

时间:2018-02-20 16:40:30

标签: python python-3.x

我有一个多子模块python项目,例如“myproject”,下面有两个子模块“submodule1”和“submodule2”。

项目结构

Example1/
|-- submodule1/
|   |-- __init__.py
|   |-- hello.py
...
|-- submodule2/
|   |-- __init__.py
...

hello.py下的submodule1有内容:

import datetime

def greeting():
    print("hello world! - " + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))

submodule1 __init__.py的内容是:

def main():
    import hello
    hello.greeting()

if __name__ == '__main__':
    main()

我可以在此处导入hello.py并成功调用greeting()功能。

现在,我将submodule1作为软件包安装到python,然后创建submodule2,并尝试通过将以下代码放入submodule1submodule2来调用__init__.py {1}}下的{1}}:

submodule2

消息失败了:

import submodule1

def main():
    submodule1.main()

if __name__ == '__main__':
    main()

我认为....in main submodule1.main() ModuleNotFoundError: No module named 'hello' 不应该暴露在外面,但我怎么能让我的hello工作?我正在使用python3

======

setup.py是:

submodule1

3 个答案:

答案 0 :(得分:1)

使用

def main():
    from .submodule1 import hello 
    # or from Example1.submodule1 import hello
        hello.greeting()

if __name__ == '__main__':
    main()

找不到hello,因为它不在submodule2文件夹中。您必须为解释器提供包内的相对路径,或包中的绝对路径。这仅适用于from ...导入语法表单。

如果您有大量导入,之后想要更改您的包名称,则省略包名称会很有用。

编辑:

这是从Example1包中的代码的角度来看,如果你把它变成一个。也就是说,这是在一些子模块中的包Example1内部的代码可以引用其他Example1子模块内的代码的方式,其中子模块放置在不同的文件夹中,其内部还有__init__.py。这是内幕知识,但它是从包Example1内部使用的。当您在室外时,导入包Example1,您可以在Example1 __init__.py文件中导出您想要从外部(在导入器中)看到的所有名称。

所以我在这里看到两个不同的问题:

如何将代码从子模块引用到子模块,两者都在同一个包中(就像我说的那样),以及如何将我的包中的名称导出到导入我的包的外部人员(导入包中的名称{{ 1}}这样您就可以编写_init__.py甚至from MyPackage import Class1, Class2, other_name之类的内容 - 使用*列表来控制这些名称。

另外我不明白你为什么要给子模块调用子模块然后尝试将其中一个安装为一个独立的包并像这样使用它。它们不属于您的__all__包裹吗?如果他们是孤立的包裹,他们不应该推荐其他包裹......

这样说,这可能看起来令人困惑,实际上包不是最简单的解释概念,但它也不是那么困难。

请参阅一个干净的示例,我就如何将类组织为一个包并在包中使用它们(当然是内部知识)做了here。请注意Example1文件内容,因为它类似于您要在程序包外部提供名称的操作(除了可导入的名称之外没有内部知识)。

第二次编辑:

所以我跟踪了我和OP之间的一些混淆来源: 我在上面说的所有内容都适用于import packages,而OP正在考虑distribution packages。毫不奇怪,即使是词汇表也将这种混淆视为一种真正的可能性。

你试过吗

__init__.py

from . import hello 导入包submodule1中,而不是__init__.py?那应该使用import hello命名空间。

第3次编辑:

您的错误可能会发生,因为在设置导入后,使用sucess运行仍取决于您运行代码的方式(我的意思是从您导入整个内容的位置),这通常是包父文件夹。如果你尝试从内部点开始编码,尽管是正确的,它仍然可能会失败(抱歉不连贯)。

即使这可以通过绝对导入解决,但这需要Example1成为一个包,因为你有子模块之间的依赖关系(实际上是子包)。

这引出了另一个观点:

导入包可以包含嵌套的导入包(子包)以及模块。这使它适合任何项目。这意味着还有一个包含所有内容的外包。那是你的项目。制作该外包装的发布,即您的项目。

假设您说“子模块必须单独安装”。精细。假设它们是独立的,那就制作它们(它们已经是包)。如上所述单独释放它们。

或者,

对我来说,他们看起来很相关,看起来有些“子模块”依赖于其他人。

您还说“下面的子模块可以由不同的开发人员响应”。这不是借口。它看起来像一个通用的代码库,把它们全部放在版本控制中(你做了,不是你)。标记发布点中的文件,或将发布版本放在自己的分支中。把它全部整理成一个包。发布该软件包。

否则我认为你正在为混乱做准备。并且您正在尝试使用发行包来控制它。如何记录哪个版本适用于另一个子模块的版本。如果将不同子模块的源放在一起进行测试,它们将冲突(覆盖)系统中先前安装的子模块。

此外,您的软件包将有一个入口点,可以是简单的导入点,也可以是一些主挂钩来运行主函数,而不是每个“子模块”的几个可能的入口点。

我希望这有助于解决您的问题,而不是您的错误。

答案 1 :(得分:1)

对于Python3.5并假设您要安装包submodule1和submodule2,如果没有,请提及您打算如何使用它们:这就是项目结构的方式

    (2005env) python@python:~/2005env/Example1$ tree
.
├── requirements.txt
├── setup.py
├── submodule1
│   ├── hello.py
│   └── __init__.py
└── submodule2
    └── __init__.py

2 directories, 5 files

在两个子模块外部放置setup.py,它也可以放在单独的子模块中,单独安装它们,并对目录结构和安装进行一些更改。

(2005env) python@python:~/2005env/Example1$ pip install -e .
Obtaining file:///home/python/2005env/Example1
Installing collected packages: submodule1
  Running setup.py develop for submodule1
Successfully installed submodule1

现在从项目目录外的位置导入python3.5中的submodule2,即/ home / python,项目位于/ home / python / 2005env / Example1

(2005env) python@python:~$ python
Python 3.5.3 (default, Nov 23 2017, 11:34:05) 
[GCC 6.3.0 20170406] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import submodule2
>>> dir(submodule2)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'main', 'submodule1']
>>> submodule2.main()
Hello World! - 2018-02-25 09:03:32
>>> dir(submodule2.submodule1)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'greeting', 'hello', 'main']


Python 3.5.3 (default, Nov 23 2017, 11:34:05) 
[GCC 6.3.0 20170406] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from submodule1 import greeting
>>> greeting()
Hello World! - 2018-02-25 09:26:14

在submodule1的 init .py中,我导入了hello以及问候语,因此可以作为属性使用

(2005env) python@python:~/2005env/Example1$ cat submodule1/__init__.py 
from . import hello
from .hello import greeting


def main():
    hello.greeting()

if __name__ == '__main__':
    main()

setup.py与你的find_packages相同,但如果是submodule1而不是mymodules则命名

答案 2 :(得分:1)

最后,我想通了。我不能放

APPEND_SET
<{1>}中的

,它永远不会起作用,这导致我尝试直接运行它并由if __name__ == '__main__': main() 失败。当__init__ImportError: cannot import name 'hello'时,它无法导入子缓冲区,但是如果从外部调用,例如,从子模块2 __init__仍然可用,则它可以正常工作。所以这个问题现在看起来并没有对用户造成影响。感谢__main__,您的回答帮助我了解这些功能如何协同工作,非常感谢!