1. 程式人生 > >函數進階之結合tornado

函數進階之結合tornado

並且 arc -1 arm project item total dom margin

一、本篇博文內容

技術分享
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