为什么我有时可以在不导入整个路径的情况下使用嵌套模块中的函数?

Why I can sometimes use functions from nested modules without importing the whole path?

我想知道这两种情况有什么区别?模块的内部结构是否有所不同?

为什么这个有效:

>>> import numpy
>>> numpy.random.RandomState
<class 'numpy.random.mtrand.RandomState'>

但是这个在我也导入嵌套模块之前不起作用:

>>> import tkinter
>>> tkinter.ttk.Spinbox
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'tkinter' has no attribute 'ttk'
>>> import tkinter.ttk
>>> tkinter.ttk.Spinbox
<class 'tkinter.ttk.Spinbox'>

我认为这一定与每个模块中的 __init__.py 文件有关,但具体的实现示例会有所帮助。

默认情况下,当您加载 "package"(实际上只是一个包含 __init__.py 文件的目录时,Python 仅加载 __init__.py 文件。

在该文件中,如果需要,您可以自动加载其他内容,例如模块(其他 .py 文件)甚至其他包(带有 __init__.py 的目录)

所以这些库不同的原因很可能是由于 __init__.py 文件的设置方式不同。

例如,我创建了一个包含单个模块的包。我有 __init__.py 文件导入的模块,因此它会自动加载。

这种导入行为可以通过在各自的 __init__.py 文件中导入深度 class 来实现。这是一个项目结构的快速演示,它的工作方式与真正的 numpytkinter 包非常相似:

.
├── __init__.py
├── main.py
├── numpy
│   ├── __init__.py
│   └── random
│       ├── __init__.py
│       └── mtrand.py
└── tkinter
    ├── __init__.py
    └── ttk.py

numpy/__init__.py:

from .random import RandomState

numpy/random/__init__.py:

from .mtrand import RandomState

numpy/random/mtrand.py:

class RandomState:
    pass

tkinter/__init__.py:


tkinter/ttk.py:

class Spinbox:
    pass

__init__.py:


main.py:

import numpy
import tkinter

print(numpy.random.RandomState)

try:
    print(tkinter.ttk.Spinbox)
except AttributeError:
    print("Caught an exception!")
    import tkinter.ttk
    print(tkinter.ttk.Spinbox)

Output:

<class 'numpy.random.mtrand.RandomState'>
Caught an exception!
<class 'tkinter.ttk.Spinbox'>