[英]Making another module importable from my Python module
我有一個像這樣的 Python 模塊目錄結構:
my_module
|--__init__.py
|--public_interface
| |--__init__.py
| |--my_sub_module
| | |--__init__.py
| | |--code.py
| |--some_more_code.py
|--other directories omitted
現在, public_interface
目錄(以及其他幾個目錄)僅用於將代碼組織成邏輯子單元,作為我和其他開發人員的指南。 my_module
的最終用戶只能將其視為my_module.my_sub_module
,中間沒有public_interface
。
我寫了這些__init__.py
文件:
my_module.__init__.py
:from .public_interface import *
和
my_module.public_interface.__init__.py
:from . import my_sub_module from .some_more_code import *
和
my_module.public_interface.my_sub_module.__init__.py
:from .code import *
只要用戶只導入頂級模塊,這就能正常工作:
import my_module
my_module.my_sub_module.whatever # Works as intended
但是,這不起作用:
from my_module import my_sub_module
也不:
import my_module.my_sub_module
我必須改變什么才能使這最后兩個進口工作?
導入系統只允許將實際的包和模塊作為帶點的模塊名稱的一部分直接導入,但是您的:
from .public_interface import *
hack 只是使my_sub_module
成為my_module
包的一個屬性,而不是用於導入系統的實際子模塊。 它因為同樣的原因而中斷:
from collections._sys import *
休息; 是的,作為一個實現細節, collections
包碰巧導入別名為_sys
sys
,但這實際上並沒有使_sys
成為collections
的子包,它只是collections
包上的許多屬性之一。 從導入機制的角度來看, my_sub_module
是my_module
的子模塊,而_sys
是collections
的子模塊; 嵌套在my_module
下的子目錄中這一事實無關緊要。
也就是說,導入系統提供了一個鈎子,允許您將其他任意目錄視為包的一部分,即__path__
屬性。 默認情況下, __path__
只包含包本身的路徑(因此my_module
的__path__
默認為['/absolute/path/to/my_module']
),但您可以根據需要以編程方式對其進行操作; 解析子模塊時,它只會搜索__path__
的最終內容,就像導入頂級模塊搜索sys.path
。 因此,要解決您的特定情況(希望public_interface
所有包/模塊都可以導入,而無需在導入行中指定public_interface
),只需將my_module/__init__.py
文件更改為具有以下內容:
import os.path
__path__.append(os.path.join(os.path.dirname(__file__), 'public_interface'))
所做的只是告訴導入系統,當import mymodule.XXXX
發生時( XXXX
是真實姓名的占位符),如果它找不到my_module/XXXX
或my_module/XXXX.py
,它應該尋找my_module/public_interface/XXXX
或my_module/public_interface/XXXX.py
。 如果你想讓它先搜索public_interface
,把它改成:
__path__.insert(0, os.path.join(os.path.dirname(__file__), 'public_interface'))
或者讓它只檢查public_interface
(所以直接在my_module
下沒有任何東西是可導入的),使用:
__path__[:] = [os.path.join(os.path.dirname(__file__), 'public_interface')]
完全替換__path__
的內容。
旁注:您可能想知道為什么os.path
是此規則的一個例外; 在 CPython 上, os
是一個帶有屬性path
的普通模塊(它恰好是模塊posixpath
或ntpath
取決於平台),但您可以執行import os.path
。 這是有效的,因為os
模塊在被導入時,顯式(和hackily)填充了os.path
的sys.modules
緩存。 這是不正常的,它有性能成本; import os
必須始終隱式地導入os.path
,即使從未使用過os.path
中的任何內容。 __path__
避免了這個問題; 除非要求,否則不會導入任何內容。
您可以通過使my_module/__init__.py
包含以下內容來獲得相同的結果:
import sys
from .public_interface import my_sub_module
sys.modules['my_module.my_sub_module'] = my_sub_module
這將允許人們在只完成import my_module
使用my_module.my_submodule
,但這將強制my_module
任何import
導入public_interface
和my_sub_module
,即使從未使用過my_sub_module
中的任何內容。 由於歷史原因, os.path
繼續這樣做(很久以前只使用os.path
API 和import os
,並且很多代碼依賴於這種錯誤行為,因為程序員很懶惰並且它有效),但是新代碼不應該使用這個黑客。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.