函數進階之結合tornado
一、本篇博文內容
1、協程函數 2、面向過程編程 3、遞歸和二分法View Code
二、協程函數
協程函數:就是使用了yield表達式形式的生成器
首先函數的傳參有幾種?
三種:
1、實參形參傳參
2、閉包的形式傳參
3、就是通過yield的方式傳參。好處:不用重復的神情局部內存空間
yield的表達式形式的應用
def eater(name): print("%s start to eat" %name) #pyrene while True: food=yield print("%s eat %s"%(name,food)) a_g=eater("pyrene") print(a_g) print(next(a_g)) #因為這裏執行yield的返回的結果,yield後面為空所以這裏為none print(next(a_g)) #這裏執行了兩步,所以結果為兩個
分析:
首先這是一個生成器。
執行print(a_g)就會的到一個生成器<generator object eater at 0x00000160E546DFC0>
然後執行第一個print(next(a_g)) 得到的結果為
pyrene start to eat
None
這是因為執行的時候next把name “pyrene”傳遞過來給了
print("%s start to eat" %name) =pyrene start to eat
之後繼續往下執行,執行到yied的時候停止,並且把yied的返回值拿到,為空
然後執行第二個print(next(a_g)) 得到的結果為
pyrene eat None
None
因為這裏執行food=yield,然後往下執行,由於這裏是個死循環,所以又重新回到了yield。
yield的傳參
先看下面代碼:
def eater(name): print("%s start to eat" %name) food_list=[] while True: food=yield food_list food_list.append(food) print("%s eat %s"%(name,food)) a_g=eater("pyrene") #拿到生成器 next(a_g) #等同於alex_g.send(None) print("=========") # a_g.send("noodles") print(a_g.send("noodles"))
執行結果:
pyrene start to eat
=========
pyrene eat noodles
[‘noodles‘]
如果滿足yield傳參需要有兩個階段:
第一階段:必須初始化,保證生成器能夠暫停初始化的位置
也就是
next(a_g)
第二階段:給yield傳值
print(a_g.send("noodles"))#send會把括號中的參數傳遞給yield,然後賦值給food
這裏解釋說明:
1、先給當前暫停位置的yield傳值
2、繼續往下執行直到遇到下一個yield,然後返回yiled的結果
例子:
1、 下面是tornado的協程原理例子
from tornado import gen @gen.coroutine def fetch_coroutine(url): http_client = AsyncHTTPClient() response = yield http_client.fetch(url) return response.body
具體細節我會在後續的源碼解析中一步一步的分析
2、實現一個單一請求的協程
def eater(name): print("%s start to eat" %name) food_list=[] while True: food=yield food_list food_list.append(food) print("%s eat %s"%(name,food)) def producer(): a_g=eater("pyrene") next(a_g) while True: food=input(">>").strip() if not food:continue print(a_g.send(food)) producer()
上面如何解決初始化的問題呢?
思路:
‘‘‘
寫一個裝飾器解決初始化的問題
1、首先寫出裝飾器的結構
def init(func):
def wrapper(*args,**kwargs):
g=func(*args,**kwargs)
return g
return wrapper
2、然後把功能加到裝飾器中
‘‘‘
如下:
def init(func): def wrapper(*args,**kwargs): g=func(*args,**kwargs) next(g) return g return wrapper @init def eater(name): print("%s start to eat" %name) food_list=[] while True: food=yield food_list food_list.append(food) print("%s eat %s"%(name,food)) a_g=eater("pyrene") print(a_g.send("noodles"))
3、多個請求的協程
import random import time from tornado import gen from tornado.ioloop import IOLoop @gen.coroutine def get_url(url): wait_time = random.randint(1, 4) yield gen.sleep(wait_time) print(‘URL {} took {}s to get!‘.format(url, wait_time)) raise gen.Return((url, wait_time)) @gen.coroutine def process_once_everything_ready(): before = time.time() coroutines = [get_url(url) for url in [‘URL1‘, ‘URL2‘, ‘URL3‘]] result = yield coroutines after = time.time() print(result) print(‘total time: {} seconds‘.format(after - before)) if __name__ == ‘__main__‘: print("First, process results as they come in:") IOLoop.current().run_sync(process_as_results_come_in)
面向過程編程
提到面向過程編程,可能都會想到函數,。如果是這樣的話,那麽就不怎麽全面了
面向過程:核心就是過程兩個字,過程即解決問題的步驟
看下面例子:
要求實現grep -rl "error" /dir 這樣的小程序
代碼實現如下:
目錄結構如下:
然後在
a1.txt a2.txt b1.txt文件中有包含若幹個error
#第一階段:找到所有文件的絕對路徑 拿到文件和文件的父 級目錄就是絕對路徑 import os def init(func): def wrapper(*args,**kwargs): g=func(*args,**kwargs) next(g) return g return wrapper @init def search(target): while True: filepath=yield g=os.walk(filepath) for pardir,_,files in g: for file in files: abspath=r"%s\%s"%(pardir,file) target.send(abspath) # g=search() # g.send(r"F:\18期代碼\day5\a") #第二階段:打開文件 @init def opener(target): while True: abspath=yield with open(abspath,"rb") as f: target.send((abspath,f)) # g = search(opener()) # g.send(r"F:\18期代碼\day5\a") #第三階段:循環讀出每一行的內容 @init def cat(target): while True: abspath,f=yield #這裏應該把文件和路徑都傳輸過來 (abspath,f) for line in f: res=target.send((abspath,line)) if res:break # g = search(opener(cat())) # g.send(r"F:\18期代碼\day5\a") #第四階段:過濾 @init def grep(pattern,target): tag=False #通過上面返回值去重操作 while True: abspath,line=yield tag tag=False if pattern in line: target.send(abspath) tag=True # g = search(opener(cat(grep("error")))) # g.send(r"F:\18期代碼\day5\a") #打印該行屬於的文件名 @init def printer(): while True: abspath=yield print(abspath) g = search(opener(cat(grep("error".encode("utf-8"),printer())))) g.send(r"C:\Users\Administrator\PycharmProjects\T4\app.py")
解析:
首先分析參數:
-r 遞歸的去找所有的目錄
-l 列出文件名,這裏是列出包含error的文件名
上面是查找目錄中所有的文件中包含error的文件
然後就是os模塊中的walk的使用:
g=os.walk(文件路徑) --》生成器
next(g)=([父目錄],[子目錄文件夾],[文件]) 返回一個元祖,裏面有三個元素
面向過程編程的優點:程序結構清晰,可以把復雜的問題簡單化,流程化
缺點:科擴展性差
應用場景:linux kernel git httpd shell腳本 還有一個很多人都不熟悉的編程序言haskell就是大名鼎鼎的面向對象編程思想
遞歸和二分法
遞歸調用:
在調用一個函數的過程中,直接或者間接的調用了函數本身。
註意,python中的遞歸性能低的原因是調用一次函數本身就會保存這次的狀態,一直等到結束才釋放
遞歸的兩個階段:遞推、回溯
簡單的例子:
def age(n): if n==1: return 18 return age(n-1)+2
g=age(3)
print(g)
例子2:如何把下面這個列表中的所有元素取出來?
l =[1, 2, [3, [4, 5, 6, [7, 8, [9, 10, [11, 12, 13, [14, 15,[16,[17,]],19]]]]]]]
def search(l): for item in l: if type(item) is list: search(item) else: print(item) search(l)
二分法
當數據量很大適宜采用該方法。采用二分法查找時,數據需是排好序的。主要思想是:(設查找的數組區間為array[low, high])
二分法:就可以用遞歸的思想來解決
如:
l=[1,2,3,5,7,12,44,77] def binary_search(l,num): print(l) if len(l)>1: mid_index=len(l)//2 if num>l[mid_index]: l=l[mid_index+1:] binary_search(l,num) elif num<l[mid_index]: l=l[:mid_index] binary_search(l, num) else: print("find it") else: if l[0]==num: print("find it") else: print("error") return binary_search(l,3)
函數進階之結合tornado