五、文件操作
1、讀取鍵盤輸入
input ?獲取標準輸入,數據類型統一為字符串
#!/usr/bin/python # -*- coding: UTF-8 -*- str = input("請輸入:") print("你輸入的內容是: ", str)
這會產生如下的對應著輸入的結果:
請輸入:[x*5 for x in range(2,10,2)] 你輸入的內容是: [10, 20, 30, 40]
3、操作文件
對文件操作的流程
-
打開文件,得到文件句柄并賦值給一個變量
-
通過句柄對文件進行操作
-
關閉文件
現有文件如下:
cat 小重山.txt 昨夜寒蛩不住鳴。 驚回千里夢,已三更。 起來獨自繞階行。 人悄悄,簾外月朧明。 白首為功名,舊山松竹老,阻歸程。 欲將心事付瑤琴。 知音少,弦斷有誰聽。 ? f = open('小重山.txt',encoding='utf8') #打開文件 data=f.read() ?#獲取文件內容 f.close() ? ? ?#關閉文件
注意:如果是windows系統,hello文件是utf8保存的,打開文件時open函數是通過操作系統打開的文件,而win操作系統默認的是gbk編碼,所以直接打開會亂碼,需要f=open(‘hello',encoding='utf8'),hello文件如果是gbk保存的,則直接打開即可。
2、文件打開模式
先介紹兩種最基本的模式:
f = open('小重山.txt','w',encoding='utf8') #打開文件 f1 = open('小重山1.txt','a',encoding='utf8') #打開文件 f1.write('莫等閑1\n') f1.write('白了少年頭2\n') f1.write('空悲切!3')
1、open() 方法
Python open() 方法用于打開一個文件,并返回文件對象,在對文件進行處理過程都需要使用到這個函數,如果該文件無法被打開,會拋出 OSError。
注意:使用 open() 方法一定要保證關閉文件對象,即調用 close() 方法。
你必須先用Python內置的open()函數打開一個文件,創建一個file對象,相關的方法才可以調用它進行讀寫。
語法:
file object = open(file_name [, access_mode][, buffering])
各個參數的細節如下:
-
file_name:file_name變量是一個包含了你要訪問的文件名稱的字符串值。
-
access_mode:access_mode決定了打開文件的模式:只讀,寫入,追加等。所有可取值見如下的完全列表。這個參數是非強制的,默認文件訪問模式為只讀(r)。
-
buffering:如果buffering的值被設為0,就不會有寄存。如果buffering的值取1,訪問文件時會寄存行。如果將buffering的值設為大于1的整數,表明了這就是的寄存區的緩沖大小。如果取負值,寄存區的緩沖大小則為系統默認。
open() 函數常用形式是接收兩個參數:文件名(file)和模式(mode)。
open(file, mode='r')
完整的語法格式為:
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
參數說明:
-
file: 必需,文件路徑(相對或者絕對路徑)。
-
mode: 可選,文件打開模式
-
buffering: 設置緩沖
-
encoding: 一般使用utf8
-
errors: 報錯級別
-
newline: 區分換行符
-
closefd: 傳入的file參數類型
-
opener:
mode 參數有:
模式 | 描述 |
---|---|
b | 二進制模式。 |
+ | 打開一個文件進行更新(可讀可寫)。 |
r | 以只讀方式打開文件。文件的指針將會放在文件的開頭。這是默認模式。 |
w | 打開一個文件只用于寫入。如果該文件已存在則打開文件,并從開頭開始編輯,即原有內容會被刪除。如果該文件不存在,創建新文件。 |
a | 打開一個文件用于追加。如果該文件已存在,文件指針將會放在文件的結尾。也就是說,新的內容將會被寫入到已有內容之后。如果該文件不存在,創建新文件進行寫入。 |
默認為文本模式,如果要以二進制模式打開,加上 b 。
下圖很好的總結了這幾種模式:
模式 | R | R+ | W | W+ | A | A+ |
---|---|---|---|---|---|---|
讀 | + | + | + | + | ||
寫 | + | + | + | + | + | |
創建 | + | + | + | + | ||
覆蓋 | + | + | ||||
指針在開始 | + | + | + | + | ||
指針在結尾 | + | + |
2、file 對象
一個文件被打開后,你有一個file對象,你可以得到有關該文件的各種信息。
以下是和file對象相關的所有屬性的列表:
屬性 | 描述 |
---|---|
file.closed | 返回true如果文件已被關閉,否則返回false。 |
file.mode | 返回被打開文件的訪問模式。 |
file.name | 返回文件的名稱。 |
如下實例:
#!/usr/bin/python # -*- coding: UTF-8 -*- # 打開一個文件 fo = open("foo.txt", "w") print("文件名: ", fo.name) print("是否已關閉 : ",fo.closed) print("訪問模式 : ",fo.mode)
以上實例輸出結果:
文件名: ?foo.txt 是否已關閉 : ?False 訪問模式 : ?w
file 對象使用 open 函數來創建,下表列出了 file 對象常用的函數:
序號 | 方法及描述 |
---|---|
1 | file.close() 關閉文件。關閉后文件不能再進行讀寫操作。 |
2 | file.read([size])從文件讀取指定的字符數,如果未給定或為負則讀取所有。 |
3 | file.readlines([sizeint])讀取所有行并返回列表,若給定sizeint>0,返回總和大約為sizeint字節的行, 實際讀取值可能比 sizeint 較大, 因為需要填充緩沖區。 |
4 | file.seek(offset[, whence]) 設置文件當前位置. |
5 | file.tell() 返回文件當前指針位置。 |
6 | file.write(str) 將字符串寫入文件,返回的是寫入的字符長度。 |
注:python中打開文件有兩種方式,即:open(...) 和 ?file(...) ,本質上前者在內部會調用后者來進行文件操作,推薦使用 open。
打開文件時,需要指定文件路徑和以何等方式打開文件,打開后,即可獲取該文件句柄,日后通過此文件句柄對該文件操作。
3、close()方法
File 對象的 close()方法刷新緩沖區里任何還沒寫入的信息,并關閉該文件,這之后便不能再進行寫入。
當一個文件對象的引用被重新指定給另一個文件時,Python 會關閉之前的文件。用 close()方法關閉文件是一個很好的習慣。
語法:
fileObject.close()
例子:
#!/usr/bin/python # -*- coding: UTF-8 -*-# 打開一個文件 fo = open("foo.txt", "w") print("文件名: ", fo.name) # 關閉打開的文件 fo.close()
以上實例輸出結果:
文件名: ?foo.txt
讀寫文件:
file對象提供了一系列方法,能讓我們的文件訪問更輕松。來看看如何使用read()和write()方法來讀取和寫入文件。
4、write()方法
write()方法可將任何字符串寫入一個打開的文件。需要重點注意的是,Python字符串可以是二進制數據,而不是僅僅是文字。
write()方法不會在字符串的結尾添加換行符('\n'):
語法:
fileObject.write(string)
在這里,被傳遞的參數是要寫入到已打開文件的內容。
例子:
#!/usr/bin/python # -*- coding: UTF-8 -*-# 打開一個文件 fo = open("foo.txt", "w") fo.write( "www.1000phone.com!\nVery good site!\n") # 關閉打開的文件 fo.close()
上述方法會創建foo.txt文件,并將收到的內容寫入該文件,并最終關閉文件。如果你打開這個文件,將看到以下內容:
cat foo.txt www.1000phone.com! Very good site!
5、read()方法
read()方法從一個打開的文件中讀取一個字符串。需要重點注意的是,Python字符串可以是二進制數據,而不是僅僅是文字。
語法:
fileObject.read([count])
在這里,被傳遞的參數是要從已打開文件中讀取的字節計數。該方法從文件的開頭開始讀入,如果沒有傳入count,它會嘗試盡可能多地讀取更多的內容,很可能是直到文件的末尾。
例子:
這里我們用到以上創建的 foo.txt 文件。
#!/usr/bin/python # -*- coding: UTF-8 -*-# 打開一個文件 fo = open("foo.txt", "r+") str = fo.read(10) print("讀取的字符串是 : ", str) # 關閉打開的文件 fo.close()
以上實例輸出結果:
讀取的字符串是 : ?www.1000phone
6、readlines() 方法
readlines() 方法用于讀取所有行(直到結束符 EOF)并返回列表,該列表可以由 Python 的 for... in ... 結構進行理。
如果碰到結束符 EOF 則返回空字符串。
語法
readlines() 方法語法如下:
fileObject.readlines( );
返回值
返回列表,包含所有的行
#!/usr/bin/python # -*- coding: UTF-8 -*-# 打開文件 fo = open(小重山.txt", "r") print("文件名為: ", fo.name)for line in fo.readlines(): ? ? ? ? ? ? ? ? ? ? ? ? ?#依次讀取每行 ?line = line.strip() ? ? ? ? ? ? ? ? ? ? ? ? ? ? #去掉每行頭尾空白 ?print("讀取的數據為: %s" % (line))# 關閉文件 fo.close()
8、with 方法
為了避免打開文件后忘記關閉,可以通過管理上下文,即:
with open('log','r') as f: ? ? ? ?...
如此方式,當with代碼塊執行完畢時,內部會自動關閉并釋放文件資源。
在Python 2.7 后,with又支持同時對多個文件的上下文進行管理,即:
with open('log1') as obj1, open('log2') as obj2:pass
9、tell() 方法
tell()方法告訴你文件內的當前位置, 換句話說,下一次的讀寫會發生在文件開頭這么多字節之后。
seek(offset [,from])方法改變當前文件的位置。
Offset變量表示要移動的字節數。
From變量指定開始移動字節參考位置。
如果from被設為0,這意味著將文件的開頭作為移動字節的參考位置。如果設為1,則使用當前的位置作為參考位置。如果它被設為2,那么該文件的末尾將作為參考位置。
例子:
就用我們上面創建的文件foo.txt。
#!/usr/bin/python # -*- coding: UTF-8 -*-# 打開一個文件 fo = open("foo.txt", "r+") str = fo.read(10) print("讀取的字符串是 : ", str)# 查找當前位置 position = fo.tell() print("當前文件位置 : ", position) ? # 把指針再次重新定位到文件開頭 position = fo.seek(0, 0) str = fo.read(10) print("重新讀取字符串 : ", str) # 關閉打開的文件 fo.close()
以上實例輸出結果:
讀取的字符串是 : ? www.1000phone 當前文件位置 : ?10 重新讀取字符串 : ? www.1000phone
10、seek() 方法
seek() 方法用于移動文件讀取指針到指定位置。
seek() 方法語法如下:
fileObject.seek(offset[, whence])
參數
-
offset -- 開始的偏移量,也就是代表需要移動偏移的字節數
-
whence:可選,默認值為 0。給offset參數一個定義,表示要從哪個位置開始偏移;0代表從文件開頭開始算起,1代表從當前位置開始算起,2代表從文件末尾算起。
返回值
該函數沒有返回值。
以下實例演示了 readline() 方法的使用:
循環讀取文件的內容:
#!/usr/bin/python # -*- coding: UTF-8 -*- ? # 打開文件 fo = open("小重山.txt", "r+") print("文件名為: ", fo.name) ? line = fo.readline() print("讀取的數據為: %s" % (line)) ? # 重新設置文件讀取指針到開頭 fo.seek(0, 0) line = fo.readline() print("讀取的數據為: %s" % (line)) ? #關閉文件 fo.close()
六、函數式編程介紹
1、沒有使用函數式編程之前帶來的問題
-
代碼的組織結構不清晰,可讀性差
-
實現重復的功能時,你只能重復編寫實現功能的代碼,導致代碼冗余,白白耗費精力
-
假如某一部分功能需要擴展或更新時,需要找出所有實現此功能的地方,一一修改,無法統一管理,加大了維護難度
2、函數是什么
-
函數是對實現某一功能的代碼的封裝(代碼分解,松耦合,按功能劃分)
-
函數可以實現代碼的復用,從而減少代碼的重復編寫
3、Python 中函數的特性
-
函數的參數可以是python 中的任意數據類型,并且參數的數量可以是零個或多個。
-
函數也可以通過關鍵字
return
返回任何數量的 Python 中的任意數據類型,作為結果
4、函數分類
-
內置函數
-
https://docs.python.org/zh-cn/3.7/library/functions.html
? ? ?為了方便我們的開發,針對一些簡單的功能,python解釋器已經為我們定義好了的函數即內置函數, 內部提供很多方法,常用功能羅列出來,類似為使用方便而創建的快捷方式
? ? ?對于內置函數,我們可以拿來就用而無需事先定義,如len(),sum(),max()
help() type() id() bool() # 布爾值 max() min() sum() ? len() all() ?#接受一個序列,判斷所有值如果是真的(非空),返回True 否則返回false l = ['aaa','bbb'] all(l) ? any() ?#只要有一個是真,就是真 練習一下 ?all() any()
#_*_ coding:utf-8 _*_ """ 代碼注釋 """ l = [1, 2, 3] a = 'aaa' print(vars()) ? #當前模塊的所有變量 print(__file__) #當前模塊文件路徑 print(__doc__) ?#當前模塊的文檔信息 print(__name__) ? # python 默認在執行 .py 文件時,__name__ = __main__
-
自定義函數
? ?很明顯內置函數所能提供的功能是有限的,這就需要我們自己根據需求,事先定制好我們自己的函數來實現某 種功能,以后,在遇到應用場景時,調用自定義的函數即可。
-
導入函數
5、函數的定義
1 、如何自定義函數?
函數的定義中可能會涉及到如下幾點:
語法
def 函數名(參數1,參數2,參數3,...):'''注釋'''函數體return 返回的值 ? # 函數名要能反映函數本身所實現的意義
-
def:表示定義函數的關鍵字
-
函數名:函數的名稱,日后根據函數名調用函數
-
函數體:函數中進行一系列的邏輯計算,如:發送郵件、計算出 [11,22,38,888,2]中的最大數等...
-
參數:為函數體提供數據
-
return:當函數執行完畢后,可以給調用者返回數據(以后的代碼不再執行)。
實例
def f():pass ? def myfunc():'s' + 1
2. 函數在定義階段都干了哪些事?
只檢測定義函數所要求的語法,不執行函數體內的代碼
也就說,語法錯誤在函數定義階段就會檢測出來,而代碼的邏輯錯誤只有在調用執行時才會知道。
def get_result():r - 1 ? get_result() ? # 調用函數后會輸出如下錯誤提示結果: NameError: name 'r' is not definedget_reuslt = """r - 1 """
6、函數調用
1、函數調用
函數的調用:函數名加小括號 func()
-
先找到名字
-
根據名字調用代碼
def myfunc():url = "www.qfedu.com" # 調用 ? ? myfunc()
2、函數使用的原則:必須先定義,才可以調用
定義函數就是在定義“變量”,“變量”必須先定義后,才可以使用。
不定義而直接調用函數,就相當于在使用一個不存在的變量名
# 情景一 def foo():print('from foo')func() ? foo() ?# 報錯 ? # 情景二 def func():print('from func') def foo():print('from foo')func() ? foo() ?# 正常執行 ? # 情景三 def foo():print('from foo')func()def func():print('from func') ? foo() ?# 可以正常執行嗎?
需求,自定義函數實現監控告警(cpu,disk,mem) def email發送郵件()(連接,發送,關閉)def CPU告警()cpu > 80email發送郵件 ? def disk告警()disk > 80email發送郵件 ? def mem告警()mem > 60email發送郵件
#!/usr/bin/python3 import yagmail import sys yag = yagmail.SMTP(user='xxxx@163.com',password='',host='smtp.163.com', ?# 郵局的 smtp 地址port='25', ? ? ? # 郵局的 smtp 端口smtp_ssl=False) yag.send(to=sys.argv[1],subject=sys.argv[2],contents=sys.argv[3])
3、總結
-
函數的使用,必須遵循的原則是:先定義,后調用
-
在使用函數時,我們一定要明確地區分定義階段和調用階段
-
在函數體里面的任何代碼都只是定義而已,只有在調用此函數時,這個函數內的代碼才會執行。
# 定義階段 def foo():print('from foo')func() def func():print('from func')# 調用階段 foo()
7、函數返回值 return
-
不定義,默認返回 None
-
返回多個值時,每個值用逗號隔開,也就是元組的形式
1、如何自定義返回值
使用 return
關鍵字
def foo():x = 1return x ? ret = foo() ?# 給你, 這一步相當于下面的一行代碼,就是 foo() 執行完畢,#創建了 1 這個對象,之后把變量名 ret 分配給對象 1 # ret = 1 ? print(ret)
2、接收函數的返回值
注意:必須先執行函數,此函數的返回值才會被創建并返回
# 可以使用一個變量來接收一個函數的返回值 ret = foo() ? print("foo 函數的返回值是:", ret) ? # 也可以把函數的返回值直接作為參數來使用,這時函數 `foo` 會先被執行, # 之后把其返回值放在其原來的位置,以供 `print` 函數作為參數使用 print("foo 函數的返回值是:", foo())
在函數執行的過程中,當在函數體內遇到了
return
關鍵字,函數就會立刻停止執行,并返回其返回值,return
之后的代碼不會
被執行。
def func():x = 100returnprint('ok') ? # 不執行 ? print('qf') ? ? ? # 執行,因為此代碼已不是函數體內的代碼了,注意縮進。 func()
定義一個有返回值的函數,并調用此函數
def echo(arg):return arg ?# 用關鍵字 return 定義函數的返回 ? # 調用函數,并用一個變量接收其返回值 ret = echo('yangge') print(ret) ? # 執行 print 的結果 'yangge'? # 當一個函數沒有顯式的調用 return 時,默認會返回 None def do_nothing():print('ok') ? # 下面的方式,會先執行函數體內的代碼,之后把函數自身的返回值 # 放在其函數原來的位置,并且作為參數給 print print(do_nothing()) ok ? ? ? ? ? ? ? ? ? ? ? ? ? ?# 函數體本身執行的結果 None ? ? ? ? ? ? ? ? ? ? ? ? ?# 函數自身的返回值# 函數可以返回一個以及多個Python 的任何數據類型的結果
8、函數的參數
以下是重點,需要加強練習,理解原理,掌握用法和技巧
-
函數的參數是為函數體內的邏輯代碼提供數據的。
-
函數的參數形式有 形參和實參
-
分類有:
-
普通參數
-
默認參數
-
動態參數
-
1. ?什么是形參
對于函數來說,形式參數簡稱形參,是指在定義函數時,定義的一個變量名;
下面的代碼中,x、y、z 就是形參
def foo(x, y, z):print("第一個參數是", x)print("第二個參數是", y)print("第三個參數是", z)
2. 什么是實參
對于函數來說,實際參數簡稱實參。 是指在調用函數時傳入的實際的數據,這會被綁定到函數的形參上; 函數調用時,將值綁定到變量名上,函數調用結束,解除綁定,并且實參將不再存在于程序中。
foo(1,2,3)
上面的 1、 ?2 ?和 3 都是實參
3. 形參分為:位置參數和默認參數
a. 位置參數
def send_mail(to, title, content):send_info = {"user": to,"title": title,"content": content}return send_info
形參的位置參數,在調用函數時必須給其傳遞實參。 但是,在函數內使用與否 ,沒有限制。
b. 默認參數
def default_mail(title, ?content, ?to='xiannv@163.com'):send_info = {"user": to,"title": title,"content": content}return send_info
默認參數在函數定義的時候,就已經綁定了實際的值了。 調用函數時,可以給其傳值,也可以不傳。
不傳值 ? 就使用定義函數時綁定的值。
傳值 ? ? ?就是使用傳遞的值。
同樣,在函數內使用與否 ,沒有限制。
4. 實參分為:位置參數和關鍵字參數
說的實參實質就是在調用函數時,給位置參數的進行賦值,這個行為通常叫做給函數 傳參
因此,大家要明白,這里的 位置參數
和 關鍵字參數
是在函數調用的情況下的概念。
a. 位置參數傳遞參數
給形參的位置參數傳參
info = send_mail("xiannv@163.com", "一生問候", "告訴你,一想到你,我這張丑臉上就泛起微笑。") print(info)
還可以這樣傳參
def myfunc(x, y, z):print(x, y, z) ? tuple_vec = (1, 0, 1) dict_vec = {'x': 1, 'y': 0, 'z': 1} ? >>> myfunc(*tuple_vec) 1, 0, 1 ? >>> myfunc(**dict_vec) 1, 0, 1
給默認參數傳參
info = default_mail( "一生問候", """因為你太美好了,我等你等了這么久,才能跟你在一起,我害怕得不得了,生怕自己搞砸了""","xiannv@163.com", ) ? print(info)
當使用實參的位置參數傳參時,需要考慮到定義函數時的形參的位置。
b. 關鍵字參數傳遞參數
info = send_mail(content="我愛你,不光是因為你的樣子,還因為和你在一起時,我的樣子", title="一生問候",to="jingjing@126.com") ? print(info)
使用關鍵字參數傳參時,不必考慮形參的位置。但是需要注意,關鍵字必須和在定義函數時形參一致。
實參角度的參數結論:
-
在調用函數時,給形參的位置參數或形參的默認參數進行賦值時,可以用實參的位置參數進行傳參; 此時傳遞的實參的順序,必須和形參的位置順序一一對應
-
在調用函數時,給形參的位置參數或形參的默認參數進行賦值時, 也可以使用實參的關鍵字參數進行傳參; 此時,關鍵字參數之間是不區分位置順序的
-
在調用函數時,形參的默認參數不傳參數給它,此時,默認參數的值就是原來定義的值;假如傳了參數,就使用傳入的參數的值。
5. 萬能參數: *args
和 **kwargs
(動態參數)
a. ?用 *
表達式接收傳進來的任意多個未明確定義的位置參數
def foo(x, y, *args):print(args) ? foo(1, 3, 5, 6) list1 = [1,2,3,4,5] ? foo(*list1) foo(list1) ? ? def foo(x, y, *args):print(args)print(x)print(y) # foo(1, 3, 5, 6) # foo(1,2,3,4,5,6) # print(foo(1,2,3,4,5)) ? list1=['a','b','c','d'] t1=('a','b','c','d') s1='helloworld' #foo(list1,t1) foo(1,2,list1,t1) ?# 不是序列的可以不用加* foo(1,2,*list1) ? #只有在傳入序列的時候加* ? # 輸出如下 (['a', 'b', 'c', 'd'], ('a', 'b', 'c', 'd')) 1 2 ('a', 'b', 'c', 'd')+ 1 2
在函數體內接收到的參數會放在元組中
b. 用 **
表達式接收傳進來的任意多個未明確定義的關鍵字參數
def func(x, y, **kwargs):print(kwargs) ? func(1, 3, name='shark', age=18) ? func(1,2,3,4,5) ?#是否會報錯 ? dict1={'k1':'a','k2':'b'} func(1,2,**dict1) ? def func(x,y,*args,**kwargs):print(kwargs)print(args)print(x)print(y) ? #func(1,2,3,4) func(1,3,name='aaa',age=18) d2 = {'k1':'aaa','k2':'18'} l1 = [1,2,3,4] print(type(d2)) func(1,2,*l1,**d2) ?# 加*號調用,就會以序列的元素為元素組成一個元祖 func(1,2,l1) ? ? ? ?# 直接調用序列,會把整個序列作為元祖的一個元素 ? # 輸出如下 {'age': 18, 'name': 'aaa'} () 1 3 <type 'dict'> {'k2': '18', 'k1': 'aaa'} (1, 2, 3, 4) 1 2 {} ([1, 2, 3, 4],) 1 2
在函數體內接收到的參數會放在字典中
相結合的方式 def func1(*args,**kwargs):print(args)print(kwargs) ? func1(1,2,3) func1(k1=1,k2=2) func1(1,2,3,k1='a',k2='b')
6、重要的東西,需要總結一下吧
函數參數從形參角度來說,就是在定義函數時定義的變量名,分為三種:
-
位置參數
如: arg1,arg2
所用的參數是有位置的特性的,位置是固定的,傳參時的值也是要一一對應的
-
默認參數
如:arg = 5 -
動態參數
如: *args,**kwargs, 可以同時用
三種形參在定義函數時的順序如下:
In [108]: def position_arg(arg1, arg2, *args, **kwargs):...: ? ? pass...:
函數參數從實參角度來說,就是在調用函數時給函數傳入的實際的值,分為兩種
-
位置參數
在傳參時,值和定義好的形參有一一對應的關系
-
關鍵字參數
在傳參時,利用key = value 的方式傳參,沒有對位置的要求