一,文件操做基本流程。
計算機系統分爲:計算機硬件,操做系統,應用程序三部分。html
咱們用python或其餘語言編寫的應用程序若想要把數據永久保存下來,必需要保存於硬盤中,這就涉及到應用程序要操做硬件,衆所周知,應用程序是沒法直接操做硬件的,這就用到了操做系統。操做系統把複雜的硬件操做封裝成簡單的接口給用戶/應用程序使用,其中文件就是操做系統提供給應用程序來操做硬盤虛擬概念,用戶或應用程序經過操做文件,能夠將本身的數據永久保存下來。python
有了文件的概念,咱們無需再去考慮操做硬盤的細節,只須要關注操做文件的流程:linux
#1. 打開文件,獲得文件句柄並賦值給一個變量 f=open('a.txt','r',encoding='utf-8') #默認打開模式就爲r #2. 經過句柄對文件進行操做 data=f.read() #3. 關閉文件 f.close()
關閉文件的注意事項:
打開一個文件包含兩部分資源:操做系統級打開的文件+應用程序的變量。在操做完畢一個文件時,必須把與該文件的這兩部分資源一個不落地回收,回收方法爲: 一、f.close() #回收操做系統級打開的文件 二、del f #回收應用程序級的變量 其中del f必定要發生在f.close()以後,不然就會致使操做系統打開的文件尚未關閉,白白佔用資源, 而python自動的垃圾回收機制決定了咱們無需考慮del f,這就要求咱們,在操做完畢文件後,必定要記住f.close() 雖然我這麼說,可是不少同窗仍是會很不要臉地忘記f.close(),對於這些不長腦子的同窗,咱們推薦傻瓜式操做方式:使用with關鍵字來幫咱們管理上下文 with open('a.txt','w') as f: pass with open('a.txt','r') as read_f,open('b.txt','w') as write_f: data=read_f.read() write_f.write(data) 注意
二,文件編碼
f=open(...)是由操做系統打開文件,那麼若是咱們沒有爲open指定編碼,那麼打開文件的默認編碼很明顯是操做系統說了算了,操做系統會用本身的默認編碼去打開文件,在windows下是gbk,在linux下是utf-8。vim
#這就用到了上節課講的字符編碼的知識:若要保證不亂碼,文件以什麼方式存的,就要以什麼方式打開。 f=open('a.txt','r',encoding='utf-8')
三,文件的打開模式
文件句柄 = open(‘文件路徑’,‘模式’)windows
#1. 打開文件的模式有(默認爲文本模式): r ,只讀模式【默認模式,文件必須存在,不存在則拋出異常】 w,只寫模式【不可讀;不存在則建立;存在則清空內容】 a, 只追加寫模式【不可讀;不存在則建立;存在則只追加內容】 #2. 對於非文本文件,咱們只能使用b模式,"b"表示以字節的方式操做(而全部文件也都是以字節的形式存儲的,使用這種模式無需考慮文本文件的字符編碼、圖片文件的jgp格式、視頻文件的avi格式) rb wb ab 注:以b方式打開時,讀取到的內容是字節類型,寫入時也須要提供字節類型,不能指定編碼 #3,‘+’模式(就是增長了一個功能) r+, 讀寫【可讀,可寫】 w+,寫讀【可寫,可讀】 a+, 寫讀【可寫,可讀】 #4,以bytes類型操做的讀寫,寫讀,寫讀模式 r+b, 讀寫【可讀,可寫】 w+b,寫讀【可寫,可讀】 a+b, 寫讀【可寫,可讀】
四,文件操做方法。
4.1經常使用操做方法。
read(3):緩存
1. 文件打開方式爲文本模式時,表明讀取3個字符閉包
2. 文件打開方式爲b模式時,表明讀取3個字節app
其他的文件內光標移動都是以字節爲單位的如:seek,tell,truncate編輯器
注意:函數
1. seek有三種移動方式0,1,2,其中1和2必須在b模式下進行,但不管哪一種模式,都是以bytes爲單位移動的
2. truncate是截斷文件,因此文件的打開方式必須可寫,可是不能用w或w+等方式打開,由於那樣直接清空文件了,因此truncate要在r+或a或a+等模式下測試效果。
五,文件的修改。
文件的數據是存放於硬盤上的,於是只存在覆蓋、不存在修改這麼一說,咱們平時看到的修改文件,都是模擬出來的效果,具體的說有兩種實現方式:
方式一:將硬盤存放的該文件的內容所有加載到內存,在內存中是能夠修改的,修改完畢後,再由內存覆蓋到硬盤(word,vim,nodpad++等編輯器)
import os # 調用系統模塊 with open('a.txt') as read_f,open('.a.txt.swap','w') as write_f: data=read_f.read() #所有讀入內存,若是文件很大,會很卡 data=data.replace('alex','SB') #在內存中完成修改 write_f.write(data) #一次性寫入新文件 os.remove('a.txt') #刪除原文件 os.rename('.a.txt.swap','a.txt') #將新建的文件重命名爲原文件
方式二:將硬盤存放的該文件的內容一行一行地讀入內存,修改完畢就寫入新文件,最後用新文件覆蓋源文件
import os with open('a.txt') as read_f,open('.a.txt.swap','w') as write_f: for line in read_f: line=line.replace('alex','SB') write_f.write(line) os.remove('a.txt') os.rename('.a.txt.swap','a.txt')
python之函數初識
一,什麼是函數?
如今有這麼個狀況:python中的len方法不讓用了,你怎麼辦?
來測試一下‘hello word’ 的長度:
s1 = "hello world" length = 0 for i in s1: length = length+1 print(length)
OK,完事兒了,很是完美。可是主管又提出一個需求,要計算兩外一個字符串的長度:‘hello china’,
因而你的代碼就變成了這樣:
s1 = "hello world" length = 0 for i in s1: length = length+1 print(length) s2 = "hello china" length = 0 for i in s2: length = length+1 print(length)
這樣確實能夠實現len方法的效果,可是總感受不是那麼完美?爲何呢?
首先,以前只要咱們執行len方法就能夠直接拿到一個字符串的長度了,如今爲了實現相同的功能咱們把相同的代碼寫了好多遍 —— 代碼冗餘
其次,以前咱們只寫兩句話讀起來也很簡單,一看就知道這兩句代碼是在計算長度,可是剛剛的代碼卻不那麼容易讀懂 —— 可讀性差
因此,咱們就想能不能用一段代碼,來實現相同的功能,好比,我們也寫一個len()叫作my_len()能夠代替len()的方法,既解決了代碼冗餘,有讓其可讀性好,用什麼方法呢? 這就引出了我們今天講的----函數。
函數是組織好的,可重複使用的,用來實現單一,或相關聯功能的代碼段。
函數能提升應用的模塊性,和代碼的重複利用率。你已經知道Python提供了許多內建函數,好比print(),len()等。但你也能夠本身建立函數,這被叫作用戶自定義函數。
二, 函數的定義與調用。
#函數定義 def mylen(): """計算s1的長度""" s1 = "hello world" length = 0 for i in s1: length = length+1 print(length) #函數調用 mylen()
上面就是我們寫了一個函數,而且成功的調用了它。
定義:def 關鍵詞開頭,空格以後接函數名稱和圓括號(),最後還有一個":"。 def 是固定的,不能變,他就是定義函數的關鍵字。 空格 爲了將def關鍵字和函數名分開,必須空(四聲),固然你能夠空2格、3格或者你想空多少都行,但正常人仍是空1格。 函數名:函數名只能包含字符串、下劃線和數字且不能以數字開頭。雖然函數名能夠隨便起,但咱們給函數起名字仍是要儘可能簡短,並能表達函數功能 括號:是必須加的,先別問爲啥要有括號,總之加上括號就對了! 註釋:每個函數都應該對功能和參數進行相應的說明,應該寫在函數下面第一行。以加強代碼的可讀性。 調用:就是 函數名() 要記得加上括號。
三,函數的返回值。
我們用len()方法時,獲得的結果會賦值給一個變量,而後獲得結果:
str_len = len('hello,world') print(str_len)
可是我們寫的這個函數,並不會獲得這樣的結果,如何讓他和len函數同樣,有返回值呢?
那就是在函數的最後加上一個return,return 後面寫你須要的返回值就能夠了。
#函數定義 def mylen(): """計算s1的長度""" s1 = "hello world" length = 0 for i in s1: length = length+1 return length #函數調用 str_len = mylen() print('str_len : %s'%str_len)
下面我們重點研究return關鍵字的做用:
return關鍵字的做用
return 是一個關鍵字,在pycharm裏,你會看到它變成藍色了。你必須一字不差的把這個單詞給背下來。
這個詞翻譯過來就是「返回」,因此咱們管寫在return後面的值叫「返回值」
要研究返回值,咱們還要知道返回值有幾種狀況:分別是沒有返回值、返回一個值、返回多個值
沒有返回值
不寫return的狀況下,會默認返回一個None:咱們寫的第一個函數,就沒有寫return,這就是沒有返回值的一種狀況。
#函數定義 def mylen(): """計算s1的長度""" s1 = "hello world" length = 0 for i in s1: length = length+1 print(length) #函數調用 str_len = mylen() #由於沒有返回值,此時的str_len爲None print('str_len : %s'%str_len)
只寫return,後面不寫其餘內容,也會返回None,有的同窗會奇怪,既然沒有要返回的值,徹底能夠不寫return,爲何還要寫個return呢?這裏咱們要說一下return的其餘用法,就是一旦遇到return,結束整個函數。
def ret_demo(): print(111) return print(222) ret = ret_demo() print(ret)
return None:和上面狀況同樣,咱們通常不這麼寫。
返回一個值
剛剛咱們已經寫過一個返回一個值的狀況,只需在return後面寫上要返回的內容便可。
#函數定義 def mylen(): """計算s1的長度""" s1 = "hello world" length = 0 for i in s1: length = length+1 return length #函數調用 str_len = mylen() print('str_len : %s'%str_len)r
返回多個值
能夠返回任意多個、任意數據類型的值
def ret_demo1(): '''返回多個值''' return 1,2,3,4 def ret_demo2(): '''返回多個任意類型的值''' return 1,['a','b'],3,4 ret1 = ret_demo1() print(ret1) ret2 = ret_demo2() print(ret2) 返回多個值
返回的多個值會被組織成元組被返回,也能夠用多個值來接收
def ret_demo2(): return 1,['a','b'],3,4 #返回多個值,用一個變量接收 ret2 = ret_demo2() print(ret2) #返回多個值,用多個變量接收 a,b,c,d = ret_demo2() print(a,b,c,d) #用多個值接收返回值:返回幾個值,就用幾個變量接收 a,b,c,d = ret_demo2() print(a,b,c,d) 多個返回值的接收
四,函數的參數。
如今,咱們已經把函數返回值相關的事情研究清楚了,咱們本身已經完成了一個能夠返回字符串長度的函數。可是如今這個函數仍是不完美,以前咱們使用len函數的時候得是length = len("hello world"),這樣我能夠想計算誰就計算誰的長度。可是如今咱們寫的這個函數,只能計算一個「hello world」的長度,換一個字符串好像就是不行了。這可怎麼辦?
#函數定義 def mylen(s1): """計算s1的長度""" length = 0 for i in s1: length = length+1 return length #函數調用 str_len = mylen("hello world") print('str_len : %s'%str_len)
咱們告訴mylen函數要計算的字符串是誰,這個過程就叫作 傳遞參數,簡稱傳參,咱們調用函數時傳遞的這個「hello world」和定義函數時的s1就是參數。
實參與形參
參數還有分別:
咱們調用函數時傳遞的這個「hello world」被稱爲實際參數,由於這個是實際的要交給函數的內容,簡稱實參。
定義函數時的s1,只是一個變量的名字,被稱爲形式參數,由於在定義函數的時候它只是一個形式,表示這裏有一個參數,簡稱形參。
傳遞多個參數
參數能夠傳遞多個,多個參數之間用逗號分割。
def mymax(x,y): the_max = x if x > y else y return the_max ma = mymax(10,20) print(ma)
位置參數
站在實參角度
1.按照位置傳值
def mymax(x,y): #此時x=10,y=20 the_max = x if x > y else y return the_max ma = mymax(10,20) print(ma)
2.按照關鍵字傳值
def mymax(x,y): #此時x = 20,y = 10 print(x,y) the_max = x if x > y else y return the_max ma = mymax(y = 10,x = 20) print(ma)
3.位置、關鍵字形式混着用
def mymax(x,y): #此時x = 10,y = 20 print(x,y) the_max = x if x > y else y return the_max ma = mymax(10,y = 20) print(ma)
正確用法
問題一:位置參數必須在關鍵字參數的前面
問題二:對於一個形參只能賦值一次
站在形參角度
位置參數必須傳值
def mymax(x,y): #此時x = 10,y = 20 print(x,y) the_max = x if x > y else y return the_max #調用mymax不傳遞參數 ma = mymax() print(ma) #結果 TypeError: mymax() missing 2 required positional arguments: 'x' and 'y'
默認參數
1.正常使用
使用方法
爲何要有默認參數:將變化比較小的值設置成默認參數
2.默認參數的定義
def stu_info(name,sex = "male"): """打印學生信息函數,因爲班中大部分學生都是男生, 因此設置默認參數sex的默認值爲'male' """ print(name,sex) stu_info('alex') stu_info('eva','female')
3.參數陷阱:默認參數是一個可變數據類型
def defult_param(a,l = []): l.append(a) print(l) defult_param('alex') defult_param('egon')
動態參數
def trans_para(*args,**kwargs): print(args,type(args)) print(kwargs,type(kwargs)) trans_para("jinxin",12,[1,2,3,4],[3,4,],(1,4,7),{"a":"123","c":456},country="china") 動態參數,也叫不定長傳參,就是你須要傳給函數的參數不少,不定個數,那這種狀況下,你就用*args,**kwargs接收,args是元祖形式,接收除去鍵值對之外的全部參數,kwargs接收的只是鍵值對的參數,並保存在字典中。
python之函數進階
如今我有個問題,函數裏面的變量,在函數外面能直接引用麼?
def func1(): m = 1 print(m) print(m) #這行報的錯 報錯了: NameError: name 'm' is not defined
上面爲何會報錯呢?如今咱們來分析一下python內部的原理是怎麼樣:
咱們首先回憶一下Python代碼運行的時候遇到函數是怎麼作的,從Python解釋器開始執行以後,就在內存中開闢裏一個空間,每當遇到一個變量的時候,就把變量名和值之間對應的關係記錄下來,可是當遇到函數定義的時候,解釋器只是象徵性的將函數名讀如內存,表示知道這個函數存在了,至於函數內部的變量和邏輯,解釋器根本不關心。
等執行到函數調用的時候,Python解釋器會再開闢一塊內存來儲存這個函數裏面的內容,這個時候,才關注函數裏面有哪些變量,而函數中的變量回儲存在新開闢出來的內存中,函數中的變量只能在函數內部使用,而且會隨着函數執行完畢,這塊內存中的全部內容也會被清空。
咱們給這個‘存放名字與值的關係’的空間起了一個名字-------命名空間。
代碼在運行伊始,建立的存儲「變量名與值的關係」的空間叫作全局命名空間;
在函數的運行中開闢的臨時的空間叫作局部命名空間。
二,命名空間和做用域
在python之禪中提到過:命名空間是一種絕妙的理念,讓咱們盡情的使用發揮吧!
命名空間一共分爲三種:
全局命名空間
局部命名空間
內置命名空間
*內置命名空間中存放了python解釋器爲咱們提供的名字:input,print,str,list,tuple...它們都是咱們熟悉的,拿過來就能夠用的方法。
三種命名空間之間的加載與取值順序:
加載順序:內置命名空間(程序運行前加載)->全局命名空間(程序運行中:從上到下加載)->局部命名空間(程序運行中:調用時才加載)
取值順序:
在局部調用:局部命名空間->全局命名空間->內置命名空間
在全局調用:全局命名空間->內置命名空間
綜上所述,在找尋變量時,從小範圍,一層一層到大範圍去找尋。
做用域
做用域就是做用範圍,按照生效範圍能夠分爲全局做用域和局部做用域。
全局做用域:包含內置名稱空間、全局名稱空間,在整個文件的任意位置都能被引用、全局有效
局部做用域:局部名稱空間,只能在局部範圍內生效
globals和locals方法
print(globals()) print(locals())
def func(): a = 12 b = 20 print(locals()) print(globals()) func()
global關鍵字,nonlocal關鍵字。
global:
1,聲明一個全局變量。
2,在局部做用域想要對全局做用域的全局變量進行修改時,須要用到 global(限於字符串,數字)。
def func(): global a a = 3 func() print(a) count = 1 def search(): global count count = 2 search() print(count)
ps:對可變數據類型(list,dict,set)能夠直接引用不用經過global。
li = [1,2,3] dic = {'a':'b'} def change(): li.append('a') dic['q'] = 'g' print(dic) print(li) change() print(li) print(dic)
nonlocal:
1,不能修改全局變量。
2,在局部做用域中,對父級做用域(或者更外層做用域非全局做用域)的變量進行引用和修改,而且引用的哪層,從那層及如下此變量所有發生改 變。
def add_b(): b = 42 def do_global(): b = 10 print(b) def dd_nonlocal(): nonlocal b b = b + 20 print(b) dd_nonlocal() print(b) do_global() print(b) add_b()
三,函數的嵌套和做用域鏈
函數的嵌套調用
def max2(x,y): m = x if x>y else y return m def max4(a,b,c,d): res1 = max2(a,b) res2 = max2(res1,c) res3 = max2(res2,d) return res3 # max4(23,-7,31,11)
函數的嵌套定義
def f1(): print("in f1") def f2(): print("in f2") f2() f1() ########### def f1(): def f2(): def f3(): print("in f3") print("in f2") f3() print("in f1") f2() f1()
函數的做用域鏈:小範圍做用域可使用大範圍的變量,可是反之不行,他是單向的。
def f1(): a = 1 def f2(): def f3(): print(a) f3() f2() f1() ################ def f1(): a = 1 def f2(): a = 2 f2() print('a in f1 : ',a) f1()
四,函數名的本質。
函數名本質上就是函數的內存地址。
1.能夠被引用
def func(): print('in func') f = func print(f)
2.能夠被看成容器類型的元素
def f1(): print('f1') def f2(): print('f2') def f3(): print('f3') l = [f1,f2,f3] d = {'f1':f1,'f2':f2,'f3':f3} #調用 l[0]() d['f2']()
3.能夠看成函數的參數和返回值
def f1(): print('f1') def func1(argv): argv() return argv f = func1(f1) f()
第一類對象(first-class object)指 1.可在運行期建立 2.可用做函數參數或返回值 3.可存入變量的實體。
*不明白?那就記住一句話,就當普通變量用
五,閉包
def func(): name = '太白金星' def inner(): print(name)
閉包函數:
內部函數包含對外部做用域而非全劇做用域變量的引用,該內部函數稱爲閉包函數
#函數內部定義的函數稱爲內部函數
因爲有了做用域的關係,咱們就不能拿到函數內部的變量和函數了。若是咱們就是想拿怎麼辦呢?返回呀!
咱們都知道函數內的變量咱們要想在函數外部用,能夠直接返回這個變量,那麼若是咱們想在函數外部調用函數內部的函數呢?
是否是直接就把這個函數的名字返回就行了?
這纔是閉包函數最經常使用的用法
def func(): name = 'eva' def inner(): print(name) return inner f = func() f()
判斷閉包函數的方法__closure__
#輸出的__closure__有cell元素 :是閉包函數 def func(): name = 'eva' def inner(): print(name) print(inner.__closure__) return inner f = func() f() #輸出的__closure__爲None :不是閉包函數 name = 'egon' def func2(): def inner(): print(name) print(inner.__closure__) return inner f2 = func2() f2()
def wrapper(): money = 1000 def func(): name = 'eva' def inner(): print(name,money) return inner return func f = wrapper() i = f() i()
from urllib.request import urlopen def index(): url = "http://www.xiaohua100.cn/index.html" def get(): return urlopen(url).read() return get xiaohua = index() content = xiaohua() print(content)
python之裝飾器
一,什麼是裝飾器?
裝飾器本質上就是一個python函數,他可讓其餘函數在不須要作任何代碼變更的前提下,增長額外的功能,裝飾器的返回值也是一個函數對象。
裝飾器的應用場景:好比插入日誌,性能測試,事務處理,緩存等等場景。
二,裝飾器的造成過程。
如今我有一個需求,我想讓你測試這個函數的執行時間,在不改變這個函數代碼的狀況下:
import time def func1(): print('in func1') def timer(func): def inner(): start = time.time() func() print(time.time() - start) return inner func1 = timer(func1) func1()
可是若是有多個函數,我都想讓你測試他們的執行時間,你每次是否是都得func1 = timer(func1)?這樣仍是有點麻煩,由於這些函數的函數名多是不相同,有func1,func2,graph,等等,因此更簡單的方法,python給你提供了,那就是語法糖。
import time def timer(func): def inner(): start = time.time() func() print(time.time() - start) return inner @timer #==> func1 = timer(func1) def func1(): print('in func1') func1()
剛剛咱們討論的裝飾器都是裝飾不帶參數的函數,如今要裝飾一個帶參數的函數怎麼辦呢?
def timer(func): def inner(a): start = time.time() func(a) print(time.time() - start) return inner @timer def func1(a): print(a) func1(1) 裝飾器——帶參數的裝飾器
import time def timer(func): def inner(*args,**kwargs): start = time.time() re = func(*args,**kwargs) print(time.time() - start) return re return inner @timer #==> func1 = timer(func1) def func1(a,b): print('in func1') @timer #==> func2 = timer(func2) def func2(a): print('in func2 and get a:%s'%(a)) return 'fun2 over' func1('aaaaaa','bbbbbb') print(func2('aaaaaa'))
上面的裝飾器已經很是完美了,可是有咱們正常狀況下查看函數信息的方法在此處都會失效:
def index(): '''這是一個主頁信息''' print('from index') print(index.__doc__) #查看函數註釋的方法 print(index.__name__) #查看函數名的方法
如何解決呢?
from functools import wraps def deco(func): @wraps(func) #加在最內層函數正上方 def wrapper(*args,**kwargs): return func(*args,**kwargs) return wrapper @deco def index(): '''哈哈哈哈''' print('from index') print(index.__doc__) print(index.__name__)
三,開放封閉原則。
1.對擴展是開放的
爲何要對擴展開放呢?
咱們說,任何一個程序,不可能在設計之初就已經想好了全部的功能而且將來不作任何更新和修改。因此咱們必須容許代碼擴展、添加新功能。
2.對修改是封閉的
爲何要對修改封閉呢?
就像咱們剛剛提到的,由於咱們寫的一個函數,頗有可能已經交付給其餘人使用了,若是這個時候咱們對其進行了修改,頗有可能影響其餘已經在使用該函數的用戶。
裝飾器完美的遵循了這個開放封閉原則。
四,裝飾器的主要功能和固定結構。
def timer(func): def inner(*args,**kwargs): '''執行函數以前要作的''' re = func(*args,**kwargs) '''執行函數以後要作的''' return re return inner
from functools import wraps def deco(func): @wraps(func) #加在最內層函數正上方 def wrapper(*args,**kwargs): return func(*args,**kwargs) return wrapper
五,帶參數的裝飾器。
假如你有成千上萬個函數使用了一個裝飾器,如今你想把這些裝飾器都取消掉,你要怎麼作?
一個一個的取消掉? 沒日沒夜忙活3天。。。
過兩天你領導想通了,再讓你加上。。。
def outer(flag): def timer(func): def inner(*args,**kwargs): if flag: print('''執行函數以前要作的''') re = func(*args,**kwargs) if flag: print('''執行函數以後要作的''') return re return inner return timer @outer(False) def func(): print(111) func()
六,多個裝飾器裝飾一個函數。
def outer(flag): def timer(func): def inner(*args,**kwargs): if flag: print('''執行函數以前要作的''') re = func(*args,**kwargs) if flag: print('''執行函數以後要作的''') return re return inner return timer @outer(False) def func(): print(111) func() 複製代碼 返回頂部 多個裝飾器裝飾同一個函數 有些時候,咱們也會用到多個裝飾器裝飾同一個函數的狀況。 複製代碼 def wrapper1(func): def inner(): print('wrapper1 ,before func') func() print('wrapper1 ,after func') return inner def wrapper2(func): def inner(): print('wrapper2 ,before func') func() print('wrapper2 ,after func') return inner @wrapper2 @wrapper1 def f(): print('in f') f()