python學習筆記(五):裝飾器、生成器、內置函數、json
一、裝飾器
裝飾器,這個器就是函數的意思,連起來,就是裝飾函數,裝飾器本身也是一個函數,它的作用是用來給其他函數添加新功能,比如說,我以前寫了很多代碼,系統已經上線了,但是性能比較不好,現在想把程序裏面每個函數都加一個功能,用來統計每個函數的運行時間是多少,找出來運行比較慢的函數,來優化代碼,就需要添加一個新的功能,來統計程序的運行時間,那這樣的話,就得修改每個函數了,需要改代碼,但是代碼特別多,改完了公司倒閉了,這時候裝飾器就能排上用場了,它可以不改變原有的函數,原來的函數和原來一模一樣,什麽都不需要改變,只需要在函數外部加上調用哪個裝飾器就可以了。so,裝飾器的作用就是不改變原來函數的調用方式,不改變原來函數的代碼,給它增加了一個新功能。但是不改變函數,給它增加新功能,那是不可能的,裝飾器只不過是偷偷改變了原來的函數而已,而原來的函數不知不覺。
使用裝飾器需要了解的知識:
1、函數即變量,這個是什麽意思呢,在python裏面函數就是一個變量,函數名就是一個變量,這個函數名裏面存的是這個函數的內存地址,它把函數體放到內存裏,在調用的時候從函數名裏面的這個內存地址找到函數體然後運行這個函數。前面的博客說函數的時候,說過函數名後面加上小括號就是調用這個函數,如果只寫這個函數名的話,打印一下就是這個函數的內存地址。
def test(): print(‘nihao‘) prinit(test)#打印函數的內存地址 test()#調用函數
運行結果:
<function test at 0x100699950> nihao
2、高階函數,高階函數在上篇博客裏面寫了,如果函數的入參是一個函數的話,那麽這個函數就是一個高階函數。
3、函數嵌套,函數嵌套,函數嵌套就是在函數裏面再定義一個函數,這就是函數嵌套,而不是說在函數裏面再調用一個函數。
def test(): def test1(): print(‘test1‘) print(‘test‘)
了解了上面的這些知識之後,就可以使用裝飾器了,下面我寫一個簡單的裝飾器,用來統計函數的運行時間,然後將被統計的函數作為參數傳遞
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()的代碼完全不用修改!
import time 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 def bar(): time.sleep(3) print(‘in the bar‘) bar=timmer(bar) bar()
運行結果:
in the bar the func run time is 3.000171661376953
函數timmer就是裝飾器,它把執行真正業務方法的func包裹在函數裏面,看起來像bar被timmer裝飾了。如果我們要定義函數時使用裝飾器,避免使用賦值語句bar=timmer(bar),要用到裝飾器的語法糖@,語法糖的意思就是一種語法的簡寫,它使代碼看起來簡潔,上面的bar=timmer(bar)和@timmter bar()是一樣的。這樣寫的話,看起來也比較高大上一些。
import time 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 @timmer def bar(): time.sleep(3) print(‘in the bar‘) bar()
運行結果:
in the bar the func run time is 3.000171661376953
這樣,我們就提高了程序的可重復利用性,當其他函數需要調用裝飾器時,可以直接調用。裝飾器在Python使用如此方便都要歸因於Python的函數能像普通的對象一樣能作為參數傳遞給其他函數,可以被賦值給其他變量,可以作為返回值,可以被定義在另外一個函數內。
如果要裝飾的函數帶有參數時,因為你也不知道到底被裝飾的函數會傳什麽參數,所以可以使用可變參數和關鍵字參數來接收所有的參數,這兩種參數也在上一篇博客中寫過了。代碼如下:
import time def timmer(func): def deco(*arg,**kwarg): start_time=time.time() func(*arg,**kwarg) stop_time=time.time() print(‘the func run time is %s‘%(stop_time-start_time)) return deco @timmer def test1(): time.sleep(1) print(‘in the test1‘) @timmer 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
下面再用裝飾器,寫一個實例,判斷用戶是否登陸,邏輯是這樣,運行程序,打印菜單,如果是選擇後臺管理和添加商品,就判斷用戶是否登錄,如果沒有登錄的話,讓用戶登錄,如果是查看商品就不需要登錄。
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!‘) @auth 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]()#調用對應的函數 else: 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方法
g=(x*x for x in range(1,11)) print(next(g)) print(next(g)) print(next(g)) print(next(g))
運行結果:
1 4 9 16
用函數生成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 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(x.__next__()) print(x.__next__()) 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處理
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數據,然後轉成字典
python學習筆記(五):裝飾器、生成器、內置函數、json