Re: [問題] 請問為何os.getcwd()不總在sys.path內?

看板Python作者 (洗洗睡)時間6年前 (2018/06/22 16:01), 6年前編輯推噓4(4025)
留言29則, 4人參與, 6年前最新討論串4/4 (看更多)
原諒我只回關於import的部份XD。這部份我發現第一次遇到很難搞懂又常常忘記,我自己 也是邊查邊寫。 import 的邏輯是什麼!? ====================== 這邊比較能夠細讀的官方文件是PEP-328和 https://docs.python.org/3/tutorial/modules.html 。 ## Absolute Import Absolute import 非常簡單,只要不是以點(dot)開頭的就是absolute import,例如: import foo import foo.bar as bar from foo import bar Absolute import的邏輯就是先找built-in module,沒有的話從`sys.path`的資料夾底下 找一個<name>.py的檔案或是<name>的package(package的定義:帶有`__init__.py`)。 `sys.path`的組成包含 * 程式進入的script的資料夾 * `PYTHONPATH`環境變數 * 安裝python時指定的位置,通常是放安裝的檔案 ## Relative Import Relative import 以點作為起始,兩個點代表上一層,以此類推,而且永遠都是用 `from <> import <>` 來敘述。 至於這個"relative"是相對什麼?這個是用當前module的`__name__`來決定 * 如果它是`foo.bar`之類的值,那就是從`foo.bar`來做相對移動。 `foo`就是最上層的module。 * 如果它是`__main__`(當你執行python script的進入點), 那這個script就是最上層的module。 最後,不管是那一種relative import,都不能超過最上層的module。 ## Import, the Pythonic Way 首先是,不要使用`from foo import *`這種形式。python的名字是會被覆蓋的,也就是 說如果連續兩行 `from <name> import *`然後又有重複的名字,那就會被第二行覆蓋。 然後,分開程式進入點的檔案(`__name__`是`__main__`)還有package。理由是你的進 入點會變成是`__main__`,那就沒辦法用relative import,那你就只能仰賴`sys.path` 來找你自己開發的檔案。如果你不想要自己加工`sys.path`,那就要把你的package放在 程式進入點那層,也就是這樣 project/ main.py setup.py packageA/ ... tests/ funtionA/ test_XXX.py ... ... 最後,盡量不要加工`sys.path`。原因跟第一點一樣是名稱衝突的問題:module是依序在 `sys.path`裡面找,加工會影響其他module,可能會讓後來的module import到錯的檔案, 這種超級難debug。 ## Take Away * absolute import: 用`sys.path`決定 * relative import: 用`__name__`決定,不能超過top-level module * 不要用`*`、分開entrypoint和package、不要加工`sys.path` -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 114.44.244.29 ※ 文章網址: https://www.ptt.cc/bbs/Python/M.1529654464.A.920.html

06/22 19:25, 6年前 , 1F
Absolute import 那邊不太對, 它只看 sys.path, 不一定
06/22 19:25, 1F

06/22 19:26, 6年前 , 2F
是先找 built-in (其實 Python 直譯器本身並不知道哪些
06/22 19:26, 2F

06/22 19:26, 6年前 , 3F
模組是 built-in 哪些不是)
06/22 19:26, 3F

06/22 19:28, 6年前 , 4F
不過這篇的概念很棒, 可惜我不是板主不能 m...
06/22 19:28, 4F

06/22 23:31, 6年前 , 5F
嗯!?那部份我是從https://tinyurl.com/qaasj5n 翻譯來
06/22 23:31, 5F

06/22 23:31, 6年前 , 6F
我猜這裡的built in是指用C寫的那些modules,不過從來沒試
06/22 23:31, 6F

06/22 23:31, 6年前 , 7F
過XD。
06/22 23:31, 7F

06/23 08:45, 6年前 , 8F
想請教一下,那 debug 跟 test 的檔案要放哪裡?
06/23 08:45, 8F

06/23 08:45, 6年前 , 9F
按照此篇所述,debug 跟 test 的檔案要放在 project 最上層
06/23 08:45, 9F

06/23 08:46, 6年前 , 10F
如此才能不加工 sys.path 又能使用 relative import
06/23 08:46, 10F
sys.path和relative import無關

06/23 08:47, 6年前 , 11F
但這很奇怪呀,尤其 test 還有可能依測試目的放不同資料夾
06/23 08:47, 11F
我修改了上面project的架構,test資料夾通常會放在top-level。 我假設你說的debug是指開發的時候用來測的script,test是指拿來跑unit test的檔案。 Debug我會把他看成是用完就丟不會把他放進版控,所以不是放在最上層就是加`sys.path` ,不然就是用`PYTHONPATH` test的話有蠻多種的,我是用pytest這個framework,然後執行的時候加`PYTHONPATH` ,像是 PYTHONPATH='./' pytest 另外,如果你寫好setup.py,那可以用tox這個工具幫你創好虛擬環境(而且一次跑多個 版本的python),安裝你寫的package然後跑你要的指令。所以open source常見的組 合就是 tox(創環境) + pytest(找test script來跑) + travis(免洗的標準環境) 當然小project可以不用這麼認真,原則就是自動化test,source code不要亂加東西。 最後請注意,以上講的都是用absolute import。Relative import是在你寫的package裡面 用,absolute import是用來找到package的root folder。 ※ 編輯: ThxThx (114.44.244.29), 06/23/2018 11:39:54

06/23 15:41, 6年前 , 12F
感謝解說,有空試試看這篇裡講的東西看看會不會比較好用
06/23 15:41, 12F

06/24 14:30, 6年前 , 13F
感謝解說!有幾個問題我想再請教一下:
06/24 14:30, 13F

06/24 14:31, 6年前 , 14F
1. PYTHONPATH環境變數可以賦予數個不同的資料夾嗎?
06/24 14:31, 14F
就跟你設PATH一樣用冒號隔開

06/24 14:33, 6年前 , 15F
2. 如果我沒理解錯,看起來relative import只能寫在「會
06/24 14:33, 15F

06/24 14:34, 6年前 , 16F
被其它script/module import的module」?在程式本身寫
06/24 14:34, 16F

06/24 14:34, 6年前 , 17F
relative import的話,一定會因為碰到__main__而錯誤。
06/24 14:34, 17F
不像javascript,code愛放哪就放哪 import python module要先找到package(用absolute import)然後再用點(dot)往下鑽 relative import是給你在自己的package底下互相引用用的

06/24 14:41, 6年前 , 18F
BTW 對我來說 最常碰到的困擾倒不是因為debug或test檔案
06/24 14:41, 18F

06/24 14:45, 6年前 , 19F
我在做的大多是data analyses,所以本來就會把檔案歸類
06/24 14:45, 19F

06/24 14:45, 6年前 , 20F
成不同的資料夾,例如:
06/24 14:45, 20F

06/24 14:47, 6年前 , 21F
projects/crons/month 是產生資料月報有關的檔案
06/24 14:47, 21F

06/24 14:47, 6年前 , 22F
projects/crons/week 是產生資料週報有關的檔案
06/24 14:47, 22F

06/24 14:48, 6年前 , 23F
projects/prediction_model 跟公司業務預測模型有關
06/24 14:48, 23F

06/24 14:49, 6年前 , 24F
projects/connection 連接公司資料庫所需的package
06/24 14:49, 24F

06/24 14:50, 6年前 , 25F
projects/utils 是資料前處理、資料清洗的package
06/24 14:50, 25F

06/24 14:51, 6年前 , 26F
這種狀況下,relative import卻完全幫不上忙 @@
06/24 14:51, 26F

06/24 14:52, 6年前 , 27F
又要不修改sys.path的話,只能讓PYTHONPATH指向多處?
06/24 14:52, 27F
設PYTHONPATH是在執行的時候修改sys.path啊:P 如果是這樣,那還不如加在sys.path裡面,至少不用記得執行的時候要加東西。 你會這樣放code可能也有其他的考量,我會這樣建議是覺得,隨意加sys.path也許沒有增 加現在的開發成本,但是絕對是增加維護的成本。哪個成本比較重要就要看你的選擇了。 回過頭來,現在假設問題就是crons/week和prediction_model裡的scripts都需要utils的 package。以前可能是每個script都寫很長,但現在如果變成 projects/ crons_week.py crons_month.py (scripts) predict.py ... crons/ ... (產生資料報表的功能) prediction_model/ ... (model本身) utils/ ... (其他共用的邏輯) 所有第一層scripts的功能是parse CLI arguments然後呼叫其他資料夾裡面的功能,不要 把複雜的邏輯寫在這裡面。也許這樣每個人就很容易了解這些檔案在做什麼、每個package 各自擁有什麼功能。 所以就是,我主張每個package把他看成是功能的集合/公約數。不一定要照做,不過 至少你現在知道absolute/relative import的原理了XD

06/24 14:53, 6年前 , 28F
這也是為什麼我第一個問題那麼問
06/24 14:53, 28F
※ 編輯: ThxThx (114.44.244.29), 06/25/2018 01:27:26

06/25 10:29, 6年前 , 29F
Thank you!!!
06/25 10:29, 29F
文章代碼(AID): #1RBAp0aW (Python)
文章代碼(AID): #1RBAp0aW (Python)