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__())
- send 获取下一个值的效果和next基本一致
- 只是在获取下一个值的时候,给上一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不能接受外部的值