Python:迭代器+生成器

迭代器和生成器

迭代器

今日内容:https://www.cnblogs.com/liwenzhou/p/9761027.html

器:器具/工具

迭代:其实属于一种重复的过程,迭代中每一次动作和下一次是有一定联系的

for循环就是典型迭代的过程,迭代取值的过程。

1. 可迭代对象(iterable):

  只要有__iter__方法,它就是一个可迭代对象。

2. 迭代器(iterator):

  调用可迭代对象的__iter__方法,得到的就是一个迭代器。

  直白一点说:迭代取值的工具

  迭代器对象:必须有一个__next__方法。

  调用__next__方法,就能够从中取出一个值

3. 迭代器协议:

  1. 在Python内部很多方法或者函数都支持迭代器协议。

    一个对象必须要有__next__方法,执行该方法要么返回一个值,要么报出一个StopIteration错误。

4. 迭代器的应用:

  range()

  1. 当数据非常多的时候, 迭代器就可以不一次性把值都放到内存,而是要一个才计算一个 

  面试题:python2中range和xrange有什么区别?

    range(10) --> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

    xrange(10) --> 可迭代对象

  Python3中,range(10)默认返回的就是一个可迭代对象。

  2. 迭代器的不足:

    1. 迭代器取值只能一个一个的取,不能跳着取, 也不能回头取

    2. 迭代器的值只能读取一次,读取完就没了。

5. 总结:

  1. 常见的可迭代对象:

    1. list

    2. string

    3. dict

    4. set

    5. tuple

    6. range(10)

  2. 迭代器对象

    打开的文件

  3. 为什么要讲可迭代对象和迭代器?

    1. 不依赖索引for循环遍历对象

    2. 通过迭代器存储大量的数据

  4. 得到迭代器的方式

    1. 基于一个已经存在的可迭代对象,调用她的__iter__方法

    2. 使用Python内置好的一些方法,比如range()

  5. 如何自己造一个迭代器?

    使用生成器!

 

生成器

 

初识生成器

 

我们知道的迭代器有两种:一种是调用方法直接返回的,一种是可迭代对象通过执行 iter方法得到的,迭代器有的好处是可以节省内存。

 

如果在某些情况下,我们也需要节省内存,就只能自己写。我们自己写的这个能实现迭代器功能的东西就叫生成器。

 

 

 

Python 中提供的 生成器:

 

1.生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行

 

2.生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表

 

3.数据转化:list() tuple() 将生成器对象 转化 .一般不这么用.

 

 

生成器Generator:

 

本质:迭代器 ( 所以自带了 __iter__方法和 __next__方法,不需要我们去实现)

 

特点:惰性运算,开发者自定义

 

生成器函数

 

一个包含yield关键字的函数就是一个生成器函数。

 

yield和return一样可以从函数中返回值,但是yield又不同于return,return的执行意味着程序的结束,只能返回一次,yield可以返回多次。

 

调用生成器函数不会得到返回的具体的值,而是得到一个生成器对象。

 

每一次从这个可迭代对象获取值,就能推动函数的执行,获取新的返回值。直到函数执行结束(yield像是拥有能够让函数暂停的魔力)。

 

 

 

def my_range():
    print('我是一个生成器函数')
    n = 0
    while 1:
        yield n
        n += 1

 

生成器有什么好处呢?就是不会一下子在内存中生成太多数据 

 

接下来,我们在这个函数的基础上来写一个我们自己的range函数,实现开始和结束

 

def my_range2(start, stop):
    n = start
    while n < stop:
        yield n
        n += 1

 

再进一步,实现步长:

 

def my_range3(start, stop, step):
    n = start
    while n < stop:
        yield n
        n += step

 

生成器本质上就是个迭代器,我们根据自己的想法创造的迭代器,它当然也支持for循环:

 

for i in my_range3(1, 10, 2):
    print(i)

 

 

 

更多应用

import time


def tail(filename):
    f = open(filename)
    f.seek(0, 2) #从文件末尾算起
    while True:
        line = f.readline()  # 读取文件中新的文本行
        if not line:
            time.sleep(0.1)
            continue
        yield line

tail_g = tail('tmp')
for line in tail_g:
    print(line)
生成器监听文件输入的例子

 

send

 

yield可以返回值,也可以接收值。

 

通过生成器的send方法可以给yield传值。

 

复制代码
def eat(name):
    print('%s要开始吃了!' % name)
    while 1:
        food = yield
        print('{}在吃{}'.format(name, food))


a = eat('alex')
a.__next__()  # 初始化,让函数暂停在yield处
a.send('包子')  # send两个作用:1.给yield传值 2.继续执行函数
a.send('饺子')
复制代码

 

yield可以同时返回值和接收值。

def averager():
    total = 0.0
    count = 0
    average = None
    while True:
        term = yield average
        total += term
        count += 1
        average = total/count


g_avg = averager()
next(g_avg)
print(g_avg.send(10))
print(g_avg.send(30))
print(g_avg.send(5))
计算移动平均值(1)

 

def init(func):  #在调用被装饰生成器函数的时候首先用next激活生成器
    def inner(*args,**kwargs):
        g = func(*args,**kwargs)
        next(g)
        return g
    return inner

@init
def averager():
    total = 0.0
    count = 0
    average = None
    while True:
        term = yield average
        total += term
        count += 1
        average = total/count


g_avg = averager()
# next(g_avg)   在装饰器中执行了next方法
print(g_avg.send(10))
print(g_avg.send(30))
print(g_avg.send(5)
计算移动平均值(2)_预激协程的装饰器

yield from

 

在一个生成器中引用另外一个生成器。

def gen1():
    for c in 'AB':
        yield c
    for i in range(3):
        yield i

print(list(gen1()))

def gen2():
    yield from 'AB'
    yield from range(3)

print(list(gen2()))
yield from

本章小结

可迭代对象:

  拥有__iter__方法

  特点:惰性运算

  例如:range(),str,list,tuple,dict,set

迭代器Iterator:

  拥有__iter__方法和__next__方法

  例如:iter(range()),iter(str),iter(list),iter(tuple),iter(dict),iter(set),reversed(list_o),map(func,list_o),filter(func,list_o),file_o

生成器Generator:

  本质:迭代器,所以拥有__iter__方法和__next__方法

  特点:惰性运算,开发者自定义

使用生成器的优点:

1.延迟计算,一次返回一个结果。也就是说,它不会一次生成所有的结果,这对于大数据量处理,将会非常有用

#列表解析
sum([i for i in range(100000000)])#内存占用大,机器容易卡死
 
#生成器表达式
sum(i for i in range(100000000))#几乎不占内存
列表解析式和生成器表达式

2.提高代码可读性

 

生成器相关的面试题

生成器在编程中发生了很多的作用,善用生成器可以帮助我们解决很多复杂的问题

除此之外,生成器也是面试题中的重点,在完成一些功能之外,人们也想出了很多魔性的面试题。
接下来我们就来看一看~

def demo():
    for i in range(4):
        yield i

g=demo()

g1=(i for i in g)
g2=(i for i in g1)

print(list(g1))
print(list(g2))
面试题1
def add(n,i):
    return n+i

def test():
    for i in range(4):
        yield i

g=test()
for n in [1,10]:
    g=(add(n,i) for i in g)

print(list(g))
面试题2
import os

def init(func):
    def wrapper(*args,**kwargs):
        g=func(*args,**kwargs)
        next(g)
        return g
    return wrapper

@init
def list_files(target):
    while 1:
        dir_to_search=yield
        for top_dir,dir,files in os.walk(dir_to_search):
            for file in files:
                target.send(os.path.join(top_dir,file))
@init
def opener(target):
    while 1:
        file=yield
        fn=open(file)
        target.send((file,fn))
@init
def cat(target):
    while 1:
        file,fn=yield
        for line in fn:
            target.send((file,line))

@init
def grep(pattern,target):
    while 1:
        file,line=yield
        if pattern in line:
            target.send(file)
@init
def printer():
    while 1:
        file=yield
        if file:
            print(file)

g=list_files(opener(cat(grep('python',printer()))))

g.send('/test1')

协程应用:grep -rl /dir
tail&grep

 

posted @ 2018-12-03 17:39  是我是我还是我  阅读(118)  评论(0编辑  收藏  举报