zoukankan      html  css  js  c++  java
  • 函数进阶之结合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: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: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: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:a")
    #打印该行属于的文件名
    @init
    def printer():
        while True:
            abspath=yield
            print(abspath)
    g = search(opener(cat(grep("error".encode("utf-8"),printer()))))
    g.send(r"C:UsersAdministratorPycharmProjectsT4app.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)
  • 相关阅读:
    ssh无密码登录设置方法以及出现问题 ECDSA host key 和IP地址对应的key不同的解决
    asp.net core 3 Swagger 添加 Authorization [Bearer token]
    asp.net core3 发布到IIS
    asp.net core 3.1 使用log4net
    asp.net core 3 跨域
    解决'vue' 不是内部或外部命令,也不是可运行的程序 或批处理文件的方法
    ReSharper 安装没有提示功能
    Python3 安装MySQL驱动
    win10 安装 Python 和 pip 详解
    Win10下 80端口被system(pid=4)占用的解决方法
  • 原文地址:https://www.cnblogs.com/pyrene/p/7265387.html
Copyright © 2011-2022 走看看