发布时间:2018-04-03 18:46:13编辑:Run阅读(3894)
一 迭代器
1 什么是可迭代对象?
字符串,列表,元组,字典,集合都可以被for循环,说明他们都是可迭代的
怎么来证明这一点呢?
注释:
type只能判断是什么类型
isintance判断方面更广,不仅能判断类型,还能判断是否可迭代
Iterable是否为可迭代
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__方法
下面来验证一下
print(dir([1, 2])) print(dir((2, 3))) print(dir({1: 2})) print(dir({1, 2}))
执行结果
注释:
dir打印该对象的所有操作方法
可以被for循环的都是可迭代的,想要可迭代,内部必须有一个__iter__方法
接着分析,__iter__方法做了什么事情呢?
可迭代的:内部必须含有一个__iter__方法
python迭代器
什么叫做迭代器?迭代器英文意思是iterator
可迭代对象转化成迭代器:可迭代对象.__iter__()--->迭代器
迭代器不仅含有__iter__,还含有__next__ 遵循迭代器协议
l1 = [1, 2, 3, 4] print(l1.__iter__())
执行结果
例1
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__方法取值,到底后,在执行会报错。
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
整个代码为
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
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
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
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))
执行结果
生成器有什么好处呢?就是不会一下子在内存中生产太多数据
例3
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
执行结果
上面的例子,也可以使用
for i in range(1,5):
print(tmp.__next__())
__next__一次,yield就执行一次
对于列表而言,for循环是从开始
对于生成器而言,它是由指针的,__next__一次,指针向前一次,它不能从头开始,不可逆
生成器和迭代器的区别
迭代器:有内置方法
生成器:开发者自定义
send使用方法
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是取不到值的
def generator(): print(123) content = yield 1 print(content) print(456) yield 2 g = generator() g.__next__() g.send('hello')
执行结果
123
hello
456
47975
46560
37488
34865
29438
26092
25051
20072
19698
18171
5895°
6537°
6046°
6052°
7166°
6003°
6071°
6555°
6516°
7898°