Python模塊化

Python的包和模塊

module類型

在Python中,使用import關鍵字導入一個包或者模塊,模塊是一個名爲module的類型,只有模塊類型才能夠直接使用import導入。首先是導包過程。python

print('導入前:', dir())   # 導包前全局變量
import os
print('導入後:', dir())   # 導包後全局變量

-----輸出結果-----#省略
導入前:全局變量中沒有"os"
導入後:出現了"os"

這說明全局變量中出現了os標識符,這也是咱們爲何可使用os.chdir()等經過os標識符進行成員訪問的緣由。bootstrap

再看一下os標識符的類型及os指向的這個對象。緩存

print(type(os))
print(type(os.path))
print(global()["os"])  # 收集全局變量,查看os對象信息

-----輸出結果-----
<class 'module'>
<class 'module'>
<module 'os' from 'E:\\Python\\lib\\os.py'>

上面結果顯示osos.path是一個module類型,這也是os可使用import導入的緣由。只有module類型才能被導入。同理os.path也能夠直接被導入。ide

print(dir())
import os.path
print(dir())
-----輸出結果-----#省略
導入前:全局變量中沒有"os" 或者 "os.path"
導入後:出現了"os",可是沒有"os.path"

os.path並非一個合法的標識符(字符,下劃線,數字),因此這個os.path對象沒有被變量接受,可是咱們以後須要能訪問這個os.path對象 ,因此python爲咱們保留了os變量指向os模塊。咱們纔可使用os.path的方式訪問。固然咱們使用別名的方式os.path就能夠被接收了。函數

import os.path as osp
osp.dirname(".")           # osp 即指向 os.path模塊對象.調用它全局空間中的dirname(".")函數                   
print(type(osp))           # <class 'module'>
print(globals()["osp"])    # <module'ntpath'from'E:\\Python\\lib\\ntpath.py'>

osp直接指向了咱們須要os.path模塊,咱們能夠osp進行訪問,os變量也就沒有意義,因此此時的全局變量總並不存在os這個全局變量,可是os這個模塊確實被import加載到內存空間中。只是在該模塊中沒有對應的表示符進行對應。學習

模塊搜索順序

sys.path中記錄了模塊的全部順序,而且能夠程序執行時候,導入了哪些類,全部被導入的類都會緩存在sys.modules中,調用便可查看。測試

import sys
print(*sys.path, "\n", *sys.modules, sep="\n")
--------輸出結果------
'''
D:\學習資料\練習項目\pycharm基礎練習\myproject   # 當前路徑
D:\學習資料\練習項目\pycharm基礎練習             # pycharm 建立的項目環境
E:\Python\python36.zip                       # 如下爲Python安裝路徑下的各個路徑,能夠爲zip,egg文件
E:\Python\DLLs
E:\Python\lib
E:\Python
E:\Python\lib\site-packages
'''

{'builtins': <module 'builtins' (built-in)>,   # 已經被導入的各個模塊信息
'sys': <module 'sys' (built-in)>, 
'os': <module 'os' (built-in)>, 
'_frozen_importlib': <module 'importlib._bootstrap'
...
'_tracemalloc': <module '_tracemalloc' (built-in)>, 
'myproject': <module 'myproject' (namespace)>}

.egg文件是由setuptools庫建立的包,第三方庫經常使用的格式,添加了元數據信息的zip文件。ui

使用from導入

from ... import ...從一個模塊中導入對象,這個模塊多是一個.py文件也多是一個包(目錄),導入的對象能夠是包或者模塊下的函數,類,模塊等全局變量標識符,而且導入後能夠直接使用import後的原變量標識符進行訪問,而不須要加上模塊前綴。fromimport後指定的內容都將被加載到內存。spa

from os import path          # 從os 導入 path模塊,並可使用path全局變量直接訪問
from os.path import dirname  # 從os.path 模塊導入 dirname方法,能夠直接經過dirname標識符訪問該方法對象。

from os.path import *        # 默認從os.path模塊導入全部公共變量(非下劃線開頭),並直接使用原變量名做爲該包下的全局變量

使用第三種方式容易形成該包中的全局變量被導入包中全局變量覆蓋。除非清楚的知道兩個包中的變量不會重名,不然儘可能不使用。線程

使用 from ... import *默認導入的是公開變量(非___打頭的變量),非公開變量導入必須手動才能訪問,或者添加到all屬性中才能使用*導入

from os import _abc, __abc   # 直接指定可導入,* 默認不含該類屬性

__all__

在被導入模塊中,能夠添加__all__屬性來指定該模塊默認的所有可向外導出的模塊。配合from ...import * 使用。__all__屬性是一個字符串列表,這些字符串必須與該全局空間中的全局變量名對應,或者與該包下的子包名對應。

__all__ = ["x", "y", "_z"]

x = 1
y = 2
z = 3
_z = 4

這個包被其餘包使用 *導入時,導出__all__中對應的x, y,_z變量,實現了咱們手動控制變量的導出。

包導入時屬性優先,沒有這個屬性纔會判斷是否有這個模塊。因此若是屬性名和模塊名衝突,該模塊將沒法再被導出。

Python中的包就是一個文件夾,文件夾下放各個模塊的.py文件或者一個子包,方便管理。在包上也是能夠寫代碼,這就是須要這個包下的__init__.py文件,這個文件屬於這個包,當咱們僅僅導入一個包時候,就是導入這個包下的__init__.py文件中的全局變量所指定的內容。並不會自動的導入這個包下的子模塊。

包文件和模塊文件

包模塊的代碼託管到包下的__init__.py文件中,即便包沒有__init__文件仍然能夠被導入,但並無導入任何內容,這種狀況下只能經過這個包名去尋找這個包下的模塊文件。使用from ... inport ...導入子模塊中的內容。

包是一個稍微特殊的模塊對象,包的屬性有['__doc__', '__loader__', '__name__', '__package__', '__path__', '__spec__'],根據__package__屬性能夠判斷它是不是一個包,該屬性爲None或者不存在表示不是包。

包下的模塊爲這個包的子模塊,他們之間能夠經過package.mod_name進行訪問,前提是這個模塊已經被導入,可使用from package import mol_name或者import package.mol_name進行導入,兩種導入效果相同,只是導入空間的變量名不一樣,使用形式不一樣。可是隻import package沒法自動導入它的子包,package.mul_name將不能訪問。

相對導入和絕對導入

使用import導入模塊順序在sys.module記錄,默認優先從執行文件的當前目錄中尋找,沒法找到再去Python安裝文件中的庫中尋找。這是一種絕對路徑導入。

相對導入一般在一個包中使用,方便的將包中的各個模塊進行關聯,這種關聯只受包內文件的相對位置約束,然而對於一個包咱們一般不會去幹涉內部的文件關係。相對導入原則遵循文件系統使用原則,即便是包__init__.py文件,這時屬於這個包下的一個子文件,等同於這個包下的其餘子文件。

符號 示例
. .m 當前文件夾中的m模塊
.. ..m 上一級文件中的m模塊
... ...m 上上一級文件中的m模塊
from .m import a     # 將會在當前目錄下找 m 文件並導入a
from ..m import a    # 訪問該文件的上一層目錄找 m 文件並導入a
from .m.m1 import a  # 當前文件所在目錄中的m包下的 m1文件並導入a

以上導入方式只能在包中使用,(任何一個相對導入的路徑不能涉及主包所處的當前目錄)這些文件不能做爲主模塊直接運行,它是一種包內部的關係的創建方式,直接運行將報錯。

命名空間

當咱們導入一個模塊時,咱們能夠訪問這個模塊是由於在當前的做用域內部,建立了一個與模塊名同名的全局變量,才能夠經過這個全局變量對這個模塊進行訪問。因此咱們是否能使用一個模塊,須要兩個條件:

  • 一、該模塊被加載到內存中
  • 二、能夠找到一個標識符直接和這個模塊對象關聯或者能夠經過其餘標識符的成員訪問方式訪問到這個模塊對象。

不一樣的導入文件方式在這個命名空間中建立的標識符是不一樣的

import os        # 只導入os模塊(os下的__init__.py中的內容),建立os標識符
import os as o   # 只導入os模塊,建立o標識符

import os.path   # 同時導入os 和 os.path,只建立 os標識符,指向os模塊。os.path可訪問
import os.path as osp  # 同時導入os 和 os.path,只建立 osp標識符,指向os.path模塊,os模塊在內存,可是沒法訪問,無標識符

from os import path   # 同時導入os 和 os.path,只建立path標識符,指向os.path。os沒法訪問,無標識符
from os import *      # 同時導入os中全部公開變量,若是有__all__屬性,導入指定的內容,建立全部 * 指代的標識符,不會建立os

form .m import a   # 在包內導入, m 和 a 均導入,m標識符可能存在,可以使用 dir查看確認再使用,a 標識符存在

自定義模塊

模塊命名規範
在導入模塊時,會使用模塊名做爲標識符,這就要求模塊名知足標識符的命名規範,這樣才能夠被導入。而且模塊一幫使用小寫字符,可使用下劃線分割。

模塊重複導入

模塊被加載一次後會在內存中緩存這個模塊對象,這個模塊的辨識信息被緩存到sys.modules中。導入模塊時候,首先是從sys.modules中查詢該模塊是否已經被加載,若是已存在,並不會從文件中讀取,而是直接使用內存中已存在的這個模塊對象,不然import會IO搜索該模塊的文件,編譯,執行這個模塊並加載大內存。

緩存的模塊被保存在sys.module中,包括本身,其中還包括解釋器運行須要的模塊。
自定義模塊a/b/c路徑,若是隻導入a模塊import a,直接使用a.b是有風險的,該模塊沒有被加載,只有使用import關鍵字時,解釋器纔會模塊進行加載,而不會自動的加載。

模塊運行

當咱們執行一個.py文件,此時解釋器會啓動去執行,但在執行這個文件以前,解釋器還須要其餘的工做,好比導入一些包,例如io,將咱們的.py文件讀入內存,或者build-in,由於咱們可能使用到了這個內建函數。還有其餘不少準備流程,當這個工做完成後,纔會執行咱們本身的.py文件。而這個.py文件也會被命名爲__main__包。查看這個模塊名,顯示爲__main__

print(__name__)       # __main__

除解釋器執行的代碼外,這個模塊將會做爲最外層的代碼塊,這個模塊的結束表明該線程的結束。也被稱做頂層代碼。頂層代碼包的"name"屬性爲__main__,其他模塊的__name__屬性爲模塊名。因此測試代碼一般這樣寫

if __name__ == "__main__":
    # test code

只有這個模塊直接執行時,if 中的測試代碼纔會執行。

相關文章
相關標籤/搜索