zoukankan      html  css  js  c++  java
  • python基础—迭代器、生成器

    python基础—迭代器、生成器

    1 迭代器定义

    迭代的意思是重复做一些事很多次,就像在循环中做的那样。

       只要该对象可以实现__iter__方法,就可以进行迭代。

       迭代对象调用__iter__方法会返回一个迭代器,所谓的迭代器就是具有next方法的对象。(在调用next方法时不需要任何参数)。在调用next方法时,迭代器会返回它的下一个值。如果next方法被调用,但迭代器没有值可以返回,就会引发一个StopITeration异常。

       一个实现了__iter__方法的对象是可迭代的,一个实现了next方法的对象是迭代器。

       迭代器也有__iter__方法。

    2 迭代器特性

    优点:

    1 迭代器提供了一种不依赖索引的取值方式,这样就可以遍历那些没有索引的可迭代对象了(字典,集合,文件)

    2 迭代器与列表比较,迭代器是惰性计算的,更节省内存

    缺点:

    1 无法获取迭代器的长度,使用不如列表索引取值灵活

    2 一次性的,只能往后取值,不能倒着取值

    迭代规则的关键是?为什么不是使用列表?

    1 如果有一个函数,可以一个接一个地计算值,那么在使用时可能是计算一个值时获取一个值,而不是像列表一样获取所有的值。如果有很多值,列表会占用太多的内存。

    2 使用迭代器更通用,更简单,更优雅

    3 迭代器代码

    (1)索引方式循环

    1
    2
    3
    4
    5
    6
    7
    l=['a','b','c','d']
    i=0
    while i<len(l):
        print(l[i])
        i+=1
    for in range(len(l)):
        print(l[i])

     

    (2)只要被内置了__iter__方法就是可迭代对象

    可迭代的:只要对象本身有__iter__方法,那他就是可迭代的

    1
    2
    3
    4
    l=['a','b','c','d']
    l.__iter__()  #相当于iter(l)
    iter(l)
    i=iter(l)

    i  就是迭代器,i 就是列表l的迭代器

     

    (3)迭代器的__next__方法

    迭代器使用__next__方法,可以不依赖下标取值,超出字典长度会有结束报错。

    调用__next__方法必须是迭代器,也只有迭代器才有__next__方法。

    1
    2
    3
    4
    5
    6
    7
    l=['a','b','c','d']
    i=iter(l)
    print(i.__next__())
    print(i.__next__())
    print(i.__next__())
    print(i.__next__())
    print(i.__next__())

    输出结果:

    1
    2
    3
    4
    5
    a
    b
    c
    d
    StopIteration  #报错

    (4)while循环与迭代器

    1
    2
    3
    4
    5
    6
    7
    d={'a':1,'b':2,'c':3,'d':4}
    i=iter(d)
    while True:
        try:
            print(next(i))
        except StopIteration:
            break

    输出结果:

    1
    2
    3
    4
    b
    c
    a
    d

    (5)for循环与迭代器

    在这里for就是使用可迭代对象的__iter__方法返回此可迭代对象的迭代器,并且执行此迭代器的next方法,并且出现StopITeration报错就结束

    For循环与列表

    1
    2
    3
    d={'a':1,'b':2,'c':3,'d':4}
    for in d:
        print(i)

    For循环与文件

    1
    2
    3
    with open('a.txt','r') as f:
        for line in f:
            print(line.strip())

      

    (6)迭代器也有__iter__方法

    f 的迭代器也有__iter__方法,并且指向f这个迭代器本身

    (7)迭代器是一次性的

    只能往后取值,不能倒着取值

     可迭代对象不是一次性的。

    1
    2
    3
    4
    5
    6
    7
    8
    d={'a':1,'b':2,'c':3,'d':4}
    i=iter(d)
    print(next(i))
    print(next(i))
    print(next(i))
    print(next(i))
    for in i:       #迭代器i已经将上面的值取完,for循环在这里没有值可取
    print(x)

    输出结果:

    1
    2
    3
    4
    d
    a
    b
    c

    (8)isinstance判断对象是否是可迭代对象和迭代器对象

    Isinstance使用例子:

    1
    2
    print(type('ssssss'is str)
    print(isinstance("ssssss",str))

     输出结果:

    1
    2
    True
    True

    用isinstance判断是不是可迭代对象与迭代器对象

    #查看可迭代对象和迭代器对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    from collections import Iterable,Iterator
    str1='hello'
    list1=[1,2,3]
    tuple1=(1,2,3)
    dict1={"a":1,"b":2,"c":3}
    set1={1,2,3,4}
    f1=open('a.txt')
    #都是可迭代的
    str1.__iter__()
    list1.__iter__()
    tuple1.__iter__()
    dict1.__iter__()
    set1.__iter__()
    f1.__iter__()
    print(isinstance(str1,Iterable))     #True
    print(isinstance(list1,Iterable))     #True
    print(isinstance(tuple1,Iterable))   #True
    print(isinstance(dict1,Iterable))    #True
    print(isinstance(set1,Iterable))     #True
    print(isinstance(f1,Iterable))       #True
    #查看是否是迭代器
    print(isinstance(str1,Iterator))     #False
    print(isinstance(list1,Iterator))     #Flase
    print(isinstance(tuple1,Iterator))   #Flase
    print(isinstance(dict1,Iterator))    #Flase
    print(isinstance(set1,Iterator))    #Flase
    print(isinstance(f1,Iterator))      #Flase

      

    4、生成器  

    4.1 生成器函数定义 

    生成器就是一个函数,这个函数内包含有yield这个关键字

    返回一个生成器对象。生成器本质就是迭代器

      生成器是一个包含yield关键字的函数。当它被调用时,在函数体中的代码不会被执行,而会返回一个迭代器。每次请求一个值,就会请求生成器中的代码,直到遇到一个yield或者return语句。Yield语句意味着生成一个值。Return语句意味着生成器要停止执行。

    生成器是由两部分组成的:生成器的函数和生成器的迭代器。生成器的函数是有def语句定义的,包含yield部分。生成器的迭代器是这个函数返回的部分。生成器函数返回的迭代器可以像其他迭代器那样使用。

      

    4.2  yield作用   

    生成器与return有何区别?

      Return只能返回一次函数就彻底结束了,而yield返回多次值

    yield到底干了什么事情?

      1 yield把函数变成生成器-,生成器实质就是迭代器。 相当于把__iter__和__next__方法封装到函数内部。   

      2 用return返回值能返回一次,而yield返回多次

      3 函数在暂停以及继续下一次运行时的状态是由yield保存

       任何包含yield语句的函数称为生成器。Yield不是像return那样返回值,而是每次产生多个值。生成器每次产生一个值(使用yield语句),函数就会被冻结:即函数停在那点等待被重新唤醒。函数被重新唤醒之后就从停止的那点开始。

    4.3 生成器运行详解

    Test函数中yield返回给g一个生成器,实质就是迭代器,通过next方法取值,并且在取值时会执行test函数和取yield的返回值。即next函数会触发生成器函数g的运行,遇到yield停止

    1
    2
    3
    4
    5
    6
    7
    8
    9
    from collections import Iterable,Iterator
    def test():
        print("first")
        yield 1
    g=test()
    print(g)
    print(next(g))
    print(isinstance(g,Iterable))
    print(isinstance(g,Iterator))

    输出结果:

    1
    2
    3
    4
    5
    <generator object test at 0x000000E8CBDE5360>
    first
    1
    True
    True

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    def test():
        print("one")
        yield 1
        print("two")
        yield 2
        print("three")
        yield 3
    g=test()
    res=next(g)
    print(res)
    res=next(g)
    print(res)
    res=next(g)
    print(res)
    res=next(g)
    print(res)

    输出结果:

    1
    2
    3
    4
    5
    6
    7
    one
    1
    two
    2
    three
    3
    StopIteration  #报错

      

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def test():
        print("one")
        yield 1
        print("two")
        yield 2
        print("three")
        yield 3
    g=test()
    for in g:
        print(i)

    输出结果:

    1
    2
    3
    4
    5
    6
    one
    1
    two
    2
    three
    3

     一个yield可以返回多个值,接收的类型是元组

    yield可以返会任何类型的值

    1
    2
    3
    4
    def test():
        yield 1,2,3,4
    g=test()
    print(next(g))

    输出结果:

    1
    (1,2,3,4)

    生成器与return有何区别?

      Return只能返回一次函数就彻底结束了,而yield返回多次值

    yield到底干了什么事情?

      1 yield把函数变成生成器--->迭代器

      2 用return返回值能返回一次,而yield返回多次

      3 函数在暂停以及继续下一次运行时的状态是由yield保存

    例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    def countdown(n):
        print("start countnum")
        while n>0:
            yield n
            n-=1
        print('done')
    g=countdown(5)
    print(next(g))
    print(next(g))
    print(next(g))
    print(next(g))
    print(next(g))
    try:
        print(next(g))      #这一次next函数应用没有yield返回,会报错StopIteration,需要try检测
    except StopIteration:
        pass
    g=countdown(5)
    print(next(g))
    for in g:    #for会将g变成迭代器:iter(g),执行next取值,并捕捉next出现的异常
        print(i)

    输出结果: 

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    start countnum
    5
    4
    3
    2
    1
    done
    start countnum
    5
    4
    3
    2
    1
    done

      

    4.4 用生成器模拟管道 

    tailf命令的实现

    1 普通函数实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #/usr/bin/env python
    import time
    def tail(file_path):
        with open(file_path,'r') as f:
            f.seek(0,2)
            while True:
                line=f.readline()
                if not line:
                    time.sleep(0.3)
                    continue
                else:
                    print(line,end="")
     
    tail("/tmp/a.txt")

    2 生成器函数实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #/usr/bin/env python
    import time
    def tail(file_path):
        with open(file_path,'r') as f:
            f.seek(0,2)
            while True:
                line=f.readline()
                if not line:
                    time.sleep(0.3)
                    continue
                else:
                    yield line
    g=tail("/tmp/a.txt")
    print(g)
    for line in g:
    print(line)

      

    tail -f |grep “error” 实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #/usr/bin/env python
    import time
    #定义阶段:定义两个生成器函数
    def tail(file_path):
        with open(file_path,'r') as f:
            f.seek(0,2)
            while True:
                line=f.readline()
                if not line:
                    time.sleep(0.3)
                    continue
                else:
                    yield line
    def grep(pattern,lines):
        for line in lines:
            if pattern in line:
                yield line
    #调用阶段:得到两生成器对象
    g=tail("/tmp/a.txt")
    gg=grep("error",g)
    #next触发执行gg生成器函数
    for line in gg:
        print(line)

      

    1 迭代器的应用

      文件名:a.txt  文件内容如下:

          apple 10 3

          tesla 100000 1

          mac 3000 2

          lenovo 30000 3

          chicken 10 3

       实现功能:cat a.txt|grep apple

       要求1:定义迭代器函数cat

       要求2:定义迭代器函数grep

       要求3:模拟管道的功能,将cat的处理结果作为grep的输入

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def cat(file_auth):
        with open(file_auth,"r") as f:
            f.seek(0)
            for line in f:
                yield line
    def grep(pattern,lines):
        for line in lines:
            if pattern in line:
                yield line
    cat_func=cat("/tmp/a.txt")
    grep_func=grep("apple",cat_func)
    for line in grep_func:
        print(line)

      

      

    5、协程函数  

    5.1 协程函数定义 

    如果在一个函数内部yield的使用方式是表达式形式的话,如x=yield,那么该函数成为协程函数。

    yield是表达式方式使用,则必须用send方法进行传值。

    协程函数=生成器+yield表达式+send方法

    5.2 e.send与next(e)的区别

    e.send与next(e)的区别

    1 如果函数内yield是表达式形式,那么必须先next(e)

      使用send方法只有在生成器挂起之后才有意义(也就是在yield函数第一次被执行之后),因此要用send方式时需要用next方法初始化生成器。

     用next(e)初始化  相当于 e.send(None) ,可以用装饰器解决初始化问题

    2 二者的共同之处是都可以让函数在上次暂停的位置继续运行,不一样的地方在于send在触发下一次代码的执行时,会顺便给yield传一个值。

     end可以传多个值,但是必须是元组类型

    5.3协程函数举例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def eater(name):
        print("%s start to eat food"%name)
        while True:
            food=yield
            print("%s get %s,to start eat"%(name,food))
        print("done")
    e=eater("people")
    print(e)
    next(e)
    e.send("apple")
    e.send("fruit")
    e.send("beef")
    e.send("meet")

    输出结果:

    1
    2
    3
    4
    5
    6
    <generator object eater at 0x000000F944583D00>
    people start to eat food
    people get apple,to start eat
    people get fruit,to start eat
    people get beef,to start eat
    people get meet,to start eat

      

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    def eater(name):
        print("%s start to eat food"%name)
        food_list=[]
        while True:
            food=yield food_list
            print("%s get %s,to start eat"%(name,food))
            food_list.append(food)
        print("done")
    e=eater("people")
    print(e)
    next(e)
     
    print(e.send("apple"))
    print(e.send("fruit"))
    print(e.send("beef"))
    print(e.send("meet"))

    输出结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <generator object eater at 0x0000006F92D83D00>
    people start to eat food
    people get apple,to start eat
    ['apple']
    people get fruit,to start eat
    ['apple''fruit']
    people get beef,to start eat
    ['apple''fruit''beef']
    people get meet,to start eat
    ['apple''fruit''beef''meet']

      

    2 生成器的应用

    把下述函数改成生成器的形式,执行生成器函数的到一个生成器g,

    然后g.send(url),打印页面的内容,利用g可以无限send

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    from  urllib.request import urlopen
    def get(url):
        while True:
            def index():
                print(url)
                print(urlopen(url).read())
            url=yield index
     
    g=get("http://www.baidu.com")
    next(g)
    baidu=g.send("http://www.baidu.com")
    baidu()

      

     

     5.4 协程函数用装饰器初始化

    协程函数使用装饰器完成初始化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    def auth(func):
        def wrapper(*args,**kwargs):
            res=func(*args,**kwargs)
            next(res)
            return res
        return wrapper
    @auth
    def eater(name):
        print("%s start to eat food"%name)
        food_list=[]
        while True:
            food=yield food_list
            print("%s get %s,to start eat"%(name,food))
            food_list.append(food)
        print("done")
     
    e=eater("people")
    e.send("123")

  • 相关阅读:
    Java中四个作用域的可见范围
    java构造方法前加void有什么作用
    css3渐变
    日历插件
    三级联动地点
    js返回上一级代码和刷新页面代码
    css3滚动条
    如何写评价“星星”有半个情况的,如3.5,这样写好调数据
    原生态js单个点击展开收缩和jQuery的写法
    推荐大家使用的CSS书写规范、顺序
  • 原文地址:https://www.cnblogs.com/chenqizhou/p/7049284.html
Copyright © 2011-2022 走看看