From 913661bebb285593473a11fb99ff08e3ec99102e Mon Sep 17 00:00:00 2001 From: PinkR1ver <3180102330@zju.edu.cn> Date: Mon, 18 Mar 2024 10:41:31 +0800 Subject: [PATCH] Add note --- .../python/package_in_python.md | 205 ++++++++++++++++++ .../python/python_namespace_pollution.md | 15 ++ 2 files changed, 220 insertions(+) create mode 100644 content/computer_sci/coding_knowledge/python/package_in_python.md create mode 100644 content/computer_sci/coding_knowledge/python/python_namespace_pollution.md diff --git a/content/computer_sci/coding_knowledge/python/package_in_python.md b/content/computer_sci/coding_knowledge/python/package_in_python.md new file mode 100644 index 000000000..881097547 --- /dev/null +++ b/content/computer_sci/coding_knowledge/python/package_in_python.md @@ -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 \ No newline at end of file diff --git a/content/computer_sci/coding_knowledge/python/python_namespace_pollution.md b/content/computer_sci/coding_knowledge/python/python_namespace_pollution.md new file mode 100644 index 000000000..8a074289e --- /dev/null +++ b/content/computer_sci/coding_knowledge/python/python_namespace_pollution.md @@ -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 +``` \ No newline at end of file