大家好呀柬甥,今天是數(shù)據(jù)分析課程的 Python 基礎(chǔ)知識(shí)部分第 5 節(jié)饮六,主要講異常、模塊和包的相關(guān)知識(shí)苛蒲,另外還會(huì)涉及到一些文件的基本操作卤橄,因?yàn)橄鹿?jié)課我們會(huì)學(xué)習(xí)用 Python 處理 word 和 excel 文件。
這節(jié)課的主要內(nèi)容有:
1臂外、捕獲異常
2窟扑、模塊
(1)自定義模塊的使用
3、包的介紹
4漏健、文件基礎(chǔ)操作(不是重點(diǎn))
(1)文件簡介
(2)讀文件
(3)文件的打開方式
(4)字符編碼
(5)寫文件
一嚎货、捕獲異常
在各種計(jì)算機(jī)編程語言中,程序一旦出錯(cuò)蔫浆,系統(tǒng)會(huì)一級(jí)一級(jí)上報(bào)殖属,直到某個(gè)函數(shù)可以處理該錯(cuò)誤(比如,給用戶輸出一個(gè)錯(cuò)誤信息)瓦盛,才會(huì)停止洗显。所以高級(jí)語言通常都內(nèi)置了一套 try...except...finally...
的錯(cuò)誤處理機(jī)制,Python也不例外原环。舉個(gè)例子來看一下 try
的機(jī)制:
try:
print('try...')
r = 10 / 0
print('result:', r)
except ZeroDivisionError as e:
print('except:', e)
finally:
print('finally...')
print('END')
當(dāng)我們認(rèn)為某些代碼可能會(huì)出錯(cuò)時(shí)挠唆,就可以用 try
來運(yùn)行這段代碼,如果執(zhí)行出錯(cuò)扮念,則后續(xù)代碼不會(huì)繼續(xù)執(zhí)行损搬,而是直接跳轉(zhuǎn)至錯(cuò)誤處理代碼,即 except
語句塊柜与,執(zhí)行完 except
后,如果有 finally
語句塊嵌灰,則執(zhí)行 finally
語句塊弄匕,至此,執(zhí)行完畢沽瞭。
上面的代碼在計(jì)算 10 / 0
時(shí)會(huì)產(chǎn)生一個(gè)除法運(yùn)算錯(cuò)誤:
try...
except: division by zero finally...
END
從輸出可以看到迁匠,當(dāng)錯(cuò)誤發(fā)生時(shí),后續(xù)語句 print('result:', r)
不會(huì)被執(zhí)行驹溃,except
由于捕獲到 ZeroDivisionError
城丧,因此被執(zhí)行。最后豌鹤, finally
語句被執(zhí)行亡哄。然后,程序繼續(xù)按照流程往下走布疙。
如果把除數(shù) 0 改成 2 蚊惯,則執(zhí)行結(jié)果如下:
try...
result: 5
finally...
END
由于沒有錯(cuò)誤發(fā)生愿卸,所以 except
語句塊不會(huì)被執(zhí)行,但是 finally
如果有截型,則一定會(huì)被執(zhí)行(可以沒有 finally
語句)趴荸。
當(dāng)然,錯(cuò)誤有很多種類宦焦,如果發(fā)生了不同類型的錯(cuò)誤发钝,應(yīng)該由不同的 except
語句塊處理。所以我們可以用多個(gè) except
來捕獲不同類型的錯(cuò)誤:
try:
print('try...')
r = 10 / int('a')
print('result:', r)
except ValueError as e:
print('ValueError:', e)
except ZeroDivisionError as e:
print('ZeroDivisionError:', e)
finally:
print('finally...')
print('END')
int()
函數(shù)可能會(huì)拋出 ValueError
波闹,所以我們用一個(gè) except
捕獲 ValueError
笼平,用另一個(gè) except
捕獲 ZeroDivisionError
。
此外舔痪,如果沒有錯(cuò)誤發(fā)生寓调,可以在 except
語句塊后面加一個(gè) else
,當(dāng)沒有錯(cuò)誤發(fā)生時(shí)锄码,會(huì)自動(dòng)執(zhí)行 else
語句:
try:
print('try...')
r = 10 / int('2')
print('result:', r)
except ValueError as e:
print('ValueError:', e)
except ZeroDivisionError as e:
print('ZeroDivisionError:', e)
else:
print('no error!')
finally:
print('finally...')
print('END')
Python 的錯(cuò)誤其實(shí)也是 class
夺英,所有的錯(cuò)誤類型都繼承自 BaseException
,所以在使用 except
時(shí)需要注意的是滋捶,它不但捕獲該類型的錯(cuò)誤痛悯,還把其子類也“一網(wǎng)打盡”。比如:
try:
foo()
except ValueError as e:
print('ValueError')
except UnicodeError as e:
print('UnicodeError')
第二個(gè) except
永遠(yuǎn)也捕獲不到 UnicodeError
重窟,因?yàn)?UnicodeError
是 ValueError
的子類载萌,如果有,也被第一個(gè) except
給捕獲了巡扇。
Python所有的錯(cuò)誤都是從 BaseException
類派生的扭仁,常見的錯(cuò)誤類型和繼承關(guān)系看這里:
https://docs.python.org/3/library/exceptions.html#exception-hierarchy
使用 try...except
捕獲錯(cuò)誤還有一個(gè)巨大的好處,就是可以跨越多層調(diào)用厅翔,比如函數(shù) main()
調(diào)用 foo()
乖坠, foo()
調(diào)用 bar()
,結(jié)果 bar()
出錯(cuò)了刀闷,這時(shí)熊泵,只要 main()
捕獲到了,就可以處理:
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main():
try:
bar('0')
except Exception as e:
print('Error:', e)
finally:
print('finally...')
也就是說甸昏,不需要在每個(gè)可能出錯(cuò)的地方去捕獲錯(cuò)誤顽分,只要在合適的層次去捕獲錯(cuò)誤就可以了。這樣一來施蜜,就大大減少了寫 try...except...finally
的麻煩卒蘸。
python所有的標(biāo)準(zhǔn)異常類:
異常名稱 | 描述 |
---|---|
BaseException | 所有異常的基類 |
SystemExit | 解釋器請(qǐng)求退出 |
KeyboardInterrupt | 用戶中斷執(zhí)行(通常是輸入^C) |
Exception | 常規(guī)錯(cuò)誤的基類 |
StopIteration | 迭代器沒有更多的值 |
GeneratorExit | 生成器(generator)發(fā)生異常來通知退出 |
SystemExit | Python 解釋器請(qǐng)求退出 |
StandardError | 所有的內(nèi)建標(biāo)準(zhǔn)異常的基類 |
ArithmeticError | 所有數(shù)值計(jì)算錯(cuò)誤的基類 |
FloatingPointError | 浮點(diǎn)計(jì)算錯(cuò)誤 |
Over?owError | 數(shù)值運(yùn)算超出最大限制 |
ZeroDivisionError | 除(或取模)零 (所有數(shù)據(jù)類型) |
AssertionError | 斷言語句失敗 |
AttributeError | 對(duì)象沒有這個(gè)屬性 |
EOFError | 沒有內(nèi)建輸入,到達(dá)EOF 標(biāo)記 |
EnvironmentError | 操作系統(tǒng)錯(cuò)誤的基類 |
IOError | 輸入/輸出操作失敗 |
OSError | 操作系統(tǒng)錯(cuò)誤 |
WindowsError | 系統(tǒng)調(diào)用失敗 |
ImportError | 導(dǎo)入模塊/對(duì)象失敗 |
KeyboardInterrupt | 用戶中斷執(zhí)行(通常是輸入^C) |
LookupError | 無效數(shù)據(jù)查詢的基類 |
IndexError | 序列中沒有沒有此索引(index) |
KeyError | 映射中沒有這個(gè)鍵 |
MemoryError | 內(nèi)存溢出錯(cuò)誤(對(duì)于Python 解釋器不是致命的) |
NameError | 未聲明/初始化對(duì)象 (沒有屬性) |
UnboundLocalError | 訪問未初始化的本地變量 |
ReferenceError | 弱引用(Weak reference)試圖訪問已經(jīng)垃圾回收了的對(duì)象 |
RuntimeError | 一般的運(yùn)行時(shí)錯(cuò)誤 |
NotImplementedError | 尚未實(shí)現(xiàn)的方法 |
SyntaxError | Python 語法錯(cuò)誤 |
IndentationError | 縮進(jìn)錯(cuò)誤 |
TabError | Tab 和空格混用 |
SystemError | 一般的解釋器系統(tǒng)錯(cuò)誤 |
TypeError | 對(duì)類型無效的操作 |
ValueError | 傳入無效的參數(shù) |
UnicodeError | Unicode 相關(guān)的錯(cuò)誤 |
UnicodeDecodeError | Unicode 解碼時(shí)的錯(cuò)誤 |
UnicodeEncodeError | Unicode 編碼時(shí)錯(cuò)誤 |
UnicodeTranslateError | Unicode 轉(zhuǎn)換時(shí)錯(cuò)誤 |
Warning | 警告的基類 |
DeprecationWarning | 關(guān)于被棄用的特征的警告 |
FutureWarning | 關(guān)于構(gòu)造將來語義會(huì)有改變的警告 |
Over?owWarning | 舊的關(guān)于自動(dòng)提升為長整型(long)的警告 |
PendingDeprecationWarning | 關(guān)于特性將會(huì)被廢棄的警告 |
RuntimeWarning | 可疑的運(yùn)行時(shí)行為(runtime behavior)的警告 |
SyntaxWarning | 可疑的語法的警告 |
UserWarning | 用戶代碼生成的警告 |
二、模塊
模塊: 通俗理解花墩,一個(gè) .py
文件就是一個(gè)模塊悬秉,模塊是管理功能代碼的澄步。
內(nèi)置模塊:就是 python 自己內(nèi)部自帶的不需要我們?nèi)ハ螺d的模塊, 比如:time
, random
等和泌。
1村缸、自定義模塊的使用
注意: 自定義模塊名字和變量名的定義很類似,都是由字母武氓、數(shù)字梯皿、下劃線組成,但是不能以數(shù)字開頭县恕,否則無法導(dǎo)入該模塊东羹。
創(chuàng)建名為?rst_module的自定義模塊:
__all__ = ["g_num", "show"]
# 指定 all 表示 from 模塊名 import * 只能使用指定的功能代碼,而不是所有的功能代碼
# 定義全局變量
g_num = 10
# 定義函數(shù)
def show():
print("我是一個(gè)函數(shù)")
# 定義類
class Student(object):
def init (self, name, age):
self.name = name
self.age = age
def show_msg(self):
print(self.name, self.age)
# 解決導(dǎo)入的模塊中方法沒有調(diào)用就會(huì)執(zhí)行
if __name__ == '__main__':
show()
使用自定義的模塊:
# 導(dǎo)入模塊
import first_module
# 使用模塊中的功能代碼
print(first_module.g_num)
first_module.show()
stu = first_module.Student("李四", 20)
stu.show_msg()
注意:使用 __name__
查看模塊名忠烛,執(zhí)行哪個(gè)文件属提,哪個(gè)文件中的 __name__
輸出 __main__
, 其他導(dǎo)入模塊中的 __name__
結(jié)果就是模塊名字。
模塊導(dǎo)入的注意點(diǎn):
- 自定義的模塊名不要和系統(tǒng)的模塊名重名美尸;
- 導(dǎo)入的功能代碼不要在當(dāng)前模塊定義否則使用不了導(dǎo)入模塊的功能代碼冤议。
三、包的介紹
包:通俗理解包就是一個(gè)文件夾师坎,只不過文件夾里面有一個(gè)__init__.py
文件恕酸,包是管理模塊的,模塊是管理功能代碼的胯陋。
# -----import導(dǎo)入包里面的模塊----
import first_package.first
#-----import導(dǎo)入包里面的模塊設(shè)置別名----
import first_package.first as one
#----from導(dǎo)入包名 import 模塊名----
from first_package import second
#--- from 包名.模塊名 import 功能代碼----
from first_package.first import show # 需要保證當(dāng)前模塊沒有導(dǎo)入模塊的功能代碼
# --- from 包名 import *, 默認(rèn)不會(huì)導(dǎo)入包里面的所有模塊蕊温,需要在 init 文件里面使用 __all__ 去指定導(dǎo)入的模塊
from first_package import *
** __init__
文件寫法**
# 如果外界使用from 包名 import * 不會(huì)導(dǎo)入包里面的所有模塊,需要使用 all 指定
__all__ = ["first", "second"]
# 從當(dāng)前包導(dǎo)入對(duì)應(yīng)的模塊
from . import first
from . import second
四遏乔、文件基礎(chǔ)操作
1义矛、文件簡介
文件包括文本文件和二進(jìn)制文件(聲音,圖像按灶,視頻)症革。不過,從存儲(chǔ)方式來說鸯旁,文件在磁盤上的存儲(chǔ)方式都是二進(jìn)制形式,所以量蕊,文本文件其實(shí)也應(yīng)該算二進(jìn)制文件铺罢。
不過二者也有區(qū)別,雖然都是二進(jìn)制文件残炮,但是二進(jìn)制代表的意思不一樣韭赘。二進(jìn)制文件是將內(nèi)存里面的數(shù)據(jù)直接讀寫入文本中,而文本文件則是將數(shù)據(jù)先轉(zhuǎn)換成了字符串势就,再寫入到文本中泉瞻。
2脉漏、讀文件
要以讀文件的模式打開一個(gè)文件對(duì)象,使用 Python 內(nèi)置的 open()
函數(shù)袖牙,傳入文件名和標(biāo)示符:
>>> f = open('/Users/michael/test.txt', 'r')
標(biāo)示符 r
表示讀侧巨,這樣,我們就成功地打開了一個(gè)文件鞭达。
如果文件不存在司忱,open()
函數(shù)就會(huì)拋出一個(gè) IOError
的錯(cuò)誤,并且給出錯(cuò)誤碼和詳細(xì)的信息告訴你文件不存在:
>>> f=open('/Users/michael/notfound.txt', 'r')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: '/Users/michael/notfound.txt'
如果文件打開成功畴蹭,接下來坦仍,調(diào)用 read()
方法可以一次讀取文件的全部內(nèi)容,Python 把內(nèi)容讀到內(nèi)存叨襟,用一個(gè) str
對(duì)象表示:
>>> f.read() 'Hello, world!'
最后一步是調(diào)用 close()
方法關(guān)閉文件繁扎。文件使用完畢后必須關(guān)閉,因?yàn)槲募?duì)象會(huì)占用操作系統(tǒng)的資源糊闽,并且操作系統(tǒng)同一時(shí)間能打開的文件數(shù)量也是有限的:
>>> f.close()
由于文件讀寫時(shí)都有可能產(chǎn)生 IOError
梳玫,一旦出錯(cuò),后面的 f.close()
就不會(huì)調(diào)用墓怀。所以汽纠,為了保證無論是否出錯(cuò)都能正確地關(guān)閉文件,我們可以使用 try... finally
來實(shí)現(xiàn):
try:
f = open('/path/to/file', 'r') print(f.read())
finally:
if f:
f.close()
但是每次都這么寫實(shí)在太繁瑣傀履,所以虱朵,Python 引入了 with
語句來自動(dòng)幫我們調(diào)用 close()
方法:
with open('/path/to/file', 'r') as f:
print(f.read())
這和前面的 try ... finally
是一樣的,但是代碼更加簡潔钓账,并且不必調(diào)用 f.close()
方法碴犬。
調(diào)用 read()
會(huì)一次性讀取文件的全部內(nèi)容,如果文件有10G梆暮,內(nèi)存就爆了服协,所以,要保險(xiǎn)起見啦粹,可以反復(fù)調(diào)用 read(size)
方法偿荷,每次最多讀取size個(gè)字節(jié)的內(nèi)容。另外唠椭,調(diào)用 readline()
可以每次讀取一行內(nèi)容跳纳,調(diào)用 readlines()
一次讀取所有內(nèi)容并按行返回 list
。因此贪嫂,要根據(jù)需要決定怎么調(diào)用寺庄。
如果文件很小, read()
一次性讀取最方便;如果不能確定文件大小斗塘,反復(fù)調(diào)用 read(size)
比較保險(xiǎn)赢织;如果是配置文件,調(diào)用 readlines()
最方便:
for line in f.readlines():
print(line.strip()) # 把末尾的'\n'刪掉
3馍盟、文件的打開方式
模式 | 描述 |
---|---|
r | 以只讀方式打開文件于置。文件的指針將會(huì)放在文件的開頭。這是默認(rèn)模式朽合。 |
rb | 以二進(jìn)制格式打開一個(gè)文件用于只讀俱两。文件指針將會(huì)放在文件的開頭。 |
r+ | 打開一個(gè)文件用于讀寫曹步。文件指針將會(huì)放在文件的開頭宪彩。 |
rb+ | 以二進(jìn)制格式打開一個(gè)文件用于讀寫。文件指針將會(huì)放在文件的開頭讲婚。 |
w | 打開一個(gè)文件只用于寫入尿孔。如果該文件已存在則打開文件,并從開頭開始編輯筹麸,即原有內(nèi) 容會(huì)被刪除活合。如果該文件不存在,創(chuàng)建新文件物赶。 |
wb | 以二進(jìn)制格式打開一個(gè)文件只用于寫入白指。如果該文件已存在則打開文件,并從開頭開始編 輯酵紫,即原有內(nèi)容會(huì)被刪除告嘲。如果該文件不存在,創(chuàng)建新文件奖地。 |
w+ | 打開一個(gè)文件用于讀寫橄唬。如果該文件已存在則打開文件,并從開頭開始編輯参歹,即原有內(nèi)容 會(huì)被刪除仰楚。如果該文件不存在,創(chuàng)建新文件犬庇。 |
wb+ | 以二進(jìn)制格式打開一個(gè)文件用于讀寫僧界。如果該文件已存在則打開文件,并從開頭開始編 輯臭挽,即原有內(nèi)容會(huì)被刪除捎泻。如果該文件不存在,創(chuàng)建新文件埋哟。 |
a | 打開一個(gè)文件用于追加。如果該文件已存在,文件指針將會(huì)放在文件的結(jié)尾赤赊。也就是說闯狱, 新的內(nèi)容將會(huì)被寫入到已有內(nèi)容之后。如果該文件不存在抛计,創(chuàng)建新文件進(jìn)行寫入哄孤。 |
ab | 以二進(jìn)制格式打開一個(gè)文件用于追加。如果該文件已存在吹截,文件指針將會(huì)放在文件的結(jié) 尾瘦陈。也就是說,新的內(nèi)容將會(huì)被寫入到已有內(nèi)容之后波俄。如果該文件不存在晨逝,創(chuàng)建新文件進(jìn) 行寫入。 |
a+ | 打開一個(gè)文件用于讀寫懦铺。如果該文件已存在捉貌,文件指針將會(huì)放在文件的結(jié)尾。文件打開時(shí) 會(huì)是追加模式冬念。如果該文件不存在趁窃,創(chuàng)建新文件用于讀寫。 |
ab+ | 以二進(jìn)制格式打開一個(gè)文件用于追加急前。如果該文件已存在醒陆,文件指針將會(huì)放在文件的結(jié) 尾。如果該文件不存在裆针,創(chuàng)建新文件用于讀寫刨摩。 |
4、字符編碼
要讀取非 UTF-8 編碼的文本文件据块,需要給 open()
函數(shù)傳入 encoding
參數(shù)码邻,例如,讀取 GBK 編碼的文件:
>>> f = open('/Users/michael/gbk.txt', 'r', encoding='gbk')
>>> f.read()
'測(cè)試'
遇到有些編碼不規(guī)范的文件另假,你可能會(huì)遇到 UnicodeDecodeError
像屋,因?yàn)樵谖谋疚募锌赡軍A雜了一些非法編碼的字符。遇到這種情況边篮, open()
函數(shù)還接收一個(gè) errors
參數(shù)己莺,表示如果遇到編碼錯(cuò)誤后如何處理。最簡單的方式是直接忽略:
>>> f = open('/Users/michael/gbk.txt', 'r', encoding='gbk', errors='ignore')
5戈轿、寫文件
寫文件和讀文件是一樣的凌受,唯一區(qū)別是調(diào)用 open()
函數(shù)時(shí),傳入標(biāo)識(shí)符 w
或者 wb
表示寫文本文件或?qū)懚M(jìn)制文件:
>>> f = open('/Users/michael/test.txt', 'w')
>>> f.write('Hello, world!')
>>> f.close()
你可以反復(fù)調(diào)用 write()
來寫入文件思杯,但是務(wù)必要調(diào)用 f.close()
來關(guān)閉文件胜蛉。當(dāng)我們寫文件時(shí)挠进,操作系統(tǒng)往往不會(huì)立刻把數(shù)據(jù)寫入磁盤,而是放到內(nèi)存緩存起來誊册,空閑的時(shí)候再慢慢寫入领突。只有調(diào)用close()
方法時(shí),操作系統(tǒng)才保證把沒有寫入的數(shù)據(jù)全部寫入磁盤案怯。忘記調(diào)用 close()
的后果是數(shù)據(jù)可能只寫了一部分到磁盤君旦,剩下的丟失了。所以嘲碱,還是用 with
語句來得保險(xiǎn):
with open('/Users/michael/test.txt', 'w') as f:
f.write('Hello, world!')
要寫入特定編碼的文本文件金砍,請(qǐng)給 open()
函數(shù)傳入 encoding
參數(shù),將字符串自動(dòng)轉(zhuǎn)換成指定編碼麦锯。
注意:
以 w
模式寫入文件時(shí)恕稠,如果文件已存在,會(huì)直接覆蓋(相當(dāng)于刪掉后新寫入一個(gè)文件)离咐。如果我們希望追加到文件末尾怎么辦谱俭?可以傳入 a
以追加(append)模式寫入。