1. 程式人生 > >第七章:模組類庫和檔案操作

第七章:模組類庫和檔案操作

文章目錄

第一節:關於模組和類庫

使用系統標準庫

什麼是模組?
可以說,一個 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 包名

pipeasy_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(