1. 程式人生 > >Python模組化

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、模組變數的修改

模組物件是同一個,因此模組的變數也是同一個,對模組變數的修改,會影響所有者,除非萬不得已,不要修改模組的變數。

猴子補丁,也可以通過打補丁的方式,修改模組的變數,類和函式等內容。