1 導入模塊后具體做了什么?
2 從哪個位置找到需要導入的模塊
3 導入模塊的常見場景晴竞?
1 導入模塊后具體做了什么亲族?
1.1 第一次導入
- 在自己當下的命名空間中,執(zhí)行所有代碼
- 創(chuàng)建一個模塊對象拂蝎,并將模塊內(nèi)所有頂級變量以屬性的形式綁定在模塊對象上
- 在 import 的位置穴墅,引入 import 后面的變量名稱到當前命名空間
1.1.1 在自己當下的命名空間中,執(zhí)行所有代碼
Test2.py
name = "Test2"
age = 2
print("執(zhí)行了Test2所有代碼")
Test1.py
import Test2
# 讓后 run 該分代碼
>>>>打印結(jié)果
執(zhí)行了Test2所有代碼
- 也就是說 Test1.py 中導入了 Test2模塊后温自,在運行 Test1模塊代碼時玄货,會先讓 Test2中的代碼在 Test2 的自己的命名空間中執(zhí)行 Test2的所有代碼
- 其實,從使用角度來理解的話悼泌,當然是 Test2 內(nèi)的代碼先跑完松捉,Test1 中才能使用 Test2 中的變量等資源啊。
1.1.2 創(chuàng)建一個模塊對象馆里,并將模塊內(nèi)所有頂級變量以屬性的形式綁定在模塊對象上
(具有內(nèi)存地址及類型)
Test2.py
name = "Test2"
age = 2
print("執(zhí)行了Test2所有代碼")
Test1.py
import Test2
# 查看Test2
print(Test2)
# 查看內(nèi)存地址
print(id(Test2))
# 查看類型
print(type(Test2))
# 通過打印 Test2 對象的屬性隘世,驗證在 Test1 中需要通過 模塊名.屬性形式訪問的原因
print(Test2.__dict__)
>>>>打印結(jié)果
執(zhí)行了Test2所有代碼
<module 'Test2' from '/Users/xxx/Desktop/PythonProject/PackageAndModule/Test2.py'>
4560942648
<class 'module'>
# __dict__ 字典打印結(jié)果沒有天上自己試驗
1.1.3 在 import 的位置,引入 import 后面的變量名稱到當前命名空間
- 在 Test1 中通過 Test2變量名來使用 Test2 模塊的資源
Test2.py
name = "Test2"
age = 2
print("執(zhí)行了Test2所有代碼")
Test1.py
import Test2
print(Test2.name)
print(Test2.age)
>>>>打印結(jié)果
執(zhí)行了Test2所有代碼
Test2
2
- 將 Test2 這個指向 Test2 模塊的對象變量名引入 當前 命名空間中(注意命名空間即作用域的區(qū)別)
Test1.py
def testFunc():
import Test2 # 導入到了函數(shù)的命名空間中
print(Test2.name)
testFunc()
print(Test2.name) # 全局命名空間不能使用 Test2
>>>>打印結(jié)果
Traceback (most recent call last):
執(zhí)行了Test2所有代碼
File "/Users/xxx/Desktop/PythonProject/PackageAndModule/Test1.py", line 14, in <module>
Test2
print(Test2.name)
NameError: name 'Test2' is not defined
1.2 第二次導入
1.2.1直接執(zhí)行1.1.3這個步驟
Test2.py
name = "Test2"
age = 2
print("執(zhí)行了Test2所有代碼")
Test1.py
import Test2
print(Test2.name)
print(Test2.age)
# 再次導入鸠踪, 打印中并沒有出現(xiàn)兩次“執(zhí)行了Test2所有代碼”
import Test2
>>>>打印結(jié)果
執(zhí)行了Test2所有代碼
Test2
2
1.3 注意
from ... import ... 導入方式與 import ... 導入方式底層執(zhí)行步驟區(qū)別
- 兩種導入方式均都會大致的執(zhí)行上述的3個步驟
- 多次導入模塊丙者,該模塊并不會執(zhí)行多次
- 兩種導入方式并不存在哪一種更節(jié)省內(nèi)存之說,區(qū)別在于把哪一部分內(nèi)容拿到當前位置來用营密,因為一旦導入都將模塊內(nèi)的對象創(chuàng)建好械媒。
Test2.py
name = "Test2"
age = 2
print("執(zhí)行了Test2所有代碼")
Test1.py
from Test2 import name
print(name)
>>>>打印結(jié)果
執(zhí)行了Test2所有代碼
Test2
2 模塊檢索順序(從哪個位置找到需要導入的模塊)
2.1 第一次導入
- 按照模塊檢索路徑順序查找
1 內(nèi)建模塊
2 sys.path
2.1 當前目錄環(huán)境變量PYTHONPATH
2.2 Python 安裝路徑
2.3 安裝路徑下的.pth 路徑
2.4 lib 庫 site-packages
2.5 lib 庫 site-packages下的 pth 文件配置
3 其他
2.2.1 第一層級
- 內(nèi)置模塊<built - in>
2.2.2 第二層級
- sys.path
>>> import sys >>> print(sys.path) ['', '/usr/local/Cellar/python3/3.6.4_1/Frameworks/Python.framework/Versions/3.6/lib/python36.zip', '/usr/local/Cellar/python3/3.6.4_1/Frameworks/Python.framework/Versions/3.6/lib/python3.6', '/usr/local/Cellar/python3/3.6.4_1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload', '/usr/local/lib/python3.6/site-packages'] >>>
- 構(gòu)成 (如下索引并非指優(yōu)先順序)
1.1 當前目錄
1.2 環(huán)境變量 PYTHONPATH 中指定的路徑列表
1.3 特定路徑下的 .path 文件中的文件路徑列表
1.4 在 Python 安裝路徑的 lib 庫中搜索
- 追加路徑的方式(修改 sys.path 方式)
-
直接修改 sys.path,只作于本次的執(zhí)行代碼模塊
Test1.py
import sys
sys.path.append("/Users/xxx/Desktop/myTest")
print(sys.path)
import fkm
# print(fkm.__dict__)
print(fkm.age)
>>>>打印結(jié)果
打印了abc 所有代碼
12
注意該路徑是追加到sys.path最后的评汰,當 工程目錄中有同名模塊時纷捞,會優(yōu)先加載工程內(nèi)的同名模塊
修改環(huán)境變量 PYTHONPATH(自行百度)
實際,修改PYTHONPATH環(huán)境變量也是修改 sys.path 列表內(nèi)容被去,將環(huán)境變量PYTHONPATH代表的路徑添加到 sys.path中主儡,只不過是默認就給添加了,不同于通過代碼修改编振。
注意缀辩,系統(tǒng)修改 PYTHONPATH 和 pycharm 添加環(huán)境變量的區(qū)別-
添加.pth 文件
添加 .pth 文件位置可通過 site 模塊查看import site print(site.getsitepackages()) >>>>打印結(jié)果 ['/usr/local/Cellar/python3/3.6.4_1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages', '/Library/Python/3.6/site-packages']
只需在以上目錄下創(chuàng)建.pth 文件臭埋,然后在文件中添加對應(yīng)模塊路徑即可
2.2 第二次導入
1 從已經(jīng)加載過的模塊中去找
- 查看已加載模塊
import sys
sys.modules
3 導入模塊的常見場景?
3.1 局部導入
def testFunc():
import Test2 # 導入到了函數(shù)的命名空間中
print(Test2.name)
testFunc()
使用的時候才進行導入臀玄,此時 Test2 是一個局部變量瓢阴,只存在于函數(shù)作用域中;減少內(nèi)存的開銷健无;
3.2 覆蓋導入
場景一:
自定義模塊和非內(nèi)置的標準模塊重名荣恐,根據(jù)前者的存儲位置,有可能前者會覆蓋后者
建議:自定義模塊命名不要與后者重名場景二:
自定義模塊與內(nèi)置模塊重名累贤,內(nèi)置肯定覆蓋自定義
建議:如果確實需要使用重名的自定義模塊叠穆,可以使用 from ... import ...方式指明絕對路徑進行導入使用
from package1 import sys
print(sys.name)
3.3 循環(huán)導入
- 模塊 A 中導入模塊 B, 模塊 B 中又導入模塊 A臼膏;
Test2.py
t2_1 = "t2_1"
t2_2 = "t2_2"
import Test1
print(Test1.t1_1)
print(Test1.t1_2)
Test1.py
t1_1 = "t1_1"
t1_2 = "t1_2"
import Test2
print(Test2.t2_1)
print(Test2.t2_2)
>>>>打印結(jié)果
t2_1
t2_2
t1_1
t1_2
t2_1
t2_2
3.4 可選導入
- 兩個功能相近的包硼被,根據(jù)需求優(yōu)先選擇其中一個導入
- 想優(yōu)先使用 A;當沒有 A 的情況下渗磅,使用 B 作備份
try:
import Testxxx as t
except ModuleNotFoundError:
import Test1 as t
print(t.name)
3.5 包內(nèi)導入
明確一下概念
絕對導入和相對導入嚷硫,這兩個概念是相對于包內(nèi)導入而言的
包內(nèi)導入:即是包內(nèi)的模塊導入包內(nèi)的模塊,即package1包內(nèi)的 Test3 內(nèi)導入 Test3_1
包外導入:即是包外的模塊導入包內(nèi)的模塊始鱼,即 Test1 內(nèi)導入package1包內(nèi)的 Test3
查看當前文件參照的執(zhí)行路徑
import sys
sys.path
- 查看當前模塊名稱
如果文件是直接運行的話仔掸,__name__的值是 __main__
如果是以模塊的形式進行加載文件的話,該名稱的值則是由加載路徑?jīng)Q定医清,格式為:包名.子包名.模塊名 (此時起暮,第一層包名稱作為頂級名稱)
print(__name__)
Test1.py
import package1.Test3
Test3
- 查找文件順序都是先 內(nèi)置<built-in> 再 sys.path
- 當點擊運行(使用解析器去執(zhí)行)某一 py 文件時,此時程序會確定當前py 文件所在的目錄会烙,并且將該目錄添加到 sys.path 中负懦,此時的 sys.path內(nèi)容基本確定
3.5.1 概念
3.5.1.1 絕對
- 參照sys.path路徑進行檢索
- 指明包名或模塊名
import a
、from a import b
- 上述兩點是基于 Python3.x 之后
3.5.1.2 相對
- 使用
.
來指代相對路徑
.
:根據(jù)模塊名(__name__)稱所獲取的當前目錄
..
:根據(jù)模塊名(__name__)稱所獲取的上層目錄 - 如:
from . import a
持搜、from .. import a
3.5.2 補充
- Python3.x 版本之前密似,直接使用 import a。會優(yōu)先從本地當前目錄查找葫盼,并非是 sys.path
- 解決方案:
from __future__ import absolute_import
残腌,使用該代碼語句改變 import 行為
3.5.3 結(jié)論
- 包內(nèi)導入,使用相對導入
- 包外導入贫导,使用絕對導入
例子1:Test1 導入 Test3抛猫,Test3 內(nèi)又導入Test3_1;即包外導入+包內(nèi)導入
Test1.py
import package1.Test3
或
from package1 import Test3
Test3.py
from . import Test3_1
不建議寫成 from package1 import Test3_1 孩灯,因為當包名變化時闺金,使用包名的地方都要改動
例子2:多層級導入
Test1.py
print("Test1")
print(__name__)
from package1 import Test3
Test2.py
print("Test2")
print(__name__)
Test3.py
print("Test3")
print(__name__)
from .package_sub1 import Test4
Test3_1.py
print("Test3_1")
print(__name__)
Test4.py
print("Test4")
print(__name__)
from .package_subsub1 import Test5
Test4_1.py
print("Test4_1")
print(__name__)
Test5.py
print("Test5")
print(__name__)
from .. import Test4_1
from ... import Test3_1
# from .... import Test2 #包內(nèi)不能獲取包外的文件
運行 Test1.py
>>>>打印結(jié)果
Test1
__main__
Test3
package1.Test3
Test4
package1.package_sub1.Test4
Test5
package1.package_sub1.package_subsub1.Test5
Test4_1
package1.package_sub1.Test4_1
Test3_1
package1.Test3_1
- 可以通過上述例子,了解點點的作用
- 注意:包內(nèi)不能獲取包外的文件