理解模塊和包的基本概念
理解模塊的導入過程
熟悉sys模塊和os模塊中的一些重要操作
當代碼量比較大的時候, 我們最好把代碼拆分成一些有組織的代碼片段.
每個代碼片段裡面包含一組邏輯上有關聯的函數或者類.
每一個片段裡放在一個獨立的文件中. 這樣的片段就稱為模塊(module)
使用 import
可以在一個Python文件中引入其他的模塊.
回憶我們C語言中, 如果一個文件想使用其他文件的功能, 通過 #include 的方式來引入一個頭文件.
C語言的 #include本質上是文本替換. 因此可能會導致一系列的問題**(重復包含, 包含順序相關).**
所以通常加上:
#pragma once //防止頭文件重復包含
Python的模塊則避免了C語言中的這些問題. import 實際上是創建了一個模塊對象. 通過這個對象來訪問
模塊中的具體方法.
import os
print(type(os)) #<class 'module'> 模塊
print(id(os)) #2191830213400 有id,所以也是一個對象
我們嘗試 import 一個模塊名時, Python解釋器必須要知道模塊文件的路徑
例如, 我們此處有一個add.py, 那麼對應的模塊名就是add (去掉後綴)
sys 是python標准庫自帶的模塊,一些和python解釋器相關的模塊
Python從sys.path中查找模塊.
我們可以看到, sys.path其實是一個列表. 裡面裡面的內容是Python的一些默認搜索路徑. 如果我們想再加
入一些新的路徑, 直接修改sys.path即可. ->sys.path.append('....')
import sysq
print(sys.path)
#執行結果:
['E:\\Python文件存儲', 'E:\\pythonProject1', 'C:\\Users\\Mango\\AppData\\Local\\Programs\\Python\\Python37\\python37.zip', 'C:\\Users\\Mango\\AppData\\Local\\Programs\\Python\\Python37\\DLLs', 'C:\\Users\\Mango\\AppData\\Local\\Programs\\Python\\Python37\\lib', 'C:\\Users\\Mango\\AppData\\Local\\Programs\\Python\\Python37', 'C:\\Users\\Mango\\AppData\\Local\\Programs\\Python\\Python37\\lib\\site-packages']
import sys
for line in sys.path:
print(line)
#執行結果:
E:\Python文件存儲
E:\pythonProject1
C:\Users\Mango\AppData\Local\Programs\Python\Python37\python37.zip
C:\Users\Mango\AppData\Local\Programs\Python\Python37\DLLs
C:\Users\Mango\AppData\Local\Programs\Python\Python37\lib
C:\Users\Mango\AppData\Local\Programs\Python\Python37
C:\Users\Mango\AppData\Local\Programs\Python\Python37\lib\site-packages
import語句不一定非要寫在源代碼文件的最開始(推薦寫在最上面)
如果在多個目錄中, 都存在相同名字的模塊, 這個時候Python解釋器會從前往後順序查找, 把找到的第一個
匹配的結果進行加載.
名字沖突問題: 如果一個項目中的代碼量很大, 那麼很可能函數/類的名字會沖突.
C語言采用的方案是,給函數名加一個又丑又長的前綴
void Huichuan_AdServer_Strategy_IndexAccessor_Load()
C++中使用命名空間來解決這個問題
namespace Huichuan {
namespace AdServer {
namespace Strategy {
namespace IndexAccessor {
void Load()
}
}
}
}
Python的模塊也相當於是命名空間. 例如 os.path.exists
, 通過這樣的層級結構, 將函數的名字組織起來, 就可以有效的避免命名沖突.
import語句導入模塊, 可以一次import一個, 或者一次import多個->用,
分割
#方式1:
import os
import sys
#方式2:
import os,sys #用逗號分割
推薦使用第一種方式, 這樣代碼看起來更好看一些
import語句的順序: 我們推薦按 Python標准庫, Python第三方庫, 應用程序自定制模塊 的順序來import,
來提高我們代碼的可讀性.
模塊也是一個對象, 也有作用域這樣的概念. 在函數內部import, 那麼這個模塊的作用域就是在這個函
數內.
例子:
def func():
import sys
print('In func',sys.argv) #In func ['E:/Python文件存儲/Class01.py']
func()
print('Out func',sys.argv) #報錯 NameError: name 'sys' is not defined
既然模塊也是一個對象, 那麼也可以給這個對象賦值(相當於定義別名).
import os.path
p = os.path #起別名
print(p.exists('add.py')) #add.py這個文件是否存在 True
這樣可以一定程度上簡化我們的代碼(敲一個字符p肯定比敲一串字符os.path要方便).
前面我們使用賦值的方式, 給模塊起了一個很短的別名. 實際上使用import-as可以更方便的完成這個動作
import os.path as p
print(p.exists('add.py')) #add.py這個文件是否存在 True
import語句是直接導入一個模塊. 而有時候我們只需要用到模塊中的某一個或幾個函數, 就可以使用from-
import
from-import相當於把模塊中的名字引入了當前文件的命名空間中.可以直接使用導入的函數
from os.path import exists
print(exists('add.py')) #True
可以使用 from module import *
的方式將module模塊中的所有名字都導入進來. 不推薦這麼用.
*
:通配符
模塊導入意味著這個模塊 “被執行” , 也就是說所有頂層的代碼(無縮進部分的代碼)都會被執行到. 這通常包括函數的定義和全局變量的定義.
例子:
add.py 的內容:
''' 定義兩數相加的函數 '''
def _Add(x,y):
return x+y
print("In Add函數內部")
test.py的內容
import add
#執行結果:
In Add函數內部
這往往不是我們期望的結果(比如導入模塊是打印了一些奇怪的日志), 我們只是想使用模塊中的一些函數和變量. 因此往往我們在實現一個模塊時, 只將函數定義/類定義放到頂層代碼中.
我們寫 import module 時, 其實有兩個大的階段, 先將這個模塊的文件加載到內存中, 並創建一個對象來表示; 然後通過一個變量名來將這個模塊引入到當前的命名空間中.
如果同一個模塊(例如sys這樣的常用模塊)被import了多次, 其實加載動作只進行了一次(也就是說內存中只有一份sys的代碼), 執行動作也只進行了一次
import add
import add
#執行結果:
In Add函數內部
我們可以通過globals()函數看到全局命名空間下有幾個內置變量
print(globals())
#執行結果:
{
'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000026C824D6400>, '__spec__': None, '__annotations__': {
}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'E:/Python文件存儲/Class01.py', '__cached__': None}
其中:
_builtins_ : 內建函數模塊. 包含了open, help, doc等內建函數的定義
__name__ : **模塊名字. 如果是從這個模塊開始執行, 那麼 __name__ 值為 __main__,否則就是當前文件不包含後綴的名稱
入口文件是誰,__name__就是__name__
__file__ : 模塊的文件名
__doc__ : 模塊的文檔字符串
__package__ :模塊從屬的包名
回憶我們剛剛講過的, import的時候, 會執行這個模塊. 我們期望, 這個模塊被別人導入時, 只進行函數定義; 如果直接從這個模塊開始運行, 則在函數定義完成後再進行一些測試
例子:
''' 定義兩數相加的函數 '''
def Add(x,y):
return x+y
#測試:
if __name__ == '__main__':
print(Add(1,2))
print(Add(2,3))
判定 __name__ 是否值為 _main_ , 如果是的話, 說明是直接執行add.py, 這時候就執行測試代碼. 否則
認為add是被其他Python文件import的, 不執行測試代碼.
當我們代碼量進一步變大時, 光拆分成多個文件已經不足以滿足需求, 還需要能按照一定的目錄結構層次化的組織這些模塊. 同時包也可以解決模塊之間的名字沖突問題.
目錄也是包的一種 ,包也是模塊
如:以下面的方式組織代碼結構:
test.py
calc/
add.py
__init__.py
在calc目錄中增加一個 _init_.py 文件, calc這個目錄就成了包(Package). 可以在test.py文件中import calc中的模塊了
**_init.py_**是包的初始化文件, 會首先執行它
__init__.py 是在包加載的時候會進行執行, 負責一些包的初始化操作. 一般是空文件即可.
包中是可以嵌套其他的包的