Python模組化
1、模組化
一般來說,程式語言中,庫、包、模組是一種概念,是程式碼組織方式。
Python中只有一種模組物件, 但是為了模組化組織模組的便利,提供了一個概念--包模組module,指的是Python的原始碼檔案。
包package,指的是模組組織在一起和包名同名的目錄及其相關檔案。
2、匯入語句
語句 |
含義 |
Import模組1[模組2] |
完全匯入 |
Import...as..... |
模組別名 |
Import 的作用:將需要的模組的名稱引用到當前所有的模組的名詞空間中。
載入到了sys.modules裡面去了。
from (後面是模組)import(類、函式)
from pathlib import *
from子句中指定的模組,載入並初始化,(並不是匯入)。
Import語句:
(1)找到指定的模組,載入並初始化他,生成模組物件。找不到,丟擲importError異常。
(2)Import所在的作用域的區域性名稱空間內,增加名稱和上一步建立的物件關聯。
import functools
print(dir())
print(functools)
print(functools.wraps)
1,['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'functools']
2,<module 'functools' from 'C:\\Users\\WCL\\AppData\\Local\\Programs\\Python\\Python35\\lib\\functools.py'>
3,<function wraps at 0x00000018C691F620>
import os.path
print(1,dir())
print(2,os)
print(3,os.path)
1 ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'os']
2 <module 'os' from 'C:\\Users\\WCL\\AppData\\Local\\Programs\\Python\\Python35\\lib\\os.py'>
3 <module 'ntpath' from 'C:\\Users\\WCL\\AppData\\Local\\Programs\\Python\\Python35\\lib\\ntpath.py'>
import os.path as osp
print(dir())
print(osp)
1 ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'osp']
2 <module 'ntpath' from 'C:\\Users\\WCL\\AppData\\Local\\Programs\\Python\\Python35\\lib\\ntpath.py'>
總結:
匯入頂級模組,其名稱會加入到本地名詞空間中,並繫結到其模組物件。
匯入非頂級模組,只是將其頂級模組名稱加入到本地名詞空間中。匯入的模組必須使用完全限定的名稱來訪問。
如果使用了as,as後面的名稱直接繫結到匯入的模組物件,並將該名稱加入到本地名詞空間中。
語句 |
含義 |
from...import.. |
部分匯入 |
From...import...as.... |
別名 |
From語句:
from pathlib import Path,PosixPath #在當前名詞空間指定匯入該模組的指定成員
print(dir())
['Path', 'PosixPath', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
from pathlib import * #在當前名詞空間匯入該模組所有公共成員(非下劃線開頭)
print(dir())
['Path', 'PosixPath', 'PurePath', 'PurePosixPath', 'PureWindowsPath', 'WindowsPath', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
from functools import wraps as wr,partial
print(dir())
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'partial', 'wr']
from os.path import exists #載入、初始化os、os.path模組,exists加入到本地名詞空間並繫結
if exists('c:/t'):
print('yes')
else:
print('no')
print(dir())
print(exists)
no
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'exists']
<function exists at 0x000000212F72B268>
import os
print(os.path.exists)
print(exists)
print(os.path.__dict__['exists'])
print(getattr(os.path,'exists'))
通過上面四種方式獲得同一個物件。
總結:
找到from子句中指定的模組,載入並初始化他(不是匯入)。
對於import子句後面的名稱。
先查from子句匯入的模組是否具有該名稱的屬性。
如果不是,則嘗試匯入該名稱的子模組。
還沒找到,則丟擲importError異常
這個名稱儲存到本地名詞空間中,如果有as子句,則使用as子句後面的名稱。
from pathlib import Path
print(1,Path,id(Path))
import pathlib as p1
print(2,dir())
print(3,p1)
print(4,p1.Path,id(p1.Path))
1 <class 'pathlib.Path'> 784869820392
2 ['Path', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'p1']
3 <module 'pathlib' from 'C:\\Users\\WCL\\AppData\\Local\\Programs\\Python\\Python35\\lib\\pathlib.py'>
4 <class 'pathlib.Path'> 784869820392
看出匯入的Path和p1.Path是同一個物件。
3、自定義模組
自定義模組:.py檔案就是一個模組。
自定義模組名命名規範:
(1)模組名就是檔名
(2)模組名必須符合識別符號的要求,是非數字開頭的字母數字和下劃線的組合。
(3)不能使用系統模組名來避免衝突,除非知道模組名的用途。
(4)模組名全為小寫,下劃線來分隔。
4、模組搜尋順序
import sys
for i in sys.path:
print(i)
C:\Users\WCL\PycharmProjects\untitled1\package\exercise
C:\Users\WCL\PycharmProjects\untitled1
C:\Users\WCL\PycharmProjects\untitled1\venv\Scripts\python35.zip
C:\Users\WCL\AppData\Local\Programs\Python\Python35\DLLs
C:\Users\WCL\AppData\Local\Programs\Python\Python35\lib
C:\Users\WCL\AppData\Local\Programs\Python\Python35
C:\Users\WCL\PycharmProjects\untitled1\venv
C:\Users\WCL\PycharmProjects\untitled1\venv\lib\site-packages
C:\Users\WCL\PycharmProjects\untitled1\venv\lib\site-packages\setuptools-28.8.0-py3.5.egg
C:\Users\WCL\PycharmProjects\untitled1\venv\lib\site-packages\pip-9.0.1-py3.5.egg
使用sys.pah檢視搜尋順序。
當載入一個模組的時候,需要從這些搜尋路徑中從前到後依次查詢,並不搜尋這些目錄的子目錄,搜尋到模組就載入,搜尋不到就丟擲異常。
路徑可以為zip檔案 egg檔案、字典。
.egg檔案,由setuptools庫建立的包,第三方庫常見的格式,添加了元資料,版本號等,依賴項。
資訊的zip檔案。
路徑順序為:程式的主目錄,程式執行的主程式指令碼所在的目錄,Pythonpath目錄,環境變數PYTHONPATH設定額目錄也是搜尋模組的路徑。
標準庫目錄,Python自帶的庫模組所在目錄。
Sys.path可以被修改,追加新的目錄。
5、模組的重複匯入
模組並不會重複匯入,就是查字典的過程(從前向後找。)所有載入的模組都會記錄在sys.modules中,sys.modules是儲存已經載入過的所有模組的字典。
6、模組執行
__name__ 每個模組都會定義一個__name__特殊變數來儲存當前模組的名稱,如果不指定,則預設為原始碼檔名,如果是包則有限定名。__name__這個屬性,用來定義當前的檔名稱。
直譯器初始化的時候,會初始化sys.modules字典,(儲存已載入的模組),建立buildtins(全域性函式、常量),模組,__main__、sys模組,以及模組搜尋路徑sys.path.
搜尋路徑也要加到sys.path中來。
python是指令碼語言,任何一個指令碼都可以直接執行,也可以作為模組被匯入。
當標準輸入、指令碼或互動式讀取的時候,會將模組的 __name__,設定為__main__,模組的頂層程式碼就在__main__這個作用域中執行,頂層程式碼:模組中縮排外層的程式碼。
如果是import匯入的,其__name__預設就是模組名。
從哪裡執行就把__name__ 改為__main__
7、If __name__ == ‘__ main__ ‘:用途
1)本模組的功能測試。
對於非主模組,測試本模組內的函式,類。
2)避免主模組變更的副作用。
頂層程式碼,沒有封裝,主模組使用時候沒有問題,但是一旦有了新的主模組,老的主模組成了被匯入模組,由於原來的程式碼沒有封裝,一併執行
Sys.path(搜尋路徑)
Sys.moduels (字典,已載入的在裡面。)
if __name__ == '__main__':
print('in __main__')
else:
print('in import module')
in __main__
import m2
in import module
8、模組的屬性
屬性 |
含義 |
__file__ |
字串,原始檔路徑 |
__cached__ |
字串,編譯後的位元組碼檔案路徑 |
__spec__ |
顯示模組的規範 |
__name__ |
模組名 |
__package__ |
當模組是包,同__name__:否則,可以設定為頂級模組的空字串 |
9、包
包:特殊的模組。
Python支援目錄。
專案中新建一個目錄m
Import m
print(m)
Print(type(m))
Print(dir())# 沒有__file__屬性
可以匯入目錄m,m也是檔案,可以匯入,目錄模組寫入程式碼,在其目錄下簡歷一個特殊的檔案__init__.py,在其中寫入程式碼。
Pycharm 中建立普通的資料夾和Python的包不同,普通包只是建立目錄,後面的則是建立一個帶有_init__.py檔案的目錄即包。
10、子模組
包目錄下的py檔案,子目錄都是其子模組。
如上建立子模組目錄和檔案,所有的py檔案中就寫一句話print(__name__)
Import * 只是拿共有的模組的內容。公共成員,私有的不能拿。
Import m (載入m)
Import m.m1(載入m和m1)
Import m.m2.m21(載入m,m2,m21)
From m import m1 從m中載入m1
From m.m2 import m21 三層載入。
保留__init__檔案。
11、模組和包的總結
包能夠更好的組織模組,由其是在大的規模下程式碼行數很多,可以將其拆分為很多子模組,便於使用某些功能就在家相應的子模組。
包目錄中 __init__.py 是包在匯入的時候就會執行,內容可以為空,也可以用於該報初始化工作的程式碼。
匯入子模組一定會匯入父模組,匯入父模組就不會載入子模組。
包目錄之間只能使用.點號作為間隔符,表示模組及其子模組之間的層級關係。
模組也是封裝,如同類,函式,不過其能夠封裝變數、類、函式。
模組就會名稱空間,其內部的頂層識別符號,都是其屬性,可以通過__dict__或者dir()檢視。
包也是模組,但是模組不一定是包,包是特殊的模組,是一種組織方式,包含__path__屬性。
From ison import encoder: 不可以執行ison.dump
Import json.encoder: 可以執行json.dump
12、絕對匯入和相對匯入
絕對匯入:
在import 語句或者from匯入模組,模組名稱前不是以.開頭的,絕對匯入總是去搜索模組搜尋路徑中找。
相對匯入:
包內使用相對的路徑。且只能用在from語句中,不在包外用。在頂層用絕對匯入。
使用 ...上一級的上一級 ..上一級 .同一級。不在頂層使用相對匯入。
對於包來說正確的使用方式還是在頂級模組中使用這些包。
13、訪問控制
下劃線或者雙下劃線開頭的模組能夠被匯入,都可以成功的匯入,因為他們都是合法的識別符號,都可以用作模組名。
模組內的識別符號:
普通變數,保護變數,私有變數,特殊變數,都沒有被隱藏,也就是說模組內沒有私有的變數,在模組定義中不做特殊處理。
使用from可以訪問所有變數。
14、from ... import * 和__all__
使用from ... import *匯入。起到作用的是from m import *。所有共有成員非子成員的全部匯入。會影響當前的名詞空間。
定義了__all__ 使用all指定匯入的是哪些。
使用__all__是一個列表,元素是字串,每個元素都是一個模組的名稱。(變數名)
15、包和子模組
如何訪問到一個模組中的一個變數呢:
1)直接匯入整個模組 import
2)直接匯入from 模組中import 需要的屬性。
3)利用from 模組import* 利用__all__指定需要匯入的名稱。
4)在__init__.py中增加from . import 模組。
16、總結
一、使用from xyz import *匯入
(1)如果模組沒有__all__,from xyz import * 只是匯入非下劃線開頭的模組的變數,如果是包,子模組不會匯入,除非在__all__中設定,或者使用__init__.py檔案中使用相對匯入。
(2)如果模組有__all__,from xyz import * 只匯入__all__列表中指定的名稱,哪怕是這個名詞是下劃線開頭或者子模組。
(3)From xyz import * 方式帶入,使用簡單,但是其副作用是匯入大量不需要的變數,甚至造成名字的衝突,而__all__可控制被匯入模組在這種匯入方式下能夠提供的變數名稱,就是為了阻止from xyz import *匯入過多的模組變數,從而避免衝突,因此,編寫模組時候,儘量加入__all__
二、From module import name1,name2匯入
匯入時明確的,匯入子模組,或者匯入下劃線開頭的名稱,可以有控制的匯入名和其對應的物件。
17、模組變數的修改
模組物件是同一個,因此模組的變數也是同一個,對模組變數的修改,會影響所有者,除非萬不得已,不要修改模組的變數。
猴子補丁,也可以通過打補丁的方式,修改模組的變數,類和函式等內容。