zoukankan      html  css  js  c++  java
  • 生成器与yield关键字

    1.可迭代对象

    为了说明可迭代对象,首先我们要知道,迭代的概念。我们先来看一个实例:

    ls = [1,2,3,4,5]
    for i in ls:
        print(i)

    上面的实例非常简单,我们创建了一个列表ls,并且用for语句遍历这个列表的每一个元素。这里列表ls被遍历的这个行为,就称之为迭代。

     

    明白了迭代的概念之后,你就会发现,在Python中,能够被迭代的不只有列表,例如你还可以迭代字符串,元组,文件,字典等等,这些能够被迭代的对象,就称作可迭代对象。

    2.生成器

    迭代固然是处理大量数据的好方法。但是以列表为例,迭代存在两个问题,第一,如果列表中的元素太多了,将大量占用内存。第二,我们有时候只需要使用一次数据,如果用列表把数据全部保存起来,岂不是有些浪费?Python中的生成器就能很好的解决这两个问题。

     

    2.1.生成器函数

    生成器是一种可以简单有效的创建迭代器的工具。它们像常规函数一样撰写,但是在需要返回数据时使用yield语句。每当对它调用next()函数(有关next函数下面会提及),生成器从它上次停止的地方重新开始(它会记住所有的数据值和上次执行的语句)。

    上面是我在Python官方文档中找到的定义,下面以实例作为解释:

    def print_letter(data):
        for index in range(len(data)):
            yield data[index]
    g = print_letter("hello")
    for i in g:
        print(i)
    
    等价于
    
    def print_letter(data):
        for index in range(len(data)):
            yield data[index]
    for i in print_letter("hello"):
        print(i)
    
    打印结果:
    h
    e
    l
    l
    o

    上述代码中,def部分定义了一个生成器函数,print_letter("hello")这行代码将返回一个生成器。为了理解生成器(即def定义的部分),我们可以从函数的角度理解,当调用函数时,代码是按顺序结构执行的,生成器与函数的区别在于,函数遇到return返回,而生成器执行到yield时返回yield之后的语句;另外,函数返回时会释放内部定义的变量,而生成器则会保持退出时的状态。以上面代码为例,如果print_letter是函数,那么只返回一次数据(将yield改成return,也就是返回data[0]);但对于生成器,它将在一次调用后接着进入之前的状态,执行代码,每次都返回yield后的语句,直到不再满足条件时停止(对于这里的例子,yield第一次返回data[0],第二次返回data[1],以此类推,当不再满足for遍历条件时就不再返回yield后的语句了)。除了使用for遍历以外,生成器可以不断调用next()函数来实现“遍历”,另外需要指出的是,如果你用next函数遍历完生成器后,程序将会抛出一个StopIteration的异常。

    如果使用type函数查看print_letter的类型,可以验证它是生成器(generator)

    ...type(print_letter)
    >>>function
    ...type(print_letter('hello'))
    >>>generator

    2.2.生成器表达式

    除了像函数那样定义一个生成器之外,还有一种定义生成器的简单方法。即生成器表达式。

     

    介绍生成器表达式之前我们先介绍一下和它长的很像的列表生成式(List Comprehension),看下面实例:

    # 两种方式产生列表[1,4,9,16]
    ls = []
    for i in range(1,5):
        ls.append(i*i)
    
    等价于
    
    ls = [i**i for i in range(1,5)]   # 列表生成式

    通过实例可以看出,列表生成式是一种定义列表的简洁方法,实际中也推荐大家使用,这种写法更加pythonic。知道了列表生成式,就很容易得到生成器表达式了。

    以上面的代码为例,要把列表生成式修改成生成器表达式,只需要把[]改为(),即

    >>>g = (i**i for i in range(1,5))  # 生成器表达式
    >>>print(type(g))
    
    <class 'generator'>

    可见,构建一个生成器有两种方式,1.生成器函数 2.生成器表达式 。按照实际情况选择。

    3.yield关键字

    如果你理解了我前面所说的生成器函数运作机制,那么yield关键词你应该也懂了。

    再次概括的话就是:生成器内部的代码执行到yield会返回,返回的内容为yield后的表达式。下次再执行生成器的内部代码时将从上次的状态继续开始。通过yield关键字,我们可以很方便的将一个函数修改为生成器。

    4.生成器的实际运用

    你可能会问,生成器到底能干啥?下面我贴上部分爬取猫眼电影top100的实战代码,希望对你有所启发。

    # 提取目标内容并且格式化
    def parse_one_page(html):   
        pattern = re.compile('<dd>.*?index.*?>(.*?)</i>.*?<img data-src="(.*?)".*?</a>.*?name"><a.*?>(.*?)</a>.*?star">(.*?)</p>.*?releasetime">(.*?)</p>.*?integer">(.*?)</i>.*?fraction">(.*?)</i>',re.S)
        items = re.findall(pattern, html)
        for item in items:
            yield {     # 每次被调用返回yield后面的参数 这里是一个字典(代表一条电影信息)
                    'index' : item[0],
                    'image' : item[1],
                    'title' : item[2].strip(),
                    'actor' : item[3].strip()[3:] if len(item[3]) > 3 else '',
                    'time'  : item[4].strip()[5:] if len(item[4]) > 5 else '',
                    'score' : item[5].strip() + item[6].strip()
            }
    # 爬取一个网页
    def main(offset):
        url = 'http://maoyan.com/board/4?offset='+str(offset)
        html = get_one_page(url)
        for item in parse_one_page(html):   # Parse_one_page(html)是一个个可迭代对象 实质上是个生成器 
            print(item)
            write_to_file(item)

    5.总结

    一开始我们介绍了迭代的概念,从迭代的问题引出了生成器的概念,阐述了生成器的运行机制,介绍了两种构建生成器的方式(生成器函数以及生成器表达式),解释了yield关键字的作用,最后我们通过实战代码见识了生成器在实际项目中作用。

  • 相关阅读:
    止步于磁盘空间不足
    添加随机扰动 爬山算法
    递归 启发式
    删除文件
    linux 下载 图片
    CURD特性
    php 写session
    php 写session
    14.5.4 InnoDB File-Per-Table Tablespaces 每个表一个文件
    14.5.4 InnoDB File-Per-Table Tablespaces 每个表一个文件
  • 原文地址:https://www.cnblogs.com/flightless/p/12003041.html
Copyright © 2011-2022 走看看