第七章:模組類庫和檔案操作
文章目錄
第一節:關於模組和類庫
使用系統標準庫
什麼是模組?
可以說,一個 xxx.py 就是一個模組。
什麼是類庫?
裝有很多個模組的資料夾包,可以稱為一個類庫,更多的時候,這些python檔案都是有機關聯的。
有了模組和類庫,我們可以輕鬆地站在巨人的肩膀上進行程式設計,例如我們使用的系統標準庫
、第三方類庫
甚至是自己寫的類庫
- 匯入外部模組
從外部匯入模組,有匯入模組和匯入成員兩種方式,具體包括:
方式一:import path.module
#匯入模組
方式二: from path.module import member1,member2,…
或者 :from path.module import *
#匯入指定模組下的全部成員
path
指代模組所在的路徑
module
是模組,模組即xxx.py
member
可以是變數、函式、類、以及包;
機智的匯入:
一個機智式的匯入方式為:直寫出需要的模組名或成員名,此時系統報語法錯誤,游標放在報錯處,使用【alt】+【enter】
快捷鍵,系統會自動識別和匯入;
- 什麼是包
含有
_ init _.py
的資料夾稱之為【包】;
在_ init .py中,我們可以為包中的其它模組做一些全域性初始化的工作;
_ init .py可以視為一個【與包名同名的模組】,引入 _init _.py中的成員方式為:
①from 包名 import member
包資料夾中的xxx.py可以視為包模組的成員member,它的匯入方式可以是:
①import 包名.xxx
,此時包被視為一個普通資料夾路徑
②from 包名 import xxx
,此時包被視為一個模組,xxx.py被視為一個成員
- 路徑path
path是相對路徑
: 相對於【系統庫標準庫根目錄】或【第三方庫根目錄】或【當前工程根目錄】;
系統標準庫根目錄所在的位置為:直譯器安裝目錄/Lib/
例如:C:\Python36\Lib;
第三方庫根目錄所在位置為:直譯器安裝目錄/Lib/site-packages/
例如:C:\Python36\Lib\site-packages;
如果是匯入工程內的模組,則相對路徑的根目錄為當前工程目錄;
- 迴歸測式
迴歸測試的作用是避免自動觸發外部模組的業務邏輯
當我們匯入一個自定義模組的時候,如果這個模組在成員定義以外,還包含一定的業務邏輯程式碼,則這些程式碼必須被寫在【迴歸測試】中;
如果模組的業務邏輯不寫在迴歸測試中,則外界在將其匯入時(無論是匯入模組還是匯入成員),這些業務邏輯都會被自動觸發;
我們通常會在迴歸測試中,測試一下自己寫的函式和類是否正確
迴歸測試的寫法:
if __name__ == '__main__':
# 一些業務邏輯
pass
安裝第三方庫
系統標準庫是安裝Python直譯器時,自帶安裝的一些最基本最常用的的Python類庫,它們的位置是:直譯器安裝目錄/Lib/
;
光有系統標準庫是遠遠不能滿足多樣化的開發需求的,我們還常常要使用到一些【第三方類庫】,它們的位置是:直譯器安裝目錄/Lib/site-packages/
;
- 安裝第三方類庫:
Python直譯器自帶一個包管理工具
pip.exe
,它的位置是:直譯器安裝目錄/Scripts/
,通常我們在安裝直譯器時都選擇將這個目錄放到系統的環境變數中;
安裝方式一:自動化安裝第三方類庫最簡單的方式是在【控制檯】中輸入:
pip install 包名
包管理器
pip
會自動尋找和安裝xxx及其所依賴的其它類庫
安裝方式二:在pip.exe
同目錄下還存在easy_install.exe
,我們同樣可以在【控制檯】中輸入:
easy_install 包名
pip
和easy_install
二者的區別是:pip 改善了不少 easy_install 的缺點,因此通常pip是一個更好的選擇,除非某些類庫指定使用easy_install進行安裝
pip的常用命令
安裝類庫:pip install 包名
更新類庫:pip install -U 包名
解除安裝類庫:pip uninstall 包名
檢索類庫:pip search 包名
檢視幫助:pip help
以上命令也都有對應的easy_install版本,其功能是相同的
方式三:使用PyCharm安裝和管理類庫
開啟IDE的
設定
,在工程直譯器欄目
中,我們可以通過點選如圖所示的“+”和“-”來執行類庫的安裝與解除安裝;
點選“+”後,在彈出的搜尋框中輸入類庫名稱
,在檢索成功後點擊install
按鈕,即可自動化完成安裝;
還更多圖形化功能,如藍色升級箭頭實現升級、安裝時勾選安裝到使用者的第三方路徑等
無論使用哪一種方式進行第三式庫的安裝,安裝效果是一樣的,安裝成功後pycharm就可以簡單使用了
使用自己寫的類庫
- 將模組和包持久化為類庫
一個複用率很高的本地的原始碼目錄,我們可以考慮將其持久化為類庫,這樣就可以在所有工程中進行匯入和使用;
持久化的方式很簡單,就是將這個資料夾拷貝到【系統庫標準庫根目錄】或【第三方庫根目錄】中,通常如無特殊必要,我們選擇放在【第三方庫根目錄】中,即直譯器安裝目錄/Lib/site-packages/
;
以後就可以像匯入系統標準庫或安裝的第三方類庫一樣,使用我們自己定義的類庫了;
當然,如果願意共享,以後還可以選擇將這部分程式碼,附帶一些說明文件,共享到開原始碼管理平臺如github上去,供全世界的程式設計師使用;
第二節:關於時間模組
time模組
- Epoch與Unix時間戳
【Epoch】 是計算機元年,指的是一個特定的時間:
1970-01-01 00:00:00
**
【Unix時間戳】 是指從元年到某個時間點所經歷的秒數
- 時間佔位符
佔位符 | 格式化意義 | 佔位符 | 格式化意義 |
---|---|---|---|
%Y: | 表示年份 | %U: | 周,在當年的週數當年的第幾周 |
%m: | 表示月份([01,12]) | %c: | 日期時間的字串表示。 |
%d: | 在這個月中的天數 | %x: | 日期字串(如:04/07/10) |
%a | 星期的簡寫 | %X: | 時間字串(如:10:43:39) |
%A | 星期的全寫 | %w: | 今天在這周的天數,範圍為[0, 6],6表示週日 |
%b | 月份的簡寫。 | %W: | 周,(當年的第幾周) |
%B | 月份的全寫。 | %j: | 在年中的天數 [001,366] |
%M: | 分鐘([00,59]) | %S: | 秒(範圍為[00,61],不是0-59) |
%p: | AM或者PM | %f: | 微秒範,圍[0,999999 |
%H: | 小時(24小時制,[0, 23]) | %z: | 與utc時間的間隔(本地時間,返回空字串) |
%I: | 小時(12小時制,[0, 11]) | %Z: | 時區名稱(本地時間,返回空字串) |
- 時間操作的幾個常用API
time.time()
—— 元年距今時間戳
time.sleep(s)
—— 睡眠
time.localtime(s)
—— 當地時間
time.asctime(tuple)
—— 將時間元組轉為美式的時間字串
time.ctime(s)
—— 將時間戳轉為時間字串
time.strftime(foramt, tuple)
—— 格式化時間
time.strptime(string,format)
—— 將時間字串轉換為時間元組
time.mktime(tuple)
—— 將時間元組轉換為時間戳
例:
import time
# 元年距今時間戳
print('time.time():',time.time())
# 當地時間localtime,不傳參預設當前時間戳,返回結構化時間(時間元組)
print(time.localtime())
# 昨日此刻時間
print(time.localtime(time.time()-24*3600))
# 美式化時間asctime,將時間元組轉為時間字串
print(time.asctime(time.localtime()))
# 美式化時間ctime,將時間戳轉為時間字串
print(time.ctime())
# 格式化成2016-03-20 11:45:39形式
print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) # 2018-03-22 21:10:48
# 格式化成Sat Mar 28 22:24:24 2016形式
print(time.strftime("%a %b %d %H:%M:%S %Y", time.localtime()))
print(time.strftime("%z", time.localtime()))
print(time.strftime("%a %A", time.localtime()))
print(time.strftime("%b %B", time.localtime()))
print(time.strftime("%c", time.localtime()))
print(time.strftime("%I", time.localtime()))
print(time.strftime("%p", time.localtime()))
a = "Sat Mar 28 22:24:24 2016"
# 將時間字串轉換為時間元組
print(time.strptime(a, "%a %b %d %H:%M:%S %Y"))
# 將時間元組轉換為時間戳
print(time.mktime(time.strptime(a, "%a %b %d %H:%M:%S %Y")))
執行結果:
datetime模組
time模組提供的功能是更加接近於作業系統層面的。通讀文件可知,time 模組是圍繞著 Unix Timestamp 進行的。time模組表述的日期範圍被限定在 1970 - 2038 之間,如果你寫的程式碼需要處理在前面所述範圍之外的日期,那可能需要考慮使用datetime模組更好。
在系統的標準庫裡面,有個模組名為datetime,而datetime模組裡還有一個類,也名為datetime,平時匯入的時候應注意,匯入的應是模組,而不是類,
- datetime的幾個常用API
在標準庫的datetime模組裡有很多方法,這裡主要介紹datetime.datetime的方法
datetime.datetime.now()
—— 當前的日期和時間
datetime.datetime.now().year
—— 當前的年份
datetime.datetime.now().month
—— 當前的月份
datetime.datetime.now().day
—— 當前的日期
datetime.datetime.now().hour
—— 當前小時
datetime.datetime.now().minute
—— 當前分鐘
datetime.datetime.now().second
—— 當前秒
相對於time模組來說,datetime 比 time 高階了不少,也更簡潔,返回值也更友好一些。可以理解為 datetime 基於 time 進行了封裝,提供了更多實用的函式。
例:
import datetime
now = datetime.datetime.now()
print("當前的日期和時間是%s" % now)
print("當前的年份是%s" % now.year)
print("當前的月份是%s" % now.month)
print("當前的日期是 %s" % now.day)
print("當前小時是%s" % now.hour)
print("當前分鐘是%s" % now.minute)
print("當前秒是%s" % now.second)
執行結果:
calendar日曆模組
- 日曆模組的幾個API
calendar.isleap(year)
—— 判斷某年是否閏年
calendar.leapdays(fromYear, toYear)
—— 判斷某段時間的閏年數
calendar.weekday(year, month, day)
—— 指定日期的星期
calendar.monthrange(year, month)
—— 當月第一天的星期和當月天數
calendar.Calendar()
—— 日曆物件
calendar.Calendar().itermonthdates(year, month)
—— 具體日期的日曆生成器
calendar.setfirstweekday(weekNb)
—— # 設定一週的起始星期
calendar.month(year, month)
—— 指定年月的日曆表
例:
import calendar
# 閏年判斷
print(calendar.isleap(2000))
print(calendar.isleap(2018))
print(calendar.leapdays(2000, 2019)) # 判斷2000-2018年之間閏年數
# 返回指定日期的星期(0-6分別代表週一到週日)
print(calendar.weekday(2018, 11, 16))
# 返回當月第一天的星期和當月天數
print(calendar.monthrange(2018, 11))
# 日曆生成器
c = calendar.Calendar() # 建立日曆物件
monthGenerator = c.itermonthdates(2018, 11) # 獲得2018年11月的日曆生成器
while True:
try:
print(next(monthGenerator)) #該生成器會從該月第一天所在星期開始生成日期
except StopIteration:
break
# 設定和列印日曆
calendar.setfirstweekday(0) # 設定周是以星期一開始
mydate = calendar.month(2018, 11) # 列印2018年11月的日曆
print(mydate)
執行結果:
第三節:關於 os 檔案模組
使用os模組的操作
os模組是系統標準庫模組;
os模組常用的檔案操作包括:
1、建立單級或多層級的資料夾、
2、刪除單級或多層級空資料夾、
3、刪除檔案;
os模組中的path子模組常用檔案操作:
1、判斷路徑是否存在
2、判斷路徑是否是檔案/資料夾;
3、刪除有內容的資料夾,這時要使用另一個標準庫模組:shutil.rmtree(path)
;
- 相對路徑和絕對路徑
【絕對路徑】是從具體碟符出發的路徑;
【相對路徑】是從當前py檔案位置出發的路徑;
相對路徑中以一個點“./”
代表當前位置,兩個點“../”
代表上一級資料夾路徑;
順斜線“/”
是Linux和Windows下通用的路徑分隔符,反斜線則只適用於Windows
;
在使用反斜線作為路徑分隔符時,通常在路徑前面加一個“r”
代表所有的反斜線都不是轉義字元;
- 檔案操作
os.mkdir()
—— 建立單級資料夾
os.makedirs()
—— 建立層級資料夾
os.rmdir()
—— 刪除空資料夾
shutil.rmtree()
—— 刪除有內容的資料夾
os.remove()
—— 刪除檔案
os.removedirs()
—— 刪除資料夾
例:
import os
import shutil
# 建立單級資料夾(在當前的上一層資料夾位置建立)
os.mkdir(r"../res")
os.mkdir(r"../res/doc")
# 建立層級資料夾(在當前的上一層資料夾位置建立)
os.makedirs(r"../res/img/large")
# 刪除空資料夾(在當前的上一層資料夾位置刪除)
os.rmdir(r"../res/img")
# 刪除有內容的資料夾
shutil.rmtree(r"../res/img")
# 刪除檔案或資料夾
os.remove(r"../res/doc/1.txt")
os.removedirs(r"../res/doc") # 刪除一整條空路徑
除了建立和刪除檔案,還能對檔案進行判斷
os.path.exists()
——判斷路徑是否存在
os.path.isfile()
—— 判斷路徑是否是檔案
os.path.isdir()
—— 判斷路徑是否是資料夾
例:
import os
# 判斷路徑是否存在
fileExists = os.path.exists(r"./mg.py")
print(fileExists)
# 判斷路徑是否是檔案/資料夾
isFile = os.path.isfile(r"./mg.py")
isDir = os.path.isdir(r"./mg.py")
print(isFile)
print(isDir)
執行結果:
第四節:檔案操作
檔案指標
一、檔案指標類似於游標位置;
二、不同點在於:游標每次移動的是一個字元,而檔案指標每次移動一個位元組
;
三、通過file.tell()
可以獲取當前檔案指標位置;
四、通過file.seek(n)
可以將檔案指標移動到任意位置;
五、檔案的讀寫都是從當前指標位置向後進行
的;
六、在file.truncate(size)
中,則是丟棄檔案指標以後的內容,進行斷尾式的擷取;
七、在不同字符集中,每個字元所佔的位元組數是不同的;
例:utf-8編碼下的每個字元佔用檔案指標位數(也就是位元組數):
____1、對於字母和數字,每字元指標移動1位(即佔用1位元組)
____2、對於空格,每字元指標移動1位(即佔用1位元組)
____3、對於\n,每字元指標移動2位 (即佔用2位元組)
____4、對於漢字,每字元指標移動3位 (即佔用3位元組)
檔案的基本讀寫模式 r、w、a、x
通過系統內建函式
open()
我們可以開啟一個檔案,得到檔案流物件file
(後文所有file均指檔案流物件);
位置引數name代表檔案路徑,可以是絕對路徑或相對路徑;
encoding引數指定檔案的編碼方式,預設為utf-8
,編碼錯誤會導致亂碼;
mode引數代表以什麼模式開啟檔案,如果開啟的是字元流檔案,那麼有四種基本模式;
1、r
(只讀模式)
2、w
(覆寫模式)
3、a
(追加模式)
4、x
(創寫模式);
注意資料夾路徑必須是存在的,否則會報FileNotFoundError錯誤
;
注意:讀寫操作結束,記得關閉已開啟的檔案流,以釋放資源
;
- 只讀模式 r
open()通過定義引數只讀模式
r
開啟的檔案,表示該檔案只能讀不能寫
,強寫會拋異常;
以只讀模式開啟的檔案,檔案指標(暫時可以簡單理解為游標)在檔案開頭位置
;
通過file.read(n)
可以讀入指定數量的字元,n不寫預設為讀入全部
;
在讀入的過程中,檔案指標會相應的向後移動n個字元位置
;
例:在當前目錄下建立了一個myfile.txt檔案,並寫下了一首詩,現在以只讀模式開啟
# 以只讀模式開啟檔案
file = open(r"./myfile.txt", "r", encoding="utf-8")
# file的型別
print(type(file))
# 讀入檔案內容
content = file.read(4) # 讀入4個字元
# content = file.read()#不寫引數表示讀入全部
print(content)
# 嘗試寫入資料(只讀模式無法寫入,會報錯)
# file.write("2018年11月13日") #io.UnsupportedOperation: not writable
# 關閉檔案流,注意每次開啟操作後一定要關閉
file.close()
執行結果:
- 覆寫模式 w
1、以覆寫模式
w
開啟的檔案,在檔案開啟的一剎那檔案內容將被清空
;
2、使用file.write(text)
向檔案中寫入一個字串;
3、使用file.writelines(strlist)
向檔案中寫入一個字串列表,每個字串元素單獨佔據一行;
注意:以覆寫模式開啟的檔案是不可讀的,強讀會拋異常
;
例:同樣以上面的file.txt為例,此時裡面已經有一首詩《望天門山》
# 以覆寫模式開啟檔案,會清空檔案內容(請謹慎)
file = open(r"./myfile.txt", mode="w", encoding="utf-8")
# 寫入內容(一個字串)
file.write("鋤禾日當午\n汗滴禾下土\n一本小破書\n看了一下午\n\n")
# 寫入內容(一個字串列表)
file.writelines(["鋤禾日當午\n", "汗滴禾下土\n", "一本小破書\n", "看了一下午\n"])
# 嘗試讀入檔案(不可讀)
# file.read() #io.UnsupportedOperation: not readable
# 關閉檔案流
file.close()
執行完再次手動開啟myfile.txt,發現原本內容已清空,並寫入了兩首詩
- 追加模式 a
已追加模式a開啟的檔案,檔案指標位於
檔案的末尾
;
此時向檔案中寫入內容,將以追加的方式寫入,原來的內容不會被刪除
;
例:以之前寫入了《望天門山》的myfile.txt為例,以追加模式,只是向下繼續增加內容
# 追加模式開啟檔案
file = open(r"./myfile.txt", "a", encoding="utf-8")
# 追加一個字串
file.write("\n從前\n從前不洗澡\n處處蚊子咬\n夜裡長泡泡\n一看真不少\n")
# 關閉檔案流
file.close()
執行完再次手動開啟myfile.txt,發現原本內容不變,並往下新添加了一首詩
- 創寫模式 x
以創寫模式
x
開啟的檔案必須是一個不存在的檔案
;
如果檔案已經存在會報FileExistsError
異常;
使用創寫模式結合try…except…
我們可以保證開啟的都是不存在的檔案,而不會覆蓋已有的檔案;
這裡還要提一個
file.flush()
的方法:
這是一個保證資料完整性的方法,一般的檔案流操作都包含緩衝機制,write方法並不直接將資料寫入檔案,而是先寫入記憶體中特定的緩衝區
。
flush方法是用來重新整理緩衝區的,即將緩衝區中的資料立刻寫入檔案,同時清空緩衝區。
正常情況下緩衝區滿時,作業系統會自動將緩衝資料寫入到檔案中。
至於close方法,原理是內部先呼叫flush方法來重新整理緩衝區,再執行關閉操作,這樣即使緩衝區資料未滿也能保證資料的完整性。
如果程序意外退出或正常退出時而未執行檔案的close方法,緩衝區中的內容將會丟失。
例:
# 以創寫模式開啟一個並不存在的檔案
file = open(r"./yourFile.txt", "x", encoding="utf-8")
# 向文件中寫入資料
file.write("鋤禾日當午\n汗滴禾下土\n一本小破書\n一看一下午\n")
# 讓寫入(快取在緩衝區中)立刻生效
file.flush()
# 關閉檔案流
file.close()
執行結果:自動生成檔案yourFile.txt,並寫入了內容
檔案的位元組讀寫模式 rb,wb,ab,xb
- 位元組流檔案的讀寫
通過基本讀寫模式(只讀r,覆寫w,追加a,創寫x)我們可以方便地操作字元流檔案的讀寫;
對於【位元組流檔案】(一切非字元型檔案,包括媒體檔案、可執行檔案、壓縮包、等等)
,我們則需要使用位元組讀寫模式
來進行相應的讀寫操作;
與基本讀寫模式對應,位元組讀寫模式有四種:rb,wb,ab,xb
,分別對應位元組只讀、位元組覆寫、位元組追加、位元組創寫;
位元組讀寫模式與普通讀寫模式所不同的,僅僅在於讀入和寫出的內容都是位元組形式,而非以字串形式
;
首先開啟檔案,看看檔案的位元組流
例:
# 以位元組只讀模式開啟一張圖片檔案
myFile = open(r"./image/timg.jpg", "rb")
myBytes = myFile.read()
print(myBytes)
執行結果:
例:拷貝圖片
圖片屬於媒體檔案,不論讀寫都應以相應的位元組模式來操作;
我們以位元組只讀方式開啟被拷貝的檔案,以位元組創寫模式建立並開啟一個要拷貝到的目標檔案;
通過file.read(size)
我們可以讀出指定位元組數的內容,預設為讀出全部;
通過file.write(content)
我們可以寫入指定的內容(字元位元組都可以);
最終記得關閉本體和目標兩個檔案;
# 以位元組只讀模式開啟圖片檔案
myFile = open(r"./image/timg.jpg", "rb")
myBytes = myFile.read()
# print(myBytes)
# 以位元組創寫模式開啟圖片檔案
yFile = open(r"./image/timg2.jpg", "xb")
#把timg.jpg的位元組流寫入檔案
byteCount = yFile.write(myBytes)
print("寫入的位元組數量是%d" % (byteCount))
# 分別關閉檔案流
myFile.close()
yFile.close()
執行結果:成功地複製了一張圖片
檔案的加強讀寫模式 r+、w+、a+
字元流檔案的四種基本讀寫模式:r/w/a/x,都是隻能讀或只能寫的,強讀強寫會報錯
接下來要介紹的【加強讀寫模式】則是全部是可讀可寫的
;
它們分別是:
1、讀優先的r+
,
2、覆寫優先的w+
,
3、追加優先的a+
;
它們之間的區別在於檔案開啟時,檔案指標的位置在哪;
- 讀優先的加強模式 r+
檔案開啟時,檔案指標位於
0的位置
,便於從頭開始讀取檔案;
因此我們稱它是讀優先
的;
例:
file = open(r"./myfile.txt", "r+", encoding="utf-8")
# 開始讀取時的指標位置'
print("當前檔案指標在%d" % (file.tell()))
# 讀取4個字元。對於ASCII字元,每字元指標移動1位,對於漢字,每字元指標移動3位
print(file.read(4))
print("當前檔案指標在%d" % (file.tell()))
# 繼續讀取4個字元
print(file.read(4))
print("當前檔案指標在%d" % (file.tell()))
# 繼續移動檔案指標到指定位置
file.seek(0)
print("當前檔案指標在%d" % (file.tell()))
print(file.read(8))
# 寫入內容
# 寫入之前應明確地seek到指定位置,否則會追加在末尾,但是指定位置也有缺點,就是會覆蓋後面的文字
#此處移動到倒數第二個文字之後
file.seek(196)
file.write("唐詩三百首")
#關閉檔案流
file.close()
執行結果:
檔案內容執行後結果:我們發現,最後一個字被覆蓋了。
我們發現,最後一個字被覆蓋了。當然也可以不seek到指定位置,此時就會把內容追加在末尾
- 覆寫優先的加強模式 w+
檔案以w+開啟時,內容會被
清空
,自然檔案指標也就位於0的位置;
這種模式先清空內容以便覆寫,因此我們稱它是覆寫優先
的;
例:
file = open(r"./myfile.txt", "w+", encoding="utf-8")
# 可讀可寫,開啟檔案時內容被清空
print('此時指標在:',file.tell())
print('內容是:',file.read())
# 寫入內容
file.write("別人笑我太瘋癲,我笑他人看不穿")
print('寫入內容後指標在:',file.tell())
# 移動指標
file.seek(0)
print('此時指標在:',file.tell())
print('內容是:',file.read())
print(