【问题标题】:Why does __init__.py import all files in the folder as modules? [duplicate]为什么 __init__.py 将文件夹中的所有文件作为模块导入? [复制]
【发布时间】:2020-01-26 04:39:56
【问题描述】:

在编写 Python 包时,我遇到了一些奇怪的行为。这是我的目录布局:

my_package/
    __init.py__
    client.py
    events.py
    utils.py

在__init__.py 中有一些from ... 导入可以导入各种名称:

__all__ = ['Client', 'get_websocket_url', 'format_cookies']

from .client import ChatSession as Client
from .utils import get_websocket_url, format_cookies

当我导入my_package 并运行dir(my_package) 时,似乎client.py、events.py 和utils.py 都包含为属性:

['Client', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'client', 'events', 'format_cookies', 'get_websocket_url', 'utils']

发生这种情况的原因是什么?我可以通过deleting 在__init__.py 中删除这些属性,但这看起来不太好。导入任何内容的唯一其他文件是client.py:

from .events import Event
from .utils import get_websocket_url

...

但这并没有将任何模块作为一个整体导入,所以这让我很困惑。有什么想法吗?

【问题讨论】:

  • 导入模块后,它成为其包的属性。这是预期和指定的行为。您对此的实际问题是什么?
  • @KlausD。好吧,我希望能够阻止将三个 src 文件作为属性添加到模块中。似乎大多数软件包都没有表现出这样的行为 - 我做的事情是否不正确?也请原谅我,因为我对包装系统还很陌生。
  • 好吧,如果您不希望模块包含在包中,那么您不要将源文件放在那里。为什么要在包中包含模块而不在包中?
  • 那么,如何在不作为属性的情况下将它们与它们一起导入?导入语法将它们合二为一,即使这在相对导入中更难看到,并且如果尚未导入,则将在导入时创建属性。
  • 因为这就是 Python 的工作方式,它是如何指定和记录的。他们(?)仍然没有解释:这会导致什么问题? Stack Overflow 不是抱怨 Python 语言设计决策的好地方。

标签: python python-3.x class package


【解决方案1】:

__init__.py为什么要将文件夹中的所有文件都导入为模块?

它没有。您必须显式导入它们。在包目录中添加一个空的other.py 文件,您将不会看到它根本没有被导入,除非您还添加了触发子模块导入的代码。有关更多信息,请参阅Do I need to import submodules directly?。

您可能想问类似“为什么子模块导入总是绑定父模块中的名称?”之类的问题。作为问题标题。好吧,这就是 Python 中的导入系统的工作原理。它记录在5.4.2. Submodules:

当使用任何机制(例如importlib API、import 或import-from 语句或内置__import__())加载子模块时,将在父模块的命名空间中放置与子模块对象的绑定。例如,如果包spam有一个子模块foo,则导入spam.foo后,spam会有一个属性foo绑定到子模块。

为了让您更好地了解为什么这实际上是一种明智的行为,请考虑导入父模块 (my_module) 在 sys.modules dict 中创建一个项目,键为 "my_module"(字符串),值为my_module(模块)。随后,客户端的子模块导入也会在sys.modules 中创建一个项,键为"my_module.client"(字符串),值为my_module.client(模块)。由于my_module 和my_module.client 现在都缓存在sys.modules 中,因此将client 作为属性附加到my_module 上是有意义的。

如果您出于某种原因不喜欢子模块名称绑定,您可以通过在 import-from 语句之后放置 del 语句从父模块命名空间中删除这些子模块名称。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-01-13
    • 2018-09-09
    • 1970-01-01
    • 1970-01-01
    • 2015-03-12
    • 1970-01-01
    • 1970-01-01
    • 2021-07-10
    相关资源
    最近更新 更多