哈喽~小夥伴們我又來啦~
Python憑什麼就那麼好用呢?
毫無疑問,大量現成又好用的内置/第三方庫功不可沒。
那我們是怎麼使用它們的呢?
噢,對了~是用的
import xxx
這個語句。
之是以會有此一問,也是之前有一次使用PyCharm進行開發時(又)踩了個坑……
廢話少說,先講問題
像下面這樣一個項目結構:
Projetc_example
|-- A
|-- alpha.py
|-- beta.py
|-- B
|-- theta.py
|-- main
|-- main.py
假設要在
main.py
中導入
theta.py
:
# main/main.py
from B import theta
顯然會導緻我們所不希望的問題,即Python不知道要到哪裡去找這個名為B的子產品(包是一種特殊的子產品):
Traceback (most recent call last):
File "main/main.py", line 1, in <module>
from B import theta
ModuleNotFoundError: No module named 'B'
可是這就奇了怪了,為啥同樣的代碼,在PyCharm裡運作就是好的了呢?
import
的查找路徑
import
于是我們不辭艱辛,上下求索,原來在Python中,import語句實際上封裝了一系列過程。
1. 查找是否已導入同名子產品
首先,Python會按照
import xxx
中指定的包名,到
sys.modules
中查找目前環境中是否已經存在相應的包——不要奇怪為什麼都沒有導入
sys
這個子產品就有
sys.modules
了。
sys
是Python内置子產品,也就是親兒子,導入隻是意思一下,讓我們這樣的外人在導入的環境中也可以使用相關接口而已,實際上相應的資料對Python而言從始至終都是透明的。
我們可以導入
sys
檢視一下這個對象的具體内容(節省篇幅,做省略處理):
>>> import sys
>>> sys.modules
{'sys': <module 'sys' (built-in)>, 'builtins': <module 'builtins' (built-in)>, ...'re': <module 're' from 'E:AnacondaAnacondalibre.py'>, ...}
這些就都是Python一開始就已經加載好的子產品,也就是安裝好Python之後,隻要一運作環境中就已經就緒的子產品——隻是作為外人的我們還不能直接拿過來用,得跟Python報備一聲:“欸,我要拿您兒子來用了嗨~”
很容易可以發現,
sys.modules
中列出來的已加載子產品中存在明顯的不同,前面的很多子產品顯得很幹淨,而後面的很多子產品都帶有
from yyy'
的字樣,并且這個
yyy
看起來還像是一個路徑。
這就關系到我們接下來要講的步驟了。
2. 在特定路徑下查找對應子產品
前面我們講到了,當我們導入某個子產品時,Python先會去查詢
sys.modules
,看其中是否存在同名子產品,查到了那當然皆大歡喜,Python直接把這個子產品給我們用就好了,畢竟兒子那麼多,借出去賺點外快也是好事兒不是?
可問題在于:那要是沒找到呢?
這顯然是一個很現實的問題。畢竟資源是有限的,Python
不可能把你可能用到的所有子產品全都一股腦給加載起來,否則這樣男上加男加男加男……誰也頂不住啊不是(大霧
于是乎就有人給Python出了個主意:那你等到要用的時候,再去找他說他是你兒子呗
Python:妙哇~
有了這個思路,Python就指定了幾家特定的酒樓,說:“凡是去消費的各位,都可以給我當兒子。”
就這樣,一些本來不是Python親兒子的人,出于各種原因聚集到了這幾家酒樓,以雇傭兵的身份随時準備臨時稱為Python的兒子。
這可就比周文王開局就收100個義子優雅多了,養家糊口的壓力也就沒那麼大了(Python:什麼?我的親兒子都不止100個?你說什麼?聽不見啊——
回到正經的畫風來——
實際上,在Python中,
sys.path
維護的就是這樣一個py交易的結果~~(诶?好像莫名發現了什麼),其中儲存的内容就是這幾家“指定酒樓”,也就是當Python遇到不認識的兒子~~子產品時,就會去實地查找的路徑。
我們也可以列印出來看看具體内容:
>>> sys.path
['', 'E:AnacondaAnacondapython37.zip', 'E:AnacondaAnacondaDLLs', 'E:AnacondaAnacondalib', 'E:AnacondaAnaconda', 'E:AnacondaAnacondalibsite-packages', 'E:AnacondaAnacondalibsite-packageswin32', 'E:AnacondaAnacondalibsite-packageswin32lib', 'E:AnacondaAnacondalibsite-packagesPythonwin']
大體上就是安裝環境時配置的一些包所在路徑,其中第一個元素代表目前所執行腳本所在的路徑。
也正是是以,我們可以在同一個目錄下,大大方方地調用其他子產品。
3. 将子產品與名字綁定
找到相應的非親生子產品還沒完,加載了包還得為它配置設定一個指定的名字,我們才能在腳本中使用這個子產品。
當然多數時候我們感覺不到這個過程,因為我們就是一個
import
走天下:
import sys
import os
import requests
這個時候我們指定的子產品名,實際上也是指定的稍後用來調用相應子產品的對象名稱。
換個更明顯的:
import requests as req
如果這個時候隻使用了第二種方式來導入
requests
這個子產品,那麼很顯然在之後的程式流程中,我們都不能使用
requests
這個名字來調用它而應當使用
req
。
這就是Python導入過程中的名稱綁定,本質上與正常的指派沒有太大差別,加載好了一個對象之後,然後為這個對象賦一個指定的變量名。
當然即使是已經加載好的子產品,我們也可以利用這個名稱綁定的機制為它們取别名,比如:
>>> import sys
>>> import sys as sy
>>> sys.path
['', 'E:AnacondaAnacondapython37.zip', 'E:AnacondaAnacondaDLLs', 'E:AnacondaAnacondalib', 'E:AnacondaAnaconda', 'E:AnacondaAnacondalibsite-packages', 'E:AnacondaAnacondalibsite-packageswin32', 'E:AnacondaAnacondalibsite-packageswin32lib', 'E:AnacondaAnacondalibsite-packagesPythonwin']
>>> sy.path
['', 'E:AnacondaAnacondapython37.zip', 'E:AnacondaAnacondaDLLs', 'E:AnacondaAnacondalib', 'E:AnacondaAnaconda', 'E:AnacondaAnacondalibsite-packages', 'E:AnacondaAnacondalibsite-packageswin32', 'E:AnacondaAnacondalibsite-packageswin32lib', 'E:AnacondaAnacondalibsite-packagesPythonwin']
>>> sys == sy
True
問題解決
好了,上面就是對Python導入機制的大緻介紹,但是說了半天,我們的問題還沒有解決:在項目中如何簡潔地跨子產品導入其他子產品?
在使用PyCharm的時候倒是一切順遂,因為PyCharm會自動将項目的根目錄加入到導入的搜尋路徑,也就是說像下面這樣的項目結構,在任意子產品中都可以很自然地通過
import A
導入子產品A,用
import B
導入子產品B。
Projetc_example
|-- A
|-- alpha.py
|-- beta.py
|-- B
|-- theta.py
|-- main
|-- main.py
但是在非IDE環境中呢?或者說就是原生的Python環境中呢?
很自然地我們就會想到:那就手動把項目根目錄加入到
sys.path
中去嘛。說起來也跟PyCharm做的事沒差呀
可以,貧道看你很有悟性,不如跟我去學修仙吧
是以我們就通過
sys
和
os
兩個子產品七搞八搞(這兩個子產品以前有過介紹,不再贅述)——
噔噔噔噔——好使了
# Peoject_example/A/alpha.py
print("name: " + __name__)
print("file: " + __file__)
def al():
print("Importing alpha succeeded.")
main.py
中則加入一個邏輯,在
sys.path
中增加一個項目根目錄:
import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
import A.alpha
A.alpha.al()
# name: A.alpha
# file: *Project_exampleAalpha.py
# Importing alpha succeeded.
大功告成,風緊扯呼~
總結
本文借由一個易現問題引出對Python導入機制的介紹,實際上限于篇幅,導入機制隻是做了一個概覽,具體的内容還要更加複雜。本文講到的這三步則适用于比較常見的情形,了解了這三步也足以應付很多問題了。更多内容還是留待大家自行探索,當然後續也可能會有文章進一步講解——誰知道呢哈哈~~(又挖坑了)~~
ノBye~