This commit is contained in:
PinkR1ver 2024-03-18 10:41:31 +08:00
parent 3171c193c4
commit 913661bebb
2 changed files with 220 additions and 0 deletions

View File

@ -0,0 +1,205 @@
---
title: Package, Module in Python
tags:
- python
- coding-language
date: 2024-03-18
---
# File Structure Example
```
sound/ Top-level package
__init__.py Initialize the sound package
formats/ Subpackage for file format conversions
__init__.py
wavread.py
wavwrite.py
aiffread.py
aiffwrite.py
auread.py
auwrite.py
...
effects/ Subpackage for sound effects
__init__.py
echo.py
surround.py
reverse.py
...
filters/ Subpackage for filters
__init__.py
equalizer.py
vocoder.py
karaoke.py
...
```
# How to use
## Example
```python
# Use Method 1
import sound.effects.echo
# Use Method 2
from sound.effects import echo
# Use Method 3
from sound.effects.echo import echofilter
```
> [!note]
> 注意,使用 `from package import item` item 可以是包的子模块(或子包),也可以是包中定义的函数、类或变量等其他名称。`import` 语句首先测试包中是否定义了 item如果未在包中定义则假定 item 是模块,并尝试加载。如果找不到 item则触发 [`ImportError`]异常。
## Notice
### import *
使用 `from sound.effects import *` 时会发生什么?你可能希望它会查找并导入包的所有子模块,但事实并非如此。因为这将花费很长的时间,**并且可能会产生你不想要的副作用**。
**解决办法是提供包的显式索引**
import语句使用如下惯例如果包的 `__init__.py` 代码定义了列表 `__all__`,运行 `from package import *` 时,它就是被导入的模块名列表。发布包的新版本时,包的作者应更新此列表。如果包的作者认为没有必要在包中执行导入 * 操作,也可以不提供此列表。
Example:
```python
__all__ = [
"echo", # refers to the 'echo.py' file
"surround", # refers to the 'surround.py' file
"reverse", # !!! refers to the 'reverse' function now !!!
]
def reverse(msg: str): # <-- this name shadows the 'reverse.py' submodule
return msg[::-1] # in the case of a 'from sound.effects import *'
```
同时有语法可以shadow submodule如上述所示**因为本地定义的reverse函数会遮挡子模块**。
> [!note]
> 如果没有定义 `__all__``from sound.effects import *` 语句 _不会_ 把包 `sound.effects` 中的所有子模块都导入到当前命名空间;**它只是确保包 `sound.effects` 已被导入**(可能还会运行 `__init__.py` 中的任何初始化代码),然后再导入包中定义的任何名称。 这包括由 `__init__.py` 定义的任何名称(以及显式加载的子模块)。 它还包括先前import语句显式加载的包里的任何子模块。 请看以下代码:
### 相对导入
```python
from . import echo
from .. import formats
from ..filters import equalizer
```
这些导入使用前导点号来表示相对导入所涉及的当前包和上级包
> [!abstract]
> 相对导入基于当前模块名。因为主模块名永远是 `"__main__"` ,所以如果计划将一个模块用作 Python 应用程序的主模块,那么该模块内的导入语句必须始终使用绝对导入。
# Template Example
## Complex one
```plaintext
package_name/
├── package_name/
│ ├── __init__.py
│ └── module_name.py
├── tests/
│ ├── __init__.py
│ └── test_module_name.py
├── .gitignore
├── LICENSE
├── README.md
├── requirements.txt
├── setup.py
└── pyproject.toml
```
- `package_name/`:包的顶层目录。
- `package_name/__init__.py`空文件告诉Python这个目录应该被视为一个包。
- `package_name/module_name.py`:代码所在。
- `tests/`:测试代码。
- `.gitignore`这个文件告诉git哪些文件不应该被版本控制系统跟踪。
- `LICENSE`:包的许可证文件。
- `README.md`:描述包的功能和使用方法。
- `requirements.txt`:列出包的依赖项。
- `setup.py`:这是一个构建和安装包的脚本。
- `pyproject.toml`这是一个新的Python包配置文件用于存储包的元数据和构建信息。
## Easy one
```plaintext
my_package/
├── __init__.py
└── module.py
```
### How to code `__init__`
1. 空的 `__init__.py` 文件:
```python
# 这个 __init__.py 文件是空的
```
2. 在 `__init__.py` 文件中导入包内模块,以便外部直接导入:
```python
from .module1 import *
from .module2 import some_function
```
3. 在 `__init__.py` 文件中设置 `__all__` 变量,控制使用 `from package import *` 时哪些符号会被导入:
```python
__all__ = ['module1', 'module2', 'some_function']
```
4. 在 `__init__.py` 文件中执行包的初始化代码,如配置日志等:
```python
import logging
logging.basicConfig(level=logging.INFO)
```
#### Notice
> [!hint]
> 在Python包的结构中`__init__.py` 文件起着一个重要的作用,它定义了包的属性和方法。当您从一个包中导入模块时,`__init__.py` 文件会被自动执行。因此,您可以在这个文件中指定需要导出的模块,这样外部就可以直接导入这些模块,而不需要知道它们具体在包的哪个位置。
>
>
```python
from .module1 import *
from .module2 import some_function
```
这里的 `.` 表示当前包目录。`from .module1 import *` 表示从当前包目录下的 `module1.py` 文件中导入所有函数和类。`from .module2 import some_function` 表示仅从 `module2.py` 文件中导入 `some_function` 函数。
这样做的好处是,当其他人使用这个包时,他们可以更方便地访问这些函数和类,而不需要知道具体的模块路径。例如,他们可以这样导入:
```python
from my_package import some_function
```
而不是:
```python
from my_package.module2 import some_function
```
这样可以使得包的结构对用户更加透明,用户只需要关注他们需要的功能,而不是包的内部结构。这也有助于在不影响用户的情况下重构包的内部结构。当然,这种方式也需要注意不要导出太多不必要的内部细节,以免造成[命名空间的污染](computer_sci/coding_knowledge/python/python_namespace_pollution.md)。
# Reference
* https://docs.python.org/zh-cn/3/tutorial/modules.html

View File

@ -0,0 +1,15 @@
---
title: Python Namespace Pollution
tags:
- python
- coding-language
date: 2024-03-18
---
在Python中命名空间污染指的是当从不同的模块导入变量或函数时如果不小心可能会覆盖掉已有的变量或函数。这通常发生在使用`from module import *`这样的语句时,因为它会将所有公开的变量和函数导入当前的命名空间。如果两个模块有重名的函数或变量,最后导入的会覆盖之前的,这就是所谓的“污染”。
为了避免这种情况,推荐的做法是只导入需要的特定函数或变量,或者**使用别名来保持命名空间的清晰**。
```python
import module as mod
from module import function as func_alias
```