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")

  • 相关阅读:
    16. 3Sum Closest
    17. Letter Combinations of a Phone Number
    20. Valid Parentheses
    77. Combinations
    80. Remove Duplicates from Sorted Array II
    82. Remove Duplicates from Sorted List II
    88. Merge Sorted Array
    257. Binary Tree Paths
    225. Implement Stack using Queues
    113. Path Sum II
  • 原文地址:https://www.cnblogs.com/chenqizhou/p/7049284.html
Copyright © 2011-2022 走看看