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

问题描述 投票:6回答:3

我有一个多子模块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,并尝试通过在submodule1下的submodule2中将以下代码放入__init__.py中调用submodule2

import submodule1

def main():
    submodule1.main()

if __name__ == '__main__':
    main()

消息失败了:

....in main submodule1.main()
    ModuleNotFoundError: No module named 'hello'

我认为hello不应该暴露在外面,但我怎么能让我的submodule1工作?我正在使用python3

========

setup.py是:

from setuptools import setup, find_packages

with open('requirements.txt') as f:
    requirements = f.read().splitlines()

__version__ = "0.0.2"

setup(
    version=__version__
    name = "mymodules",
    packages = find_packages(exclude=['tests', '*.tests', '*.tests.*']),
    install_requires=requirements,
)
python python-3.x
3个回答
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文件中的外部(在导入器中)导入您想要看到的所有名称。

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

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

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

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

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

第二编辑:

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

你试过了吗

from . import hello

submodule1进口包__init__.py,而不是import hello?那应该使用submodule1命名空间。

عرضعديت:

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

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

这引出了另一个观点:

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

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

要么,

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

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

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

你的包也会有一个入口点,或者是一个简单的导入点,或者是一个主挂钩来运行一个主函数,而不是每个“子模块”的几个可能的入口点。

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


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则命名


1
投票

最后,我弄清楚了。我不能放

if __name__ == '__main__':
    main()

__init__,它永远不会工作,这导致我尝试直接运行它并由ImportError: cannot import name 'hello'失败。当__init____main__时,它不能导入子缓冲区,但是如果从外部调用它可以工作,例如,从submodule2,submodule1.main()仍然可用。所以这个问题看起来不会对用户造成影响。感谢@progmatico,您的回答帮助我了解这些功能如何协同工作,非常感谢!

© www.soinside.com 2019 - 2024. All rights reserved.