迭代器and生成器

主要内容:

  1,迭代器

  2,生成器

 

一,迭代器

  之前一直在用可迭代对象进行迭代操作,那么到底什么是可迭代对象

  str,list,tuple,dict,set.那么为什么我们可以称他们为可迭代对象呢?因为他们都遵循了可迭代协议,什么事可迭代协议.

s = 'abc'
for c in s:
    print(c)
    
for i in 123:
    print(i)  # 'int' object is not iterable

 注意:报错信息,整数类型对象不是可迭代的,iterable表示可迭代的,表示可迭代协议,那么如何进行验证数据类型是否符合可迭代协议.我们可以通过dir()函数进行产看类中定义好的所有方法

s = '我的哈哈哈哈'
# print(dir(s))
print(dir(str))
'''
['__add__', '__class__', '__contains__', '__delattr__', '__dir__',
 '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', 
 '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__',
  '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__',
   '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
    '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 
    'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 
    'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier',
     'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 
     'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit',
      'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']'''
通过dir()查看所有方法

list,tuple,dict,set同样,自行查看

  综上,我们可以确定,如果对象中有__iter__函数,那么我们认为这个对象遵守了可迭代协议,就可以进行迭代,这里的__iter__shi帮助我们获取到对象的迭代器,我们使用__next__()来获取到一个迭代器中的元素,那么我们之前讲的for的工作原理是什么呢?

s = '我爱我的祖国'
c = s.__iter__()
print(c.__next__())
print(c.__next__())
print(c.__next__())
print(c.__next__())
print(c.__next__())
print(c.__next__())  # 这个是最后一个
# print(c.__next__()) # StopIteration

   我们可以把迭代的内容当成子弹,然后呢,获取到迭代器__iter__(),就把子弹都装在弹夹中,然后发射就是__next__()把每一个子弹(元素)打出来,也就是说,for循环的时候,一还是的时候是__iter__()来获取迭代器,后面每次获取元素都是通过__next__()来完成的.当程序遇到StopIterable将结束循环

 

二,生成器

  什么事生成器,生成器实质就是迭代器

  在python中有三种方式来获取生成器

 

  1,通过生成器函数

  2,通过各种推导式来实现生成器

  3,通过数据的转换也可以获取生成器

def func():
    print("111")
    return 222

ret = func()
print(ret)
# 结果
# 111
# 222

将函数中的return替换成yield就是生成器

def func():
    print("111")
    yield 222
ret = func()
print(ret)

# 结果:<generator object func at 0x000001F05A470E08>

 运行的结果和上面的不一样,为什么呢?由于函数中存在了yield.那么这个函数就是一个生成器函数.这个时候,我们在执行这个函数的时候.就不再是函数的执行了.而是获取这个生成器.如何使用?想想迭代器.生成的本质就是迭代器,所以,我们可以直接运行__next__()来执行以下生成期:

def func():
    print("111")
    yield 222

gener = func()  # 这个时候函数不会执行,而是获取到生成器
ret = gener.__next__()  # 这个时候函数才会执行,yield的作用和return一样,就是返回数据
print(ret)
# 结果:
# 111
# 222

 那么我们可以看到.yield和return的效果是一样的,有什么去别呢?yield是分段来执行一个函数.return呢?直接停止执行函数

def func():
    print("111")
    yield 222
    print("333")
    yield 444

gener = func()
ret = gener.__next__()
print(ret)
ret2 = gener.__next__()
print(ret2)
ret3 = gener.__next__()  # 这个会报错
print(ret3)

 当程序运行到最后一个yield.那么后面继续进行__next__()程序会报错.

好了生成器说晚了.生成器有什么作用呢?我们来看这样一个需求.学校想耐克订单100000件校服,要是等衣服制造好了,全部一次性交付,学生都毕业了.

def cloth():
    for i in range(0, 1000):
        yield '衣服' + str(i)

c1 = cloth()
print(c1.__next__())
print(c1.__next__())
print(c1.__next__())
print(c1.__next__())


# 一次性提取50件衣服
for i in range(0, 50):
    ret = c1.__next__()
    print(ret)

 接下来是send方法,send和__next__()方法都可以让生成器执行到下一个yield.

def eat():
    print('我吃什么啊')  # 运行第一个yield的时候,会执行print(),并将"馒头"返回给ret1,进而打印ret1
    a = yield '馒头'
    print(a)  # 当执行第二个yield的时候,yield会返回一个None赋值给a,打印a就是一个None
    b = yield '大饼'
    print(b)
    c = yield '韭菜盒子'
    print(c)
    print('程序结束')
    yield 'Game Over'

gen = eat()
ret1 = gen.__next__()
print(ret1)
ret2 = gen.__next__()
print(ret2)
ret3 = gen.__next__()
print(ret3)
ret4 = gen.__next__()
print(ret4)
# 结果
# 我吃什么啊
# 馒头
# None
# 大饼
# None
# 韭菜盒子
# None
# 程序结束
# Game Over

   使用send和__next__()区别:

  1,send和next()都是让让生成器向下走一次

  2,send可以给上一个yield的位置传递值,不能给最后一个yield发送值,在第一次执行生成器代码的时候不能使用send()

def func():
    print('我吃什么啊?')
    a = yield '馒头'
    print(a)
    b = yield '大饼'
    print(b)
    c = yield '韭菜盒子'
    print(c)
    print('程序结束')
    yield 'Game Over'

gen = func()
ret1 = gen.__next__()
print(ret1)
ret2 = gen.send('胡辣汤')  # send()会将"胡辣汤"发送给上一个yield的位置,赋值给a,进而打印a
print(ret2)
ret3 = gen.send('狗粮')
print(ret3)
ret4 = gen.send('猫粮')
print(ret4)

 

posted @ 2018-06-19 12:51  猴里吧唧  阅读(136)  评论(0)    收藏  举报