前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python3--迭代器,生成器

python3--迭代器,生成器

作者头像
py3study
发布2018-08-02 16:16:28
4180
发布2018-08-02 16:16:28
举报
文章被收录于专栏:python3python3

一 迭代器

1 什么是可迭代对象?

字符串,列表,元组,字典,集合都可以被for循环,说明他们都是可迭代的

怎么来证明这一点呢?

注释:

type只能判断是什么类型

isintance判断方面更广,不仅能判断类型,还能判断是否可迭代

Iterable是否为可迭代

代码语言:javascript
复制
from collections import Iterable
s = [1, 2, 3, 4]
t = [5, 6, 7, 8]
print(isinstance(s, Iterable)) 
print(isinstance(s, Iterable))

执行结果

True

True

可迭代协议

现在是从结果分析原因,能被for循环的就是"可迭代的",但是如果按常规想,for怎么知道谁是可迭代的呢?

假设自己写了一个数据类型,希望这个数据类型里的东西也可以使用for被一个个的取出来,那就必须满足for的要求,这个要求就叫做"协议"

可以被迭代要满足的要求就叫做可迭代协议,可迭代协议的定义非常简单,就是内部实现了__iter__方法

下面来验证一下

代码语言:javascript
复制
print(dir([1, 2]))
print(dir((2, 3)))
print(dir({1: 2}))
print(dir({1, 2}))

执行结果

注释:

dir打印该对象的所有操作方法

blob.png
blob.png

可以被for循环的都是可迭代的,想要可迭代,内部必须有一个__iter__方法

接着分析,__iter__方法做了什么事情呢?

可迭代的:内部必须含有一个__iter__方法

python迭代器

什么叫做迭代器?迭代器英文意思是iterator

可迭代对象转化成迭代器:可迭代对象.__iter__()--->迭代器

迭代器不仅含有__iter__,还含有__next__ 遵循迭代器协议

代码语言:javascript
复制
l1 = [1, 2, 3, 4]
print(l1.__iter__())

执行结果

例1

代码语言:javascript
复制
l1 = [1, 2, 3, 4]
ss = l1.__iter__()
print(ss.__next__())
print(ss.__next__())
print(ss.__next__())
print(ss.__next__())

执行结果

1

2

3

4

注意:使用__next__方法取值,到底后,在执行会报错。

blob.png
blob.png

for循环,能遍历一个可迭代对象,它的内部到底进行了什么?将可迭代对象转化成迭代器(可迭代对象.__iter__()),内部使用__next__方法,一个一个取值,加了异常处理功能,取值到底后自动停止

仅含有__iter__方法的,就是可迭代对象

包含__iter__和__next__方法的,就是迭代器

迭代器的好处

1 节省内存空间(一个好的程序员,考虑的是性能,迭代器)

2 满足惰性机制

3 不能反复取值,不可逆

使用while循环模拟for循环

1 转换成迭代器

l1 = [1, 2, 3, 4, 5, 6, 7]

l2 = l1.__iter__()

2 使用__next__

while True:

    i = l2.__next__()

    print(i)

3 异常处理

while True:

    try:

        i = l2.__next__()

        print(i)

    except Exception:

        break

整个代码为

代码语言:javascript
复制
l1 = [1, 2, 3, 4, 5, 6, 7]
l2 = l1.__iter__()

while True:
    try:
        i = l2.__next__()
        print(i)
    except Exception:
        break

执行结果

1

2

3

4

5

6

7

try里面的代码,出现报错不会出红

excetp表示报错的时候,该怎么处理

生成器

初始生成器

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

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

Python中提供的生成器:

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

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

生成器Generator:

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

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

生成器函数

一个包含yield关键字的函数就是一个生成器函数,yield可以为我们从函数中返回值,但是yield又不同于return,return的执行意味着程序的结束,调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象,每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值,直到函数执行结束。

生成器需要自己构建

1 生成器函数构造

2 生成器推导式构造

3 数据类型的转化

例1

代码语言:javascript
复制
def func1():
    print(111)
    print(222)
    print(333)
    yield 666

g = func1()
print(g)

执行结果,没有打印出值,why?

<generator object func1 at 0x000002D2A8D033B8>

上例中g为生成器对象

第一:函数中只要有yield那他就不是一个函数,而是一个生成器

第二:g称作生成器对象

而生成器本质上也是迭代器,也可以使用__next__取值,而一个__next__对应一个yield

代码语言:javascript
复制
def func1():
    print(111)
    print(222)
    print(333)
    yield 666

g = func1()
print(g)
print(g.__next__())

执行结果

<generator object func1 at 0x000001DAB83133B8>

111 #print打印

222 #print打印

333 #print打印

666 yield __next__实际上只是打印这个666

例2

代码语言:javascript
复制
import time
def genrator_fun():
    a = 1
    print('现在定义了a变量')
    yield a
    b = 2
    print('现在又定义了b变量')
    yield b

gl = genrator_fun()
print('g1:', gl)   # 打印gl可以发现gl就是一个生成器
print('-'*10)      # 分割线
print(next(gl))
time.sleep(1)      # 延迟1秒
print(next(gl))

执行结果

blob.png
blob.png

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

例3

代码语言:javascript
复制
def produce():
    '''生产衣服'''
    for i in range(1000):
        yield '生产了第{}件衣服'.format(i)

tmp = produce()
print(tmp.__next__())
print(tmp.__next__())
print(tmp.__next__())
num = 0
for i in tmp:
    print(i)
    num += 1
    if num == 5:
        break

执行结果

blob.png
blob.png

上面的例子,也可以使用

for i in range(1,5):

    print(tmp.__next__())

__next__一次,yield就执行一次

对于列表而言,for循环是从开始

对于生成器而言,它是由指针的,__next__一次,指针向前一次,它不能从头开始,不可逆

生成器和迭代器的区别

迭代器:有内置方法

生成器:开发者自定义

send使用方法

代码语言:javascript
复制
def generator():
    print(123)
    yield 1
    print(456)
    yield 2
g = generator()
g.__next__()
g.send('hello')

执行结果

123

456

可以看到send定义的变量没有被执行输出

next和send功能一样,都是执行一次

第一个取值,只能用next

send是给上一个yield发送值

最后一个yeild是取不到值的

代码语言:javascript
复制
def generator():
    print(123)
    content = yield 1
    print(content)
    print(456)
    yield 2
g = generator()
g.__next__()
g.send('hello')

执行结果

123

hello

456

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-04-03 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档