Python 小技巧: Namespace Package

Dboy Liao
5 min readOct 12, 2020

--

會注意到這個東西,起因於工作上用到許多 GCP 的 python 套件。像是 bigquery 跟 dataproc。

如果你看它們的安裝過程,會發現它們在 PyPI 上都是分開的 package,像是 google-cloud-bigquerygoogle-cloud-dataproc 。可是安裝之後,卻可以像這樣 import 它們:

from google.cloud import bigquery, dataproc_v1

咦? 我們沒有裝一個 package 叫 google 阿? 如果你把 site-packges 打開來看,你會發現 bigquerydataproc_v1 都被放在 google.cloud 這個 module 下面,彷彿是 google.cloud 的 submodule。如果你本身又有一些包 python package 的經驗,應該已經開始覺得事情不單純了。

setup.py

當初想到這個的時候,我第一時間就去把 google-cloud-bigquerysetup.py 翻出來看了:

其實細看後,也沒做什麼太黑魔法的事,不過是用了 namespace package:

https://packaging.python.org/guides/packaging-namespace-packages/

很久以前在 python language reference 就讀過 namespace package 這東西,但一直沒有想通這東西的使用情境,讀了 PEP 420 還是懞懞懂懂,一直到看到 GCP 的 python packages 才想通了用法。

理解源於實踐,讓我們模仿 google-cloud-bigquerysetup.py 來寫幾個 namespace package 看看。

譬如說我們想寫兩個 pakcage: dboy-package1dboy-package2,而且可以用 from dboy import package1, package2 的方式去 import 。這兩個套件的資料夾可以長這樣:

眼尖的讀者可能發現,為什麼 dboy_package1/dboy 下面沒有 __init__.py? 想起這幾年面試別人常會聽到有人說一個資料夾要有 __init__.py 才可以被 python import ,通常當下就會被我糾正然後跟他們說說 python language reference 中的 import system 一章。

老實說我真心希望身為 python programmer 應該時不時複習一下 python language reference,幫助真的很大啊…..

好啦,言歸正傳。根據上述的文件,可以知道當資料夾沒有 __init__.py 時,會被當成 native namespace package 處理。接下來在 dboy_package1setup.py 這樣寫:

setup(
name="dboy-package1",
packages=setuptools.PEP420PackageFinder.find(),
namespace_packages=["dboy"],
zip_safe=False
)

沒錯,只需要加上 namespace_packages 這個 keyword argument ,就可以實現從同一個 namespace 去 import 不同 package 這種語法。 dboy-package2setup.py 其實也就大同小異。

但這裡有個陷阱 (奸笑)

眼尖的人應該又會發現,不是說要沒有 __init__.py 才是 native namespace package 嗎? 那為什麼 dboy_package2/dboy 下面又有 __init__.py 了呢?

沒錯,一般來說是這樣,所以有需要在 dboy_pakage2/dboy/__init__.py 做些手腳。裡面的內容是這樣的:

import pkg_resourcespkg_resources.declare_namespace(__name__)

有就是透過 pkg_resources 去宣告某個 module 是 namespace 而已。

安裝之後,我們就可以這樣寫了:

from dboy import package1, package2

而且在 site-packages 中是長這樣的:

package1package2 彷彿就像嵌入 dboy 這個 package 一樣。

結語

又學會了一個 python 冷技巧 (撒花)

但到底拿來幹嘛呢? 老實說我也不知道。但是我覺得或許是個設計 addon package 的好用功能。

譬如說你有一個核心的 core ,然後你希望根據不同需求去安裝不同擴充功能,然後都被歸在 core 底下時,就可以用 namespace package 去實現。

大概就是這樣囉,Happy Python Programming!

--

--

Dboy Liao
Dboy Liao

Written by Dboy Liao

Code Writer, Math Enthusiast and Data Scientist, yet.

No responses yet