關於裝飾器的更多信息可以參考http://egon09.blog.51cto.com/9161406/1836763 1.裝飾器Decorator裝飾器:本質上是函數,(裝飾其他函數),就是為其他函數添加附加功能 原則:不能修改被裝飾函數的源代碼;不能修改被裝飾函數的調用方式#實現裝飾器的知識儲備:... ...
關於裝飾器的更多信息可以參考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 @timer def test1(): time.sleep(3) print("in the test1....") test1()#高階函數 def bar(): print("in the bar...") def test(func): print(func)#列印出該函數變數的地址 func() test(bar)# 把一個函數名當做實參傳遞給另一個函數 import time def 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 time def bar(): time.sleep(2) print("in the bar...") def test2(func): print(func) return func bar2=test2(bar) bar2()#嵌套函數 #局部作用域和全局作用域的訪問順序 x=0 def 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+y print(calc(13,15))import time def 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() @timer def 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_wrapper def 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 time def 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()
函數實現的,例如: