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