第八章 文件、異常和模塊
實際應用中,我們絕大多數的數據都是通過文件的交互完成的
8.1 文件的讀寫
8.1.1 文件的打開
- 文件的打開通用格式
with open("文件路徑", "打開模式", encoding = "操作文件的字符編碼") as f:
"對文件進行相應的讀寫操作"
使用with 塊的好處:執行完畢後,自動對文件進行close操作。
【例1】一個簡單的文件讀取
with open("E:\ipython\測試文件.txt", "r", encoding = "gbk") as f: # 第一步:打開文件
text = f.read() # 第二步:讀取文件
print(text)
我是一個測試文件
1、文件路徑
-
完整路徑,如上例所示
-
程序與文件在同一文件夾,可簡化成文件名
with open("測試文件.txt", "r", encoding = "gbk") as f: # 第一步:打開文件
text = f.read() # 第二步:讀取文件
print(text)
我是一個測試文件
2、打開模式
-
“r” 只讀模式,如文件不存在,報錯
-
“w” 覆蓋寫模式,如文件不存在,則創建;如文件存在,則完全覆蓋原文件
-
“x” 創建寫模式,如文件不存在,則創建;如文件存在,報錯
-
“a” 追加寫模式,如文件不存在,則創建;如文件存在,則在原文件後追加內容
-
“b” 二進制文件模式,不能單獨使用,需要配合使用如"rb",“wb”,“ab”,該模式不需指定encoding
-
“t” 文本文件模式,默認值,需配合使用 如"rt",“wt”,“at”,一般省略,簡寫成如"r",“w”,“a”
-
“+”,與"r",“w”,“x”,"a"配合使用,在原功能基礎上,增加讀寫功能
-
打開模式缺省,默認爲只讀模式
3、字符編碼
- 萬國碼 utf-8
包含全世界所有國家需要用到的字符
- 中文編碼 gbk
專門解決中文編碼問題
-
windows系統下,如果缺省,則默認爲gbk(所在區域的編碼)
-
爲清楚起見,除了處理二進制文件,建議不要缺省encoding
8.1.2 文件的讀取
1、讀取整個內容——f.read()
with open("三國演義片頭曲_utf.txt", "r", encoding="utf-8") as f: # 第一步:打開文件
text = f.read() # 第二步:讀取文件
print(text)
臨江仙·滾滾長江東逝水
滾滾長江東逝水,浪花淘盡英雄。
是非成敗轉頭空。
青山依舊在,幾度夕陽紅。
白髮漁樵江渚上,慣看秋月春風。
一壺濁酒喜相逢。
古今多少事,都付笑談中。
with open("三國演義片頭曲_utf.txt", encoding="utf-8") as f: # "r",可缺省,爲清晰起見,最好寫上
text = f.read()
print(text)
臨江仙·滾滾長江東逝水
滾滾長江東逝水,浪花淘盡英雄。
是非成敗轉頭空。
青山依舊在,幾度夕陽紅。
白髮漁樵江渚上,慣看秋月春風。
一壺濁酒喜相逢。
古今多少事,都付笑談中。
- 解碼模式不匹配
with open("三國演義片頭曲_utf.txt", "r", encoding="gbk") as f:
text = f.read()
print(text)
---------------------------------------------------------------------------
UnicodeDecodeError Traceback (most recent call last)
<ipython-input-6-8e9ea685585d> in <module>
1 with open("三國演義片頭曲_utf.txt", "r", encoding="gbk") as f:
----> 2 text = f.read()
3 print(text)
UnicodeDecodeError: 'gbk' codec can't decode byte 0x80 in position 50: illegal multibyte sequence
with open("三國演義片頭曲_utf.txt", "r") as f: # encoding缺省,windows系統默認爲"gbk"
text = f.read()
print(text)
---------------------------------------------------------------------------
UnicodeDecodeError Traceback (most recent call last)
<ipython-input-7-480622bc01aa> in <module>
1 with open("三國演義片頭曲_utf.txt", "r") as f: # encoding缺省,windows系統默認爲"gbk"
----> 2 text = f.read()
3 print(text)
UnicodeDecodeError: 'gbk' codec can't decode byte 0x80 in position 50: illegal multibyte sequence
2、逐行進行讀取——f.readline()
with open("三國演義片頭曲_gbk.txt", "r", encoding="gbk") as f:
for i in range(3):
text = f.readline() # 每次只讀取一行
print(text)
臨江仙·滾滾長江東逝水
滾滾長江東逝水,浪花淘盡英雄。
是非成敗轉頭空。
with open("三國演義片頭曲_gbk.txt", "r", encoding="gbk") as f:
while True:
text = f.readline()
if not text:
# print(text is "")
break
else:
# print(text == "\n")
print(text, end="") # 保留原文的換行,使print()的換行不起作用
臨江仙·滾滾長江東逝水
滾滾長江東逝水,浪花淘盡英雄。
是非成敗轉頭空。
青山依舊在,幾度夕陽紅。
白髮漁樵江渚上,慣看秋月春風。
一壺濁酒喜相逢。
古今多少事,都付笑談中。
3、讀入所有行,以每行爲元素 形成一個列表——f.readlines()
with open("三國演義片頭曲_gbk.txt", "r", encoding="gbk") as f:
text = f.readlines() # 注意每行末尾有換行符
print(text)
['臨江仙·滾滾長江東逝水\n', '滾滾長江東逝水,浪花淘盡英雄。\n', '是非成敗轉頭空。\n', '\n', '青山依舊在,幾度夕陽紅。\n', '白髮漁樵江渚上,慣看秋月春風。\n', '一壺濁酒喜相逢。\n', '古今多少事,都付笑談中。\n']
with open("三國演義片頭曲_gbk.txt", "r", encoding="gbk") as f:
for text in f.readlines():
print(text) # 不想換行則用print(text, end="")
臨江仙·滾滾長江東逝水
滾滾長江東逝水,浪花淘盡英雄。
是非成敗轉頭空。
青山依舊在,幾度夕陽紅。
白髮漁樵江渚上,慣看秋月春風。
一壺濁酒喜相逢。
古今多少事,都付笑談中。
4、文本文件讀取小結
文件比較大時,read()和readlines()佔用內存過大,不建議使用
readline用起來又不太方便
with open("三國演義片頭曲_gbk.txt", "r", encoding="gbk") as f:
for text in f: # f本身就是一個可迭代對象,每次迭代讀取一行內容
print(text)
臨江仙·滾滾長江東逝水
滾滾長江東逝水,浪花淘盡英雄。
是非成敗轉頭空。
青山依舊在,幾度夕陽紅。
白髮漁樵江渚上,慣看秋月春風。
一壺濁酒喜相逢。
古今多少事,都付笑談中。
5、二進制文件
圖片:二進制文件
with open("test.jpg", "rb") as f:
print(len(f.readlines()))
69
8.1.3 文件的寫入
1、向文件寫入一個字符串或字節流(二進制)——f.write()
with open("第九套廣播體操.txt", "w", encoding="utf-8") as f:
f.write("大家好\n") # 文件不存在則立刻創建一個
f.write("歡迎一起做體操\n") # 如需換行,末尾加換行符\n
f.write("第一節\n")
f.write("頭部運動\n")
with open("第九套廣播體操.txt", "w", encoding="utf-8") as f:
f.write("第二節\n") # 如果文件存在,新寫入內容會覆蓋掉原內容,一定要注意!!!
f.write("伸展運動\n")
f.write("12345678\n")
f.write("22345678\n")
2、追加模式——"a"
with open("第九套廣播體操.txt", "a", encoding="utf-8") as f:
f.write("第三節\n") # 如果文件存在,新寫入內容會覆蓋掉原內容,一定要注意!!!
f.write("腰部運動\n")
f.write("12345678\n")
f.write("22345678\n")
3、將一個元素爲字符串的列表整體寫入文件——f.writelines()
ls = ["春天颳着風", "秋天下着雨", "我們要運動"]
with open("第九套廣播體操.txt", "w", encoding="utf-8") as f:
f.writelines(ls)
ls = ["春天颳着風\n", "秋天下着雨\n", "我們要運動\n"]
with open("第九套廣播體操.txt", "w", encoding="utf-8") as f:
f.writelines(ls)
8.1.4 既讀又寫
1、"r+"
- 如果文件名不存在,則報錯
- 指針在開始
- 要把指針移到末尾才能開始寫,否則會覆蓋前面內容
with open("浪淘沙_北戴河.txt", "r+", encoding="gbk") as f:
# for line in f:
# print(line) # 全部讀一遍後,指針到達結尾
f.seek(0,2) # 或者可以將指針移到末尾f.seek(偏移字節數,位置(0:開始;1:當前位置;2:結尾))
text = ["蕭瑟秋風今又是,\n", "換了人間。\n"]
f.writelines(text)
2、"w+"
- 若文件不存在,則創建
- 若文件存在,會立刻清空原內容!!!
with open("浪淘沙_北戴河.txt", "w+", encoding="gbk") as f:
pass
with open("浪淘沙_北戴河.txt", "w+", encoding="gbk") as f:
text = ["蕭瑟秋風今又是,\n", "換了人間。\n"] # 清空原內容
f.writelines(text) # 寫入新內容,指針在最後
f.seek(0,0) # 指針移到開始
print(f.read()) # 讀取內容
3、"a+"
- 若文件不存在,則創建
- 指針在末尾,添加新內容,不會清空原內容
with open("浪淘沙_北戴河.txt", "a+", encoding="gbk") as f:
f.seek(0,0) # 指針移到開始
print(f.read()) # 讀取內容
大雨落幽燕,
白浪滔天。
秦皇島外打魚船。
一片汪洋都不見,
知向誰邊?
往事越千年,
魏武揮鞭,
東臨碣石有遺篇。
蕭瑟秋風今又是,
換了人間。
with open("浪淘沙_北戴河.txt", "a+", encoding="gbk") as f:
text = ["蕭瑟秋風今又是,\n", "換了人間。\n"]
f.writelines(text) # 指針在最後,追加新內容,
f.seek(0,0) # 指針移到開始
print(f.read()) # 讀取內容
大雨落幽燕,
白浪滔天。
秦皇島外打魚船。
一片汪洋都不見,
知向誰邊?
往事越千年,
魏武揮鞭,
東臨碣石有遺篇。
蕭瑟秋風今又是,
換了人間。
蕭瑟秋風今又是,
換了人間。
8.1.5 數據的存儲與讀取
通用的數據格式,可以在不同語言中加載和存儲
本節簡單瞭解兩種數據存儲結構csv和json
1、csv格式
由逗號將數據分開的字符序列,可以由excel打開
- 讀取
with open("成績.csv", "r", encoding="gbk") as f:
ls = []
for line in f: # 逐行讀取
ls.append(line.strip("\n").split(",")) # 去掉每行的換行符,然後用“,”進行分割
for res in ls:
print(res)
['編號', '數學成績', '語文成績']
['1', '100', '98']
['2', '96', '99']
['3', '97', '95']
- 寫入
ls = [['編號', '數學成績', '語文成績'], ['1', '100', '98'], ['2', '96', '99'], ['3', '97', '95']]
with open("score.csv", "w", encoding="gbk") as f: # encoding="utf-8"中文出現亂碼
for row in ls: # 逐行寫入
f.write(",".join(row)+"\n") # 用逗號組合成字符串形式,末尾加換行符
也可以藉助csv模塊完成上述操作
2、json格式
常被用來存儲字典類型
- 寫入——dump()
import json
scores = {"Petter":{"math":96 , "physics": 98},
"Paul":{"math":92 , "physics": 99},
"Mary":{"math":98 , "physics": 97}}
with open("score.json", "w", encoding="utf-8") as f: # 寫入整個對象
# indent 表示字符串換行+縮進 ensure_ascii=False 顯示中文
json.dump(scores, f, indent=4, ensure_ascii=False)
- 讀取——load()
with open("score.json", "r", encoding="utf-8") as f:
scores = json.load(f) # 加載整個對象
for k,v in scores.items():
print(k,v)
Petter {'math': 96, 'physics': 98}
Paul {'math': 92, 'physics': 99}
Mary {'math': 98, 'physics': 97}
8.2 異常處理
8.2.1 常見異常的產生
1、除0運算——ZeroDivisionError
1/0
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
<ipython-input-1-9e1622b385b6> in <module>
----> 1 1/0
ZeroDivisionError: division by zero
2、找不到可讀文件——FileNotFoundError
with open("nobody.csv") as f:
pass
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
<ipython-input-2-f2e8c7d0ac60> in <module>
----> 1 with open("nobody.csv") as f:
2 pass
FileNotFoundError: [Errno 2] No such file or directory: 'nobody.csv'
3、值錯誤——ValueError
傳入一個調用者不期望的值,即使這個值的類型是正確的
s = "1.3"
n = int(s)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-8-69942d9db3c0> in <module>
1 s = "1.3"
----> 2 n = int(s)
ValueError: invalid literal for int() with base 10: '1.3'
4、索引錯誤——IndexError
下標超出序列邊界
ls = [1, 2, 3]
ls[5]
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-9-acf459124b52> in <module>
1 ls = [1, 2, 3]
----> 2 ls[5]
IndexError: list index out of range
5、類型錯誤——TypeError
傳入對象類型與要求不符
1 + "3"
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-10-ee505dc42f75> in <module>
----> 1 1 + "3"
TypeError: unsupported operand type(s) for +: 'int' and 'str'
6、其他常見的異常類型
NameError 使用一個未被定義的變量
KeyError 試圖訪問字典裏不存在的鍵
print(a)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-11-bca0e2660b9f> in <module>
----> 1 print(a)
NameError: name 'a' is not defined
d = {}
d["1"]
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-12-e629d551aca0> in <module>
1 d = {}
----> 2 d["1"]
KeyError: '1'
當異常發生的時候,如果不預先設定處理方法,程序就會中斷
8.2.2 異常的處理
提高程序的穩定性和可靠性
1、try_except
-
如果try內代碼塊順利執行,except不被觸發
-
如果try內代碼塊發生錯誤,觸發except,執行except內代碼塊
-
單分支
x = 10
y = 0
try:
z = x/y
except ZeroDivisionError: # 一般來說會預判到出現什麼錯誤
# z = x/(y+1e-7)
# print(z)
print("0不可以被除!")
0不可以被除!
x = 10
y = 0
try:
z = x/y
except NameError: # 一般來說會預判到出現什麼錯誤
# z = x/(y+1e-7)
# print(z)
print("0不可以被除!")
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
<ipython-input-14-aea58863ad72> in <module>
2 y = 0
3 try:
----> 4 z = x/y
5 except NameError: # 一般來說會預判到出現什麼錯誤
6 # z = x/(y+1e-7)
ZeroDivisionError: division by zero
- 多分支
ls = []
d = {"name": "Faye"}
try:
y = m
# ls[3]
# d["age"]
except NameError:
print("變量名不存在")
except IndexError:
print("索引超出界限")
except KeyError:
print("鍵不存在")
變量名不存在
- 萬能異常 Exception (所有錯誤的老祖宗)
ls = []
d = {"name": "Faye"}
try:
# y = m
ls[3]
# d["age"]
except Exception:
print("出錯啦")
出錯啦
- 捕獲異常的值 as
ls = []
d = {"name": "Faye"}
# y = x
try:
y = m
# ls[3]
# d["age"]
except Exception as e: # 雖不能獲得錯誤具體類型,但可以獲得錯誤的值
print(e)
name 'm' is not defined
2、try_except_else
- 如果try 模塊執行,則else模塊也執行
可以將else 看做try成功的額外獎賞
try:
with open("浪淘沙_北戴河.txt") as f:
text = f.read()
except FileNotFoundError:
print("找不到該文件,ta是不是用了美顏?")
else:
for s in ["\n", ",", "。", "?"]: # 去掉換行符和標點符號
text = text.replace(s, "")
print("名作《浪淘沙_北戴河》共由{}個字組成。".format(len(text)))
名作《浪淘沙_北戴河》共由65個字組成。
3、try_except_finally
- 不論try模塊是否執行,finally最後都執行
ls = []
d = {"name": "Faye"}
# y = x
try:
y = m
# ls[3]
# d["age"]
except Exception as e: # 雖不能獲得錯誤具體類型,但可以獲得錯誤的值
print(e)
finally:
print("不論觸不觸發異常,都將執行")
name 'm' is not defined
不論觸不觸發異常,都將執行
8.3 模塊簡介
已經被封裝好
無需自己再“造輪子”
聲明導入後,拿來即用
8.3.1 廣義模塊分類
1、Python 內置
時間庫time\ 隨機庫random\ 容器數據類型collection\ 迭代器函數itertools
2、第三方庫
數據分析numpy、pandas\ 數據可視化matplotlib\ 機器學習scikit-learn\ 深度學習Tensorflow
3、自定義文件
-
單獨py文件
-
包——多個py文件
# 文件夾內多個py文件,再加一個__init__.py文件(內容可爲空)
8.3.2 模塊的導入
1、導入整個模塊——import 模塊名
- **調用方式:**模塊名.函數名或類名
import time
start = time.time() # 調用time模塊中的time()
time.sleep(3) # 調用time模塊中的sleep() 休息3秒鐘
end = time.time()
print("程序運行用時:{:.2f}秒".format(end-start))
程序運行用時:3.00秒
import fun1
fun1.f1()
導入fun1成功
2、從模塊中導入類或函數——from 模塊 import 類名或函數名
- **調用方式:**函數名或類名
from itertools import product
ls = list(product("AB", "123"))
print(ls)
[('A', '1'), ('A', '2'), ('A', '3'), ('B', '1'), ('B', '2'), ('B', '3')]
from function.fun1 import f1 # 注意這種用法
f1()
導入fun1成功
一次導入多個
from function import fun1, fun2
fun1.f1()
fun2.f2()
導入fun1成功
導入fun2成功
3、導入模塊中所有的類和函數——from 模塊 import *
- **調用方式:**函數名或類名
from random import *
print(randint(1,100)) # 產生一個[1,100]之間的隨機整數
print(random()) # 產生一個[0,1)之間的隨機小數
36
0.6582485822110181
8.3.3 模塊的查找路徑
模塊搜索查找順序:
- 1、內存中已經加載的模塊
import fun1
fun1.f1()
導入fun1成功
# 刪除硬盤上的fun1文件後再次執行
import fun1
fun1.f1()
導入fun1成功
# 修改硬盤上的fun1 文件
import fun1
fun1.f1()
# 居然沒變,說明是優先從內存中讀取的
導入fun1成功
- 2、內置模塊
# Python 啓動時,解釋器會默認加載一些 modules 存放在sys.modules中
# sys.modules 變量包含一個由當前載入(完整且成功導入)到解釋器的模塊組成的字典, 模塊名作爲鍵, 它們的位置作爲值
import sys
print(len(sys.modules))
print("math" in sys.modules)
print("numpy" in sys.modules)
for k,v in list(sys.modules.items())[:20]:
print(k, ":", v)
738
True
False
sys : <module 'sys' (built-in)>
builtins : <module 'builtins' (built-in)>
_frozen_importlib : <module 'importlib._bootstrap' (frozen)>
_imp : <module '_imp' (built-in)>
_thread : <module '_thread' (built-in)>
_warnings : <module '_warnings' (built-in)>
_weakref : <module '_weakref' (built-in)>
zipimport : <module 'zipimport' (built-in)>
_frozen_importlib_external : <module 'importlib._bootstrap_external' (frozen)>
_io : <module 'io' (built-in)>
marshal : <module 'marshal' (built-in)>
nt : <module 'nt' (built-in)>
winreg : <module 'winreg' (built-in)>
encodings : <module 'encodings' from 'C:\\Users\\ibm\\Anaconda3\\lib\\encodings\\__init__.py'>
codecs : <module 'codecs' from 'C:\\Users\\ibm\\Anaconda3\\lib\\codecs.py'>
_codecs : <module '_codecs' (built-in)>
encodings.aliases : <module 'encodings.aliases' from 'C:\\Users\\ibm\\Anaconda3\\lib\\encodings\\aliases.py'>
encodings.utf_8 : <module 'encodings.utf_8' from 'C:\\Users\\ibm\\Anaconda3\\lib\\encodings\\utf_8.py'>
_signal : <module '_signal' (built-in)>
__main__ : <module '__main__'>
- 3、sys.path路徑中包含的模塊
import sys
sys.path
['E:\\ipython',
'C:\\Users\\ibm\\Anaconda3\\python37.zip',
'C:\\Users\\ibm\\Anaconda3\\DLLs',
'C:\\Users\\ibm\\Anaconda3\\lib',
'C:\\Users\\ibm\\Anaconda3',
'',
'C:\\Users\\ibm\\AppData\\Roaming\\Python\\Python37\\site-packages',
'C:\\Users\\ibm\\Anaconda3\\lib\\site-packages',
'C:\\Users\\ibm\\Anaconda3\\lib\\site-packages\\win32',
'C:\\Users\\ibm\\Anaconda3\\lib\\site-packages\\win32\\lib',
'C:\\Users\\ibm\\Anaconda3\\lib\\site-packages\\Pythonwin',
'C:\\Users\\ibm\\Anaconda3\\lib\\site-packages\\IPython\\extensions',
'C:\\Users\\ibm\\.ipython']
- sys.path的第一個路徑是當前執行文件所在的文件夾
- 若需將不在該文件夾內的模塊導入,需要將模塊的路徑添加到sys.path
# import fun3
import sys
sys.path.append("C:\\Users\\ibm\\Desktop") # 注意是雙斜槓
import fun3
fun3.f3()
導入fun3成功