您當前的位置:首頁 > 文化

[Python] 也整理一下萬惡的Python import機制

作者:由 藍莓的鏟屎官 發表于 文化時間:2020-02-25

關於Python import,基本上是個寫Python的人都遇到過(除非你從來不拆分專案的目錄結構)。這個問題在StackOverflow上被討論了七八年,但其實早就有人提出過,只要模組搜尋路徑裡包含專案根目錄,這個愚蠢的問題就沒了,但從來沒有人正面回答過這個問題。

結果就是,萬惡的import機制仍然在禍害著每一個人。

Python的import機制坑在哪?

假設專案目錄是這樣子的——

import_test

|_______ modules

| |____ __init__。py

| |____ a。py

| |____ b。py

|_______ tests

| |____ __init__。py

| |____ test_a。py

| |____ test_b。py

|_______ app。py

app。py

假設是你的主程式,從這裡執行的時候,

import

是不太容易出問題的,絕對路徑也好相對路徑也好。坑在

tests

這個目錄,把測試指令碼放到一個目錄裡這本身沒問題

,但是,如果你在tests下直接執行這些測試指令碼,你就會踩坑。

比如,

test_a。py

modules

裡匯入了

a

,如下所示——

from modules import a

a = a。a1()

if a > 50:

print(a)

else:

print(100-a)

當你執行的時候,你會發現

Python

找不到

modules

在哪 ,這是自然的,因為模組搜尋路徑裡沒有包含上級目錄,所以會報錯

ModuleNotFoundError: No module named ‘modules’

import_test> python tests/test_a。py

Traceback (most recent call last):

File “tests/test_a。py”, line 1, in

from modules import a

ModuleNotFoundError: No module named ‘modules’

import_test>

有人說

-m

這個引數有效,我試過,

仍然是要退回到import_test這個目錄,

而且要注意,

不能加.py,因為此時你執行的是一個模組

,如下所示——

import_test> python -m tests。test_a

100

import_test>

Pycharm的最佳化可能會讓你更懵逼

其次,如果你用pycharm,你

可能會直到上線的那天

你才發現這個問題,因為pycharm非常人性化,它

自動幫你把專案路徑新增

好了,所以在pycharm裡,你是可以直接在

tests

下 執行

test_a。py

的 ,不會有任何問題。差別很容易找到,在程式碼最前面加上這麼兩行,

然後分別在pycharm和其它IDE,比如vscode裡執行——

import

sys

print

sys

path

你會發現

pycharm裡打出來的路徑是更多的,它把專案根目錄加進去了。

如果你在不知情的情況下繼續開發下去,並且你把需要直接執行的程式碼,放在了類似於

tests

這樣的目錄下面,而不是

import_test

這個根目錄下,等你上線程式碼那天,你就有的哭了,你會看到各種匯入失敗的錯誤,而你平時用pycharm卻毫無感覺……

也許這才是真正科學的做法——把測試case寫成模組,而不是指令碼

我因為最初用的vscode,在import這個問題上糾結了無數次……然而python的import就是這麼反人類,我又不想把測試指令碼全都扔到最外面,後來我發現很多專案是把測試的case寫在

tests

目錄下,然後

在外面留一個入口指令碼去匯入這些測試case,說白了,測試case本身也變成了一個模組,測試目錄變成了一個包。

參考

最後貼兩篇有用的參考,第二篇是一位臺灣老師寫的

Python專案如何合理組織規避import天坑

Python的Import陷阱

我摘出其中最關鍵的解釋

# 標準的 explicit relative import 寫法

from 。sample_module import sample_func

1。 包含相對路徑的檔案不能直接執行,只能作為 module 被引用,所以失敗

2。 成功印出 Hello!

相對引用是有限制的,比如

tests

目錄下的模組是沒辦法透過相對引用,直接引用到

modules

下面去的

以我們的例子來看

from 。a import a1

a = a1()

if a > 50:

print(a)

else:

print(100-a)

這個執行的結果將會是一個錯誤

import_test> python -m tests。test_a

Traceback (most recent call last):

……

ModuleNotFoundError: No module named ‘tests。a’

import_test>

所以,我個人跨包匯入的時候,都是用的絕對匯入

# 標準的 absolute import 寫法

from sample_package。sample_module import sample_func

1。 如果此層目錄位置不在 python path 中,就會失敗

2。 成功印出 Hello!

也就是

from modules import a

a = a。a1()

if a > 50:

print(a)

else:

print(100-a)

小結

貼一個stackoverflow的連線,在這個連線裡,被採納的回答在7年後更新了答案,所以你就知道這個問題真的是個老問題了……

https://

stackoverflow。com/quest

ions/6323860/sibling-package-imports/50193944#50193944

一個更加廣為人知的做法是sys。path。append,但就像很多在stackoverflow上提問的人說的那樣,我個人一點也不喜歡這種做法,也許只是用在測試腳本里沒有問題,但它看上去更奇怪

另外,這個連結裡也有一段有意思的內容——

[Python] 也整理一下萬惡的Python import機制

換言之,Python的開發團隊是知道這個問題也討論過的,所以,真傲慢。

The only use case?

標簽: import  test  Tests  py  Modules