這周學習了裝飾器和生成器,寫下部落格,記錄一下裝飾器和生成器相關的内容。
一、裝飾器
裝飾器,這個器就是函數的意思,連起來,就是裝飾函數,裝飾器本身也是一個函數,它的作用是用來給其他函數添加新功能,比如說,我以前寫了很多代碼,系統已經上線了,但是性能比較不好,現在想把程式裡面每個函數都加一個功能,用來統計每個函數的運作時間是多少,找出來運作比較慢的函數,來優化代碼,就需要添加一個新的功能,來統計程式的運作時間,那這樣的話,就得修改每個函數了,需要改代碼,但是代碼特别多,改完了公司倒閉了,這時候裝飾器就能排上用場了,它可以不改變原有的函數,原來的函數和原來一模一樣,什麼都不需要改變,隻需要在函數外部加上調用哪個裝飾器就可以了。so,裝飾器的作用就是不改變原來函數的調用方式,不改變原來函數的代碼,給它增加了一個新功能。但是不改變函數,給它增加新功能,那是不可能的,裝飾器隻不過是偷偷改變了原來的函數而已,而原來的函數不知不覺。
使用裝飾器需要了解的知識:
1、函數即變量,這個是什麼意思呢,在python裡面函數就是一個變量,函數名就是一個變量,這個函數名裡面存的是這個函數的記憶體位址,它把函數體放到記憶體裡,在調用的時候從函數名裡面的這個記憶體位址找到函數體然後運作這個函數。前面的部落格說函數的時候,說過函數名後面加上小括号就是調用這個函數,如果隻寫這個函數名的話,列印一下就是這個函數的記憶體位址。
1 2 3 4 | def test(): print('nihao') prinit(test)#列印函數的記憶體位址 test()#調用函數 |
運作結果:
<function test at 0x100699950> nihao |
2、高階函數,高階函數在上篇部落格裡面寫了,如果函數的入參是一個函數的話,那麼這個函數就是一個高階函數。
3、函數嵌套,函數嵌套,函數嵌套就是在函數裡面再定義一個函數,這就是函數嵌套,而不是說在函數裡面再調用一個函數。
def test(): def test1(): print('test1') print('test') |
了解了上面的這些知識之後,就可以使用裝飾器了,下面我寫一個簡單的裝飾器,用來統計函數的運作時間,然後将被統計的函數作為參數傳遞
5 6 7 8 9 10 | import time def bar(): time.sleep(3) print('in the bar') def test1(func): start_time=time.time() func() stop_time = time.time() print('the func run time is %s' % (stop_time - start_time)) test1(bar) |
in the bar the func run time is 3.000171661376953 |
但是這樣的話,我們每次都要将一個函數作為參數傳遞給test1函數。改變了函數調用方式,之前執行業務邏輯時,執行運作bar(),但是現在不得不改成test1(bar)。此時就要用到裝飾器。我們就來想想辦法不修改調用的代碼;如果不修改調用代碼,也就意味着調用bar()需要産生調用test1(bar)的效果。我們可以想到将test1指派給bar,但是test1似乎帶有一個參數……想辦法把參數統一吧!如果test1(bar)不是直接産生調用效果,而是傳回一個與foo參數清單一緻的函數的話……就很好辦了,将test1(bar)的傳回值指派給bar,然後,調用bar()的代碼完全不用修改!
11 12 13 | def timmer(func): def deco(): start_time=time.time() func() stop_time=time.time() print('the func run time is %s'%(stop_time-start_time)) return deco bar=timmer(bar) bar() |
函數timmer就是裝飾器,它把執行真正業務方法的func包裹在函數裡面,看起來像bar被timmer裝飾了。如果我們要定義函數時使用裝飾器,避免使用指派語句bar=timmer(bar),要用到裝飾器的文法糖@,文法糖的意思就是一種文法的簡寫,它使代碼看起來簡潔,上面的bar=timmer(bar)和@timmter bar()是一樣的。這樣寫的話,看起來也比較高大上一些。
@timmer |
in the bar the func run time is 3.000171661376953 |
這樣,我們就提高了程式的可重複利用性,當其他函數需要調用裝飾器時,可以直接調用。裝飾器在Python使用如此友善都要歸因于Python的函數能像普通的對象一樣能作為參數傳遞給其他函數,可以被指派給其他變量,可以作為傳回值,可以被定義在另外一個函數内。
如果要裝飾的函數帶有參數時,因為你也不知道到底被裝飾的函數會傳什麼參數,是以可以使用可變參數和關鍵字參數來接收所有的參數,這兩種參數也在上一篇部落格中寫過了。代碼如下:
14 15 16 17 18 | def deco(*arg,**kwarg): func(*arg,**kwarg) def test1(): time.sleep(1) print('in the test1') def test2(name,age) : time.sleep(2) print('in the test2:',name,age) test1() test2('niuniu,18) |
in the test1 the func run time is 1.0000572204589844 in the test2: niuniu 18 the func run time is 2.0001144409179688 |
下面再用裝飾器,寫一個執行個體,判斷使用者是否登陸,邏輯是這樣,運作程式,列印菜單,如果是選擇背景管理和添加商品,就判斷使用者是否登入,如果沒有登入的話,讓使用者登入,如果是檢視商品就不需要登入。
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | import os def login(): ''' 登入函數,登入成功的話,寫如user這個檔案 :return: print('login') username = input('請輸入賬号') password = input('請輸入密碼') if username =='admin' and password=='123456': print('登入成功!') with open('user','a+') as fw: fw.write(username) else: print('賬号密碼錯誤!') #是否登入校驗裝飾器 def auth(func): def check(*args,**kwargs): if os.path.exists('user'):#判斷使用者檔案是否存在 func(*args,**kwargs)#存在的話,調用原來的函數 else: print('你未登陸!')#不存在的話,調用登入函數 login() return check @auth def admin(): print('welcome!') def show(): print('show!') def add(): print('add product!') def menu():#列印菜單函數 msg= ''' 1 : 背景管理 2 : 檢視商品 3 : 添加商品 print(msg) m = { "1":admin, "2":show, "3":add } choice = input('請輸入你的選擇:').strip() if choice in m: m[choice]()#調用對應的函數 print('輸入錯誤!') menu() if __name__ == '__main__': menu() |
二、生成器
生成器是什麼東西呢,可以了解為,生成器也是一個疊代的對象,和list似的,裡面存資料,和list不同的是,list在定義的時候資料就已經在記憶體裡面了,而生成器是,你用到這個裡面的資料的時候它才會生成,這樣就比較省記憶體,因為是需要這個值的時候,才會在記憶體裡面産生。生成器是按照某種規則生成的一個清單,用到這個清單的中的資料時,才會在記憶體裡生成,但是由于生成器是根據某種規則産生的,必須得知道前一個值是多少,才能知道後面的那個值是多少,是以不能像list那樣,直接用索引去取值。
1、 清單生成式,在第二篇部落格裡面我寫了三元運算符,和那個有點像,如果要生成清單[1x1, 2x2, 3x3, ..., 10x10]怎麼做?除了循環還可以用一行語句代替循環生成
list=[x*x for x in range(1,11)] print(list) [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]#運作結果 |
這種寫法就是Python的清單生成式,寫清單生成式時,把要生成的元素 x * x 放到前面,後面跟 for 循環,就可以把list建立出來。
2、生成器:要建立一個generator,有很多種方法。第一種方法很簡單,隻要把一個清單生成式的[]改成(),就建立了一個generator。
g=(x*x for x in range(1,11)) print(g) <generator object <genexpr> at 0x1036ff258>#運作結果 |
建立list和generator的差別僅在于最外層的[]和()。list的元素我們可以一個個列印出,如果要列印generator中的元素需要借助next方法
print(next(g)) |
1 4 9 16 |
但是generator儲存的是算法,每次調用next(g),就計算出g的下一個元素的值,直到計算到最後一個元素,沒有更多的元素時,抛出StopIteration的錯誤。可以通過for循環來疊代它,并且不需要關心StopIteration的錯誤。
for i in g: print(i) |
1 4 9 16 25 36 49 64 81 100 |
用函數生成generator:generator非常強大。如果推算的算法比較複雜,用類似清單生成式的for循環無法實作的時候,還可以用函數來實作。例如,斐波拉契數列用清單生成式寫不出來,但是,用函數把它列印出來卻很容易:
def fib(max): n,a,b=0,0,1 while n<max: print(b) a,b=b,a+b n=n+1 return 'done' f=fib(6) |
1 2 3 5 8 |
上面的函數和generator僅一步之遙。要把fib函數變成generator,隻需要把print(b)改為yield b就可以了:
def fib(max): n,a,b=0,0,1 while n<max: yield b a,b=b,a+b n=n+1 return 'done' x = fib(6) print(x) print(x.__next__()) |
三、内置函數
print(all([1,2,3,4]))#判斷可疊代的對象裡面的值是否都為真 print(any([0,1,2,3,4]))#判斷可疊代的對象裡面的值是否有一個為真 print(bin(10))#十進制轉二進制 print(bool('s'))#把一個對象轉換成布爾類型 print(bytearray('abcde',encoding='utf-8'))#把字元串變成一個可修改的bytes print(callable('aa'))#判斷傳入的對象是否可調用 print(chr(10))#列印數字對應的ascii print(ord('b'))#列印字元串對應的ascii碼 print(dict(a=1,b=2))#轉換字典 print(dir(1))#列印傳入對象的可調用方法 print(eval('[]'))#執行python代碼,隻能執行簡單的,定義資料類型和運算 print(exec('def a():pass'))#執行python代碼 print(filter(lambda x:x>5,[12,3,12,2,1,2,35]))#把後面的疊代對象根據前面的方法篩選 print(map(lambda x:x>5,[1,2,3,4,5,6])) print(frozenset({1,2,3,3}))#定義一個不可修改的集合 print(globals())#傳回程式内所有的變量,傳回的是一個字典 print(locals())#傳回局部變量 print(hash('aaa'))#把一個字元串哈希成一個數字 print(hex(111))#數字轉成16進制 print(max(111,12))#取最大值 print(oct(111))#把數字轉換成8進制 print(round(11.11,2))#取幾位小數 print(sorted([2,31,34,6,1,23,4]))#排序 dic={1:2,3:4,5:6,7:8} print(sorted(dic.items()))#按照字典的key排序 print(sorted(dic.items(),key=lambda x:x[1]))#按照字典的value排序 __import__('decorator')#導入一個子產品 |
四、匿名函數
如果這個函數隻執行一次的話,那就可以定義一個匿名函數,匿名函數隻能處理比較簡單的處理邏輯,隻能寫簡單的表達式,不能寫循環 判斷,比如三元運算符。
匿名函數定義使用lambda關鍵字,比如說要定義一個函數,它的功能是傳回兩個數相加和,就可以使用lambda,代碼如下:
s = lambda x,y:x+y#冒号号前面的x,y是入參,冒号後面的是傳回值 print(s(1,9))#因為函數即變量,如果沒有定一個變量把lambda存起來的話,它就不在記憶體裡,沒法執行,所有把它放到s這個變量裡面 |
五、json處理
json是一種所有語言中都通用的key-value資料結構的資料類型,很像python中的字典,json處理使用json子產品,json子產品有下面常用的方法:
import json dic = {"name":"niuniu","age":18} print(json.dumps(dic))#把字典轉成json串 fj = open('a.json','w') print(json.dump(dic,fj))#把字典轉換成的json串寫到一個檔案裡面 s_json = '{"name":"niuniu","age":20,"status":true}' print(json.loads(s_json))#把json串轉換成字典 fr = open('b.json','r') print(json.load(fr))#從檔案中讀取json資料,然後轉成字典 |