测开之数据类型· 第4篇《迭代器、生成器》

坚持原创输出,点击蓝字关注我吧

做者:清菡
博客:oschina、云+社区、知乎等各大平台都有。微信

因为微信公众号推送改成了信息流的形式,防止走丢,请给加个星标 ⭐,你就能够第一时间接收到本公众号的推送!

文章总览图

目录

  • 1、迭代器函数

    • 1.迭代协议
    • 2.什么是迭代器呢?
    • 3.可迭代对象
    • 4.这个是可迭代对象和迭代器的区别
  • 2、生成器性能

    • 1.什么是迭代操做?
    • 2.生成器和迭代器有什么不一样呢?
    • 3.生成器比迭代器多了 3 种方法
    • 4.为何生成器有的方法,迭代器没有?
    • 5.数据发送到生成器,在哪一个地方呢?
  • 3、系列推荐

1、迭代器

1.迭代协议

一种是包含iter方法的,另外一种是包含getitem方法的(好比str对象就没有iter方法,可是同样可以迭代),只要对象中包含了这两种方法的任意一种,那么这个对象就能够进行迭代操做,也就是实现了迭代协议。测试

2.什么是迭代器呢?

生成器是迭代器的一种。迭代器的范围比生成器更广。只要能够经过next(),从里面一个一个往外面取值,都被称为迭代器。编码

关于要建立一个迭代器对象,那么内部要实现一个迭代器的协议。spa

2.1 迭代器协议

  • 实现了迭代器协议的对象(实现方式:对象内部定义了一个iter()方法)。
  • 对象实现了__next__方法。
  • __next__方法返回了某个数值(固然通常状况下,咱们须要的是返回这个对象的特定的数字,而且按照必定的顺序进行依次返回)。
  • __next__方法须要在值取完的时候,抛出stopiteration的错误信息。

3.可迭代对象

有个东西须要区分,一个是迭代器,一个是可迭代对象。code

只要内部实现了迭代协议的就是一个可迭代对象(可迭代对象能够进行相关的迭代操做,好比for循环,map函数等)。对象

能够用 for 循环进行遍历的,那么都是可迭代对象。可迭代对象不必定是迭代器,迭代器是在可迭代基础上,它内部要首先定义一个__next_方法。blog

迭代器内部实现了一个__next_方法,实现了这个方法以后,经过__next_这个函数才能够对这个迭代器进行一个取值。rem

还有个iter()方法,这个方法可将可迭代对象转换成一个迭代器。

yieldreturn是 2 个东西。yield只是暂停那个生成器函数。yield能够从生成器里面生成一个内容。

列表能够进行 for 循环,能够进行 for 循环遍历,它就是个可迭代对象。 列表是能够经过 for 循环遍历的,可是它不是迭代器。

迭代器是能够经过next()进行取值的。

生成器也是迭代器,生成器是能够经过next()去取值。那么,生成器它是迭代器的一种,是属于迭代器的。

你看,报错了:

# 列表
# 可迭代对象:能够for循环遍历的都是可迭代对象
li = [1,2,3,4]
next(li)
print(next(li))

提示:列表它不是一个迭代器。

不是个迭代器,不能经过这个去取值。要把一个可迭代对象转换成一个迭代器的话,经过iter()这个函数把可迭代对象放进去,它可以返回一个迭代器。

你看,这样就能获取到了:

# 列表
# 可迭代对象:能够for循环遍历的都是可迭代对象
li = [1,2,3,4]
li1 = iter(li)
print(next(li1))
print(next(li1))

经过iter()这个函数,来处理某个对象,它实际上至关于触发这个对象内部的一个__iter__这个方法。

我们看看list()的源码:

经过iter()这个函数把对象li传进去的时候,它会触发li这个对象对应的__iter_这个方法。

若是经过next()去取值,把li1这个对象传进去的时候,其实是触发这个对象的__next__方法。

它的类里面只有这个__iter__方法。

迭代器能够经过__next__取值。迭代器内部实现了__next__方法。

迭代器内部实现了 __iter__方法以外,还实现了__next__ 方法。

4.这个是可迭代对象和迭代器的区别

2、生成器

生成器是迭代器的一种。

迭代器是在可迭代对象的基础上实现了__iter_方法。迭代器和生成器均可以支持迭代操做。

1.什么是迭代操做?

for 循环。

2.生成器和迭代器有什么不一样呢?

生成器是迭代器的一种。 刚才用起来的时候好像没有什么区别,打印下这个类型看看。

能够看到,它返回的是个列表迭代器对象:

这个是生成器对象:

li1 = iter(li)这个是可迭代对象。而后经过iter()转换成一个迭代器。

3.生成器比迭代器多了 3 种方法

send()方法 发送数据
close() 方法 关闭生成器
throw() 方法 使用的 throw 指令抛出错误

生成器是有send这个方法的,迭代器是没有的。

例如,前面有个生成器叫作tu

# () 生成器表达式
tu = (i for i in range(1000))#生成器对象
print(tu)

tu能够调用send()这个方法,能够与生成器进行交互,可将数据传输到生成器里面。

4.为何生成器有的方法,迭代器没有?

举个栗子:

生成器是迭代器的一种。

例如定义了一个父类,再有个子类,父类建立出一个对象,子类建立出一个对象。子类有本身的方法。父类建立的出来的对象里面,确定没有子类对象里面的方法。 子类里面有的方法,父类里面没有。

迭代器就是“父类”。生成器就是“子类”。

def gen():
    for i  in range(1,5):
        yield i

gen()

生成器运行的时候,调用函数gen(),调用这个函数的时候,这个函数里面的代码不会直接运行。

代码修改为这样:

def gen():
    for i  in range(1,5):
        yield i

g = gen()
print(g)

只有经过next()方法往生成器里面取值的时候,它才会从代码上面往下面运行。

这个send()方法可将数据传到生成器里面。使用next(),从生成器里面获取出一个值。若是使用send()方法,它也可以获取出来一条数据。

def gen():
    for i  in range(1,5):
        se = yield i
        print(se)

g = gen()
print(next(g))
print(g.send(100))

send()方法能够往生成器里面传入一个值。

经过send()方法生成数据的时候,它也能够往里面发送一个 100 的值。

5.数据发送到生成器,在哪一个地方呢?

若是经过next()去取值的话,这个yield完毕后是没有返回内容的。

代码详解:

第一轮: 循环进来,经过next()去取值生成了一个 1:

def gen():
    for i  in range(1,5):
        se = yield i
        print(se)

g = gen()
print(next(g))

第二轮: 经过print(g.send(100))去发送值,而后打印:

def gen():
    for i  in range(1,5):
        se = yield i
        print('se的值:',se)

g = gen()
print(next(g))
print(g.send(100))

在第一轮结束以后,在yield这里,yield完毕就中止了。在第一轮yield完以后,第二轮经过send()传值进去,传到se那里,打印出来 100。

而后再往上返回一个数据,又暂停,返回第二条数据就是个 2。

第三轮: 经过next()再去生成一条元素,又触发了yield i这个地方,这里释放了,日后面走,日后面走的话,可是没有放数据进来,这个时候se是空的,打印出来的se是空的。

而后再往上,生成一条元素到 3,而后又停在yield i这个地方了,生成完元素,把这个值返回出去。

def gen():
    for i  in range(1,5):
        se = yield i
        print('se的值:',se)

g = gen()
print(next(g))
print(g.send(100))
print(next(g))

再次next()或者send()来触发它的时候,它会这样走:

注意: yield接收不是存在i中,这个yield返回出来的i是遍历出来的内容。

send()发进去的,是yield i这里运行完毕以后,当下一个send()触发的时候,它把这个值发送到yield i这里运行完毕以后的一个结果。

yield i这里把这个i返回出去,就停在这里不动了。send()发送个数据进去,那么数据就发送到个yield i这地方。

至关于yield i这个地方返回的一个结果,也就是send()发进去的内容,若是send()不发进去内容,返回出来是个空的。

舒适提示:生成器<迭代器<可迭代对象

3、系列推荐


公众号 「清菡软件测试」 首发,更多原创文章:清菡软件测试 109+原创文章,欢迎关注、交流,禁止第三方擅自转载。

相关文章
相关标签/搜索