Deep Dive into Python Packages
What are Packages? - Lecture
-
Packages are specilaized modules
- they can contains modules
- packages can contains packages (sub-packages)
- If a module is a package, it must have a value set for
__path__
- Both modules and packages do not have to be entities in the file system, but typically they are.
-
Packages represent a hierarchy of modules / packages (Modules → packages - > packages)
-
Import nested packages
-
Let’s focus on (File based) packages
- A package is simply a module tha can contain other modules/packages
- On file system we therefore have to use directories for packages, and modules are the files
- Directory name => Package name => the Code of the Package (
__init__.py
inside the directory) - Module file name =>
module_name.py
- To define a package in our file system, we must
- Create a directory whose name will be the package name
- Create a file call
__init__.py
inside that directory => tells Pythons that the directory is a package
-
While a file based package get imported
-
Nested package
-
Three Dunder Properties (
__file__
,__package__
) (__path__
- Package only property)- For Modules
__file__
=> the location of module code in the file system (include the module file name)__package__
=> the package that the module code is located in__path__
=> an empty string if the module is located in the application root => ‘’
- For Packages
__file__
=> the location of__init__.py
in the file system (include the file name)__package__
=> package name__path__
=> the location of package(directory) in the file system => Fully Qualiified Path
- For Modules
-
The package hierarchy - an example and the dunder properties
-
sub-package and the dunder properties
-
More examples
What are Packages? - Coding
-
import pack1.pack1_1
- sys.modules
- ‘pack1.pack1_1’ in sys.modules => \color{tomato}{True}
- ‘pack1_1’ in sys.modules => False
- ‘pack1’ in sys.modules => False
- globals()
- ‘pack1’ in globals() => \color{tomato}{True}
- ‘pack1_1’ in globals() => False
- ‘pack1.pack1_1’ in globals() => False
- pack1.pack1_1.value => ‘pack1_1 value’
- pack1.value => ‘pack1 value’
- sys.modules
-
from pack1 import pack1_1
=> Put the symbol directly into global namespace- ‘pack1_1’ in globals() => \color{tomato}{True}
- id(pack1_1) == id(sys.modules[‘pack1.pack1_1’]) => \color{tomato}{True}
-
import pack1.pack1_1.module1_1a
- sys.modules
- ‘pack1’ in sys.modules() => True
- ‘pack1.pack1_1’ in sys.modules() => True
- ‘pack1.pack1_1.module1_1a’ in sys.modules() => True
- globals()
- ‘pack1’ in globals() => \color{tomato}{True}
- ‘pack1.pack1_1’ in globals() => False
- ‘pack1.pack1_1.module1_1a’ in globals() => False
- pack1.pack1_1.module1_1a.value => ‘module1_1a value’
- sys.modules
Why Packages?
-
Code organization , ease of use
-
Example: Single file(All-in-one) → Structured Packages
-
Using
__init__.py
-
Why Packages?
The benefits for the developers
-
模組化
- 重用性:模組化讓你將功能拆分成多個小的、獨立的部件,每個部件專注於特定的功能。這樣可以在不同的項目中重複使用相同的程式碼,而不需要重新編寫。
- 可維護性:將程式碼分解為較小的模組可以使每個模組更易於理解和修改。當你需要修改某個功能時,可以專注於相關的模組,而不是整個程式庫。
-
版本控制
- 版本管理:每個 package 可以有自己的版本號碼,這樣開發者可以追蹤功能變更和修復,並控制不同版本之間的兼容性。
- 兼容性:package 管理工具(如 pip)會確保安裝的 package 與其他 package 兼容。這有助於避免因為 package 版本不兼容而導致的問題。
-
測試
- 單元測試:開發者可以為 package 的各個模組編寫單元測試,這樣可以及早發現和修復錯誤,保證每個模組的功能正確。
- 持續集成:持續集成工具可以自動化測試和部署過程,確保每次程式碼更改後 package 都能正常運行。
-
部署與協同合作
- 容易部署:將 package 上傳到 PyPI 等公共平台後,其他開發者可以輕鬆地通過 pip 安裝這些 package,簡化了部署的過程。
- 協同開發:通過版本控制系統(如 Git)來管理 package 的源代碼,可以方便地進行協作開發,合併程式碼和處理版本控制問題。
-
說明文件
- 內建說明文件:可以在 package 中包括說明文件,例如使用 Sphinx 生成的 API 文檔,這有助於使用者理解如何使用 package 提供的功能。
The benefits for the users
-
簡化安裝
- 相依管理:通過 pip 等工具,使用者可以輕鬆安裝 package (含其相依性),避免手動配置和安裝的麻煩。
- 一致性:確保安裝的程式庫版本與開發者測試的版本一致,這有助於避免環境配置的不一致問題。
-
提高效率
- 即開即用:許多程式庫提供了開箱即用的功能,使用者無需從頭開始編寫程式碼,只需調用 package 提供的 API 即可。
- 功能封裝:將複雜的功能封裝在 package 中,使用者只需要調用簡單的接口,即可完成複雜的操作。
-
說明文件和範例
- 學習資源:大多數 package 都會附帶詳細的文檔和使用範例,幫助使用者快速理解如何使用 package 提供的功能。
- 示範程式:提供示範程式幫助用戶理解 package 的使用方法,通常在檔案中包含示範程式碼,可以加快上手速度。
Structuring Packages - Part 1 & 2
-
Nest packages => How we can roll things up into a nicer namespace infrastructure for the users of our package
-
root
-
2nd common(package)
-
3rd validators(package)
-
4th numeric(module)
-
The whole idea is that we want to
- Hide the implementation details (users don’t need to know how our code is structured)
- Independent of the name of the package name (e.g. make it easier to rename
common
toshared
) - Flatten the structure out and make the entire model a little simpler to understand
-
The technique we use to accomplish this
-
Using relative import
-
Controlling what gets imported when someone does an
import *
-
1st method( \color{tomato}{selected}) - use
__all__
= [‘function1’, ‘function2’]
-
2nd method - If Python sees an underscore infront of a function, it is not going to put it into the namespace.
-
-
-
About
__init__.py
, it should be used for importing other modules and subpackages, not for defining functions, classes, etc. -
the built-in asyncio package
-
the buit-in email package (with some functions in the
__init__.py
file)
Namespace Packages
-
What are Implicit namespace packages
-
Regular vs. Namespace Packages
-
an example
-
Conclusion (About namespace package)
- You can spread namespace packages over different parts of your disk, even into zip files
- You can’t manipulate and flatten the namespace the way we did with the regular packages
- Read
PEP 420
for more details
Importing from Zip Archives
- import sys
- sys.path.append(r’.\common.zip’)