第四周 day4 python學習筆記,day4python
關於裝飾器的更多資訊可以參考http://egon09.blog.51cto.com/9161406/1836763
1.裝飾器Decorator
裝飾器:本質上是函數,(裝飾其他函數),就是為其他函數添加附加功能
原則:不能修改被裝飾函數的原始碼;不能修改被裝飾函數的調用方式
#實現裝飾器的知識儲備:
1.函數即變數
2.高階函數,有兩種方式:
(1)把一個函數名當做實參傳遞給另一個函數(在不修改被裝飾函數原始碼的情況下為其添加功能)
(2)傳回值中包含函數名(不修改函數調用的方式)
3.嵌套函數
高階函數+嵌套函數==》裝飾器
import time#計算一個函數的已耗用時間的裝飾器def timer(func): def wrapper(*kargs,**kwargs): start_time=time.time() func() end_time=time.time() print("the func runtime is %s"%(end_time-start_time)) return wrapper@timerdef test1(): time.sleep(3) print("in the test1....")test1()#高階函數def bar(): print("in the bar...")def test(func): print(func)#列印出該函數變數的地址 func()test(bar)
# 把一個函數名當做實參傳遞給另一個函數import timedef bar(): time.sleep(2) print("in the bar...")def test(func): start_time=time.time() func() #run bar() end_time=time.time() print(" the func runtime is %s"%(end_time-start_time))test(bar)# 傳回值中包含函數名import timedef bar(): time.sleep(2) print("in the bar...")def test2(func): print(func) return funcbar2=test2(bar)bar2()#嵌套函數#局部範圍和全域範圍的訪問順序x=0def grandpa(): x=1 print("grandpa:",x) def dad(): x=2 print("dad:",x) def son(): x=3 print("son:",x) son() dad()grandpa()
#匿名函數:沒有定義函數名字的函數calc=lambda x,y:x+yprint(calc(13,15))
import timedef timer(func): def deco(*kargs,**kwargs): start_time=time.time() func(*kargs,**kwargs) end_time=time.time() print(" the func runtime is %s"%(end_time-start_time)) return deco@timer # test1=timer(test1)def test1(): time.sleep(2) print("in the test1.....")# test1=timer(test1)test1()@timerdef test2(name,age): time.sleep(3) print("in the test2:",name,age)test2("Jean_V",20)
#裝飾器進階版#對各個頁面添加使用者認證username,password="admin","123"def auth(auth_type): print("auth_type:",auth_type) def out_wrapper(func): def wrapper(*args,**kwargs): if auth_type=="local": uname=input("請輸入使用者名稱:").strip() passwd=input("請輸入密碼:").strip() if uname==username and passwd==password: print("\033[32;1m User has passed authentication\033[0m") res=func(*args,**kwargs) print("************after authentication") print(res) else: exit("\033[31;1m Invalid username or password\033[0m") elif auth_type=="LADP": print("我不會LADP認證,咋辦?。。。") return wrapper return out_wrapperdef index(): print("index page......")@auth(auth_type="local")#home 頁採用本地驗證def home(): print("home page.....") return "from home page....."@auth(auth_type="LADP")#bbs 頁採用LADP驗證def bbs(): print("bbs page....")index()print(home())home()bbs()
2.列表產生式
#列表產生式:一句代碼更加簡潔a=[i*2 for i in range(10)]print(a)b=[i*i for i in range(20)]print(b)#上面的列表產生式等同於res=[]for i in range(10): res.append(i*2)print(res)#列表產生式的更加進階的用法:#[funv(i) for i in range(10)]def fun(n):#定義一個求階乘的函數 if n>1: return n*fun(n-1) else: return 1#利用函數產生10以內的階乘c=[fun(i) for i in range(10)]print(c)
3.迭代器與產生器
參考資訊:http://www.cnblogs.com/alex3714/articles/5765046.html
產生器:按照某種演算法可以進行推算,不必建立完整的List,節省大量空間,在迭代過程中一邊迴圈一邊計算的機制,稱之為產生器generator
建立L和g的區別僅在於最外層的[]和(),L是一個list,而g是一個generator。
針對generator可以使用for迴圈輸出;因為generator也是可迭代對象
for j in g:
print(j)
如果要一個一個列印出來,可以通過__next__()函數獲得generator的下一個傳回值:
著名的斐波拉契數列(Fibonacci),除第一個和第二個數外,任意一個數都可由前兩個數相加得到:
1, 1, 2, 3, 5, 8, 13, 21, 34, ...
斐波拉契數列用列表產生式寫不出來,但是,用函數把它列印出來卻很容易:
#斐波那契數列#輸出斐波那契數列的前m個數def fibnacci(m): n,a,b=0,0,1 while n<m: print(b) a,b=b,a+b #這一句相當於:t=(b,a+b) a=t[0] b=t[1] n+=1 return 'done'fibnacci(10)
上面的函數和generator僅一步之遙。要把fib函數變成generator,只需要把print(b)改為yield b就可以了:
#斐波那契數列#輸出斐波那契數列的前m個數def fibnacci(m): n,a,b=0,0,1 while n<m: yield b #這一句很關鍵 a,b=b,a+b #這一句相當於:t=(b,a+b) a=t[0] b=t[1] n+=1 return ' well done'#異常的時候顯示的資訊f=fibnacci(10)for i in f: print(i)g=fibnacci(6)#捕獲異常並處理while True: try: x=next(g) print("g:",x) except StopIteration as e: print("Generator return value :",e.value) break
在上面fib的例子,我們在迴圈過程中不斷調用yield,就會不斷中斷。當然要給迴圈設定一個條件來退出迴圈,不然就會產生一個無限數列出來。
同樣的,把函數改成generator後,我們基本上從來不會用next()來擷取下一個傳回值,而是直接使用for迴圈來迭代:
但是用for迴圈調用generator時,發現拿不到generator的return語句的傳回值。如果想要拿到傳回值,必須捕獲StopIteration錯誤,傳回值包含在StopIteration的value中:
還可通過yield實現在單線程的情況下實現並發運算的效果
生產者消費者問題 (通過產生器實現協程並行運算)
#生產者消費者問題import timedef consumer(name): print("%s,準備好吃漢堡包了"%name) while True: hamburg=yield print("第[%s]個漢堡包來了,被[%s]吃了"%(hamburg+1,name))def producer(name): c1=consumer('Alice') c2=consumer('Bob') #生產者生產的時候需要通知消費者前來吃 c1.__next__() c2.__next__() print("%s,開始做漢堡包了"%name) for i in range(10): time.sleep(1.5) print("做好了1個漢堡包,可以吃了") c1.send(i) c2.send(i)producer("Jean")
迭代器
可以直接作用於for迴圈的資料類型有以下幾種:
一類是集合資料類型,如list、tuple、dict、set、str、bytes等;
一類是generator,包括產生器和帶yield的generator function。
這些可以直接作用於for迴圈的對象統稱為可迭代對象:Iterable。
可以使用isinstance()判斷一個對象是否是Iterable對象:
而產生器不但可以作用於for迴圈,還可以被__next__()函數不斷調用並返回下一個值,直到最後拋出StopIteration錯誤表示無法繼續返回下一個值了。
*可以被next()函數調用並不斷返回下一個值的對象稱為迭代器:Iterator。
可以使用isinstance()判斷一個對象是否是Iterator對象:
產生器都是Iterator對象,但list、dict、str雖然是Iterable,卻不是Iterator。
把list、dict、str等Iterable變成Iterator可以使用iter()函數:
Iterator對象表示的是一個資料流,Iterator對象可以被next()函數調用並不斷返回下一個資料,直到沒有資料時拋出StopIteration錯誤。可以把這個資料流看做是一個有序序列,但我們卻不能提前知道序列的長度,只能不斷通過next()函數實現按需計算下一個資料,所以Iterator的計算是惰性的,只有在需要返回下一個資料時它才會計算。
Iterator甚至可以表示一個無限大的資料流,例如全體自然數。而使用list是永遠不可能儲存全體自然數的。
小結:
凡是可作用於for迴圈的對象都是Iterable類型;
凡是可作用於next()函數的對象都是Iterator類型,它們表示一個惰性計算的序列;
集合資料類型如list、dict、str等是Iterable但不是Iterator,不過可以通過iter()函數獲得一個Iterator對象。
Python的for迴圈本質上就是通過不斷調用next()函數實現的,例如: