摘要:所以,解決方案是強制要求從整個項目的頂層用來設置端正的路徑。這個做法是官方推薦的,也是合邏輯的,即一個完整的項目運行就應當以項目為入口來運行所有的子或子。經過不斷的實踐,發現他們大都沒說清楚上下文,甚至沒有告訴完整的解決方案。
參考Python官方:Packages
?參考:Python相對導入一處不解
參考:使用相對路徑名導入包中子模塊
Python里,就像所有的.py文件被稱為Module模塊一樣,所有的文件夾都被稱為Package包。前提是,這個文件夾里有一個__init__.py文件,可以是空文件也可以有一些方便都內容。
一旦一個文件夾可以被視為Package,那么其中的所有文件都會有獨立的Namespace命名空間,即變量都不共享,與其它的package完全獨立。一個項目里,可以有很多個子文件夾、子子文件夾,一旦變成package,那么它們都能互相獨立,方便我們引用。
理解sys.pathPython的目錄結構中,“向當前腳本以下的目錄import”永遠不會出問題,出問題的,總是“向上一層目錄import”
在Python的運行一個.py腳本的時候,會自動將sys.path設置為腳本所在目錄。然后凡是這個sys.path之下的所有的文件模塊,都能直接import導入。
但是,很明顯的,只有這個.py腳本之內的目錄才包括在path里,也就是說它之外的所有事情它都一無所知。相當于Linux shell中的PATH。
在Python的導入邏輯里,非常/非常/非常重要的一點必須時時刻刻記在心上:
啟動腳本所在的目錄,將持續作為當前目錄來運行所有導入的模塊。
也就是說,如果你要導入另一文件夾的模塊,而那個模塊依賴于它同級目錄的一個模塊,這個時候它是不能直接導入同級目錄模塊的!因為當前的path已經變了!
比如,目錄結構如下:
Project ├── __init__.py ├── main.py ├── sub1 │?? ├── __init__.py │?? ├── common1.py │?? ├── mod1.py * └── sub2 ├── __init__.py ├── common2.py └── mod2.py *
假設,我們在mod1.py中使用from common1 import *來引用同級目錄的sub1/common1.py。
同時,我們在mod2.py中使用from common2 import *來引用同級目錄的sub2/common2.py。
另外,我們在mod1.py中需要導入mod2.py。(先略過具體實現方法)
那么問題出現了:
當我用$ python mod1.py時候,當前的path是project/sub1/,導入mod2.py后,它在執行from common2 ...的時候,理所當然的是找不到的,因為sub1中沒有common2.py這個文件!
那么現在,我們應該做什么呢?改變mod2.py中的導入路徑嗎?
當然不行!我們不能隨便改一個Package的導入邏輯,如果這個package是別的作者寫的怎么辦?如果改了以后,那個package以自身為入口時候運行怎么辦?這些全都會亂套。
所以,解決方案是:
強制要求從整個項目的頂層用python -m project.sub1.mod1來設置端正的PATH路徑。 然后其它所有子模塊、子包都用from project.sub2 import mod2這樣的完整項目引用的語句來導入。
這個做法是PEP官方推薦的,也是合邏輯的,即:
一個完整的項目運行就應當以項目為入口來運行所有的子module或子package。
如果又想讓某個子package被同項目的其它子package引用,又想多帶帶運行,那就應當徹底把它從文件夾里抽離出來變成一個多帶帶的“第三方庫”來用。
那么具體應該怎么修改各個文件中的導入語句呢:
刪除每個文件中的相對引用,如from common1 import *。
改為項目級別的絕對引用,如:from project.sub2 import mod2
如果需要運行/測試某個子模塊的文件,需要cd切換到項目目錄的再上一層中,執行:python -m project.sub1.mod1。注意,這里的命名是包/模塊式的,不能以.py結尾!
Sibling Package Imports 父目錄中的同級目錄導入具體問題來了:怎么導入父級目錄中的其它模塊或包呢?
再復習一下我們的目錄結構:
Project ├── main.py ├── sub1 │?? ├── common.py │?? ├── mod1.py * └── sub2 ├── common.py └── mod2.py *
目的:在sub1/mod1.py中,導入sub2/mod2.py。同時,mod2.py中還需要導入同目錄的common.py才能工作。
這是一個項目里再正常不過的操作了,可是如果你嘗試google一下的話,可能會花費你數小時,結果還是讓你自己的大腦Stack-overflow.
目前stackoverflow會有人提供這幾種hacks來實現我們的目的:
在mod1.py中用sys.path.append("..")來把父級目錄加到path中引用
用from ..sub2 import mod2來實現相對引用
直接from project.sub2 import mod2
在__init__.py進行一些path設定或import導入。
經過不斷的實踐,發現他們大都沒說清楚上下文,甚至沒有告訴完整的解決方案。
總結出的經驗就是:以上的那些hacks終究是hacks,不是官方推薦的,也不能真正派上用場。
比如sys.path.append()方法,一開始似乎走通了不報錯。但是真實項目走起來,比不可能給每個文件都加一句sys.path.append()。走到最后,你用print( sys.path )會發現path中莫名其妙多了很多很多路徑。這肯定不行,也沒見過任何項目源碼里這么寫的。
再比如from ..sub2 import *這樣的相對引用,能這么用的上下文必須是:執行腳本時要是從文件夾頂級入口執行,如果直接從mod1.py執行腳本,那么這句話是會報錯的。
所以還是用官方推薦的方法和邏輯,丟棄那些hacks吧。
要達到sibling imports,在這個例子里,具體做法是:
修改所有的模塊mod1.py和mod2.py,把導入語句改為from project.sub? import mod?這樣的。
如果要導入具體某個模塊中的類或函數,則:from project.sub?.mod? import MyClass
執行子包中的某個子模塊時,cd到project目錄再往上一層,輸入python -m project.sub1.mod1執行
執行整個項目的話,就python -m project
__init__ 和 __all__ 限制導入模塊參考Python官方: Importing ? From a Package
Python規定:
如果在一個package包中的__init__.py中寫上__all__ = ["模塊1", "模塊2", "模塊3"]的話,
那么在其它模塊引用這個package包使用from PACKAGE import *這種用法的時候,
就不會真的引用包中所有的模塊(那樣會很耗內存),而只能導入作者在__all__里規定的模塊。
參考Python官方:Packages in Multiple Directories
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/43083.html
摘要:搭建多頁面應用在往下看之前請確保先上個涼的吃著目錄結構編譯結果配置文件腳本存放處項目開發中一些常用的方法主要存放和請求有關的靜態文件模版文件項目開發中一些常用的方法其實我覺得可以和放在一塊,但是個人習慣還是分開啦開始擼代碼如何創建多頁面應用 webpack4 搭建 react 多頁面應用 在往下看之前請確保nodejs > 8.X 先上個涼的吃著 目錄結構 . ├── dist ...
摘要:之痛原文地址譯者校正實用編程指南這是我在所做的演講。事實一和二共同造成了計算機設備結構與世界人類需求的一個沖突。就是為了解決之前的老的字符集問題。值意味著,失敗時將會返回一個標準的替代字符。將使用進行了解碼。 Unicode之痛原文地址: http://nedbatchelder.com/text...譯者: yudun1989 校正: sicklife實用Unicode編程指南這是...
摘要:引子考慮有如下代碼結構情況其余文件為空。分析情況當我們運行時,這時候系統的搜素路徑包括,我們可以通過在中添加證實這一點。情況情況其實和情況很不一樣,情況叫做,也就是包內間接引用。顧名思義,這種引用方法只能在包內使用。都是,但不一定是。 引子 考慮有如下代碼結構 . ├── cat │?? ├── __init__.py │?? ├── cat.py │?? └── moo.py └──...
閱讀 2985·2021-11-24 10:22
閱讀 3050·2021-11-23 10:10
閱讀 1359·2021-09-28 09:35
閱讀 1756·2019-08-29 13:16
閱讀 1397·2019-08-26 13:29
閱讀 2792·2019-08-26 10:27
閱讀 683·2019-08-26 10:09
閱讀 1443·2019-08-23 18:05