Python迭代器、生成器

可迭代对象

在说迭代器之前先说一下什么是可迭代对象

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

from collections import Iterable                      

l = [1,2,3,4]                

t = (1,2,3,4)                

d = {1:2,3:4}                

s = {1,2,3,4}                

print(isinstance(l,Iterable))

print(isinstance(t,Iterable))

print(isinstance(d,Iterable))

print(isinstance(s,Iterable))

可迭代协议

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

该对象中,含有__iter__方法的就是可迭代对象,遵循可迭代协议。

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

判断对象是不是可迭代对象

第一种方法:
1,判断该对象是不是可迭代对象  '__iter__' in dir(对象)

print('__iter__' in dir(str))

print('__iter__' in dir(list))

2,判断该对象是不是可迭代对象  isinstance('abc',Iterable)

from collections import Iterable

print(isinstance('abc',Iterable))

print(isinstance('abc',str))

迭代器(Iterator)

什么是迭代器

迭代器 内部含有__iter__ 且含有__next__方法的对象就是迭代器,遵循迭代器协议。

l = [1, 2, 3, 4]

l_iter = l.__iter__()  # 将可迭代的转化成迭代器

item = l_iter.__next__()
print(item)

item = l_iter.__next__()
print(item)

item = l_iter.__next__()
print(item)

item = l_iter.__next__()
print(item)

如何判断该对象是不是迭代器

#方法一:
s1 = 'abc'

print('__iter__'in dir(s1))   
print('__next__'in dir(s1))

#方法二:
from collections import Iterator

l1 = [1, 2, 3]
print(isinstance(l1,Iterator))
l1_obj = l1.__iter__()
print(isinstance(l1_obj,Iterator))

为什么for 循环能遍历可迭代的对象

1、将可迭代对象转化成迭代器。(可迭代对象.__iter__())

2、内部使用__next__方法,一个一个取值。

3、加了异常处理功能,取值到底后自动停止。

用while循环模拟for循环

l = [1,2,3,4]

l_iter = l.__iter__()

while True:

    try:

        item = l_iter.__next__()

        print(item)

    except StopIteration:

        break

迭代器的好处

1,节省内存  2、惰性机制  3,单向执行,不可逆。

生成器

初始生成器

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

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

Python中提供的生成器:

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

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

生成器Generator:

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

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

生成器函数

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

def func():
    print(111)
    yield  666
    print(222)
    yield 333
    print(777)
    print(888)

g_object = func()
print(g_object.__next__())
print(g_object.__next__())
print(g_object.__next__())

你会发现在运行上面代码时会出现错误,那是应为yield必须和__next__一一对应上面我定义了两个yield打印时却出现了三个__next__

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

send方法

def func():
    print(111)
    yield  666
    print(222)
    yield 333
    print(777)
    print(888)
    yield 'abc'

g_object = func()
print(g_object.__next__())
print(g_object.send(None))
print(g_object.__next__())
  1. send 获取下一个值的效果和next基本一致    
  2. 只是在获取下一个值的时候,给上一yield的位置传递一个数据
def func():
    print(111)
    a = yield  666
    print(a)
    print(222)
    yield 333
    print(777)
    yield 'abc'

g_object = func()
print(g_object.__next__())
print(g_object.send('yang'))
print(g_object.__next__())

那么问题来了!我第一次取值能用send吗?

def func():
    print(111)
    a = yield  666
    print(a)
    print(222)
    yield 333
    print(777)
    yield 'abc'

g_object = func()
print(g_object.send('yang'))
print(g_object.send('yang'))
print(g_object.__next__())

#错误
Traceback (most recent call last):
  File "C:/Users/Administrator/Documents/python/迭代器.py", line 87, in <module>
    print(g_object.send('dsad'))
TypeError: can't send non-None value to a just-started generator

#使用send的注意事项

# send 不能用在第一次取值

# 第一次使用生成器的时候 是用next获取下一个值

# 最后一个yield不能接受外部的值

 

posted @ 2018-06-05 13:08  杨灏  阅读(163)  评论(0编辑  收藏  举报