mirror of
https://github.com/jackyzha0/quartz.git
synced 2025-12-27 23:04:05 -06:00
Add note
This commit is contained in:
parent
3171c193c4
commit
913661bebb
@ -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
|
||||
@ -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
|
||||
```
|
||||
Loading…
Reference in New Issue
Block a user