|
1、引子
l=[1,2,3]
取值的方式有:索引取值、for循环取值,对于for 循环取值方式:可被循环的数据类型有:list、dict、str、set、tuple、f=open()句柄,range(),enumerate枚举。
2、概念
(1)查看某种数据类型中所有方法的方式
print(dir([])) #告诉我列表拥有的所有方法 print(dir({})) #告诉我集合拥有的所有方法 print(dir('')) #告诉我字符串拥有的所有方法 print(dir(())) #告诉我元组拥有的所有方法 print(dir(range(10))) #告诉我range拥有的所有方法
set = set(dir([]))&set(dir({}))&set(dir('')) print(set) #输出列表、字典、字符串的共有方法,其中均包含__iter__方法
(2)验证某种数据类型是否可循环的方式:
print('__iter__' in dir(int)) # False print('__iter__' in dir(list)) # True print('__iter__' in dir(dict)) # True print('__iter__' in dir(set)) # True
得出结论:只要能被for循环的数据类型,就一定拥有__iter__方法。
(3)一个列表执行了__iter__()方法之后的返回值就是一个迭代器
print([].__iter__()) # <list_iterator object at 0x00000293238CA108>
print(set(dir([].__iter__())) - set(dir([]))) # {'__length_hint__', '__setstate__', '__next__'}
__length_hint__() #计算元素个数
print([12].__iter__().__length_hint__()) # 元素个数
__next__() #从迭代器中一个一个取值
l = [1,2,3] iterator = l.__iter__() #生成一个迭代器 print(iterator.__next__()) print(iterator.__next__()) print(iterator.__next__()) print(iterator.__next__())
得出结论:
能被for循环的都是iterable,可迭代的-->__iter__。只要含有__iter__()方法都是可迭代的。
[].__iter__()生成一个迭代器-->.__next__通过next可以从迭代器中一个一个的取值
只要含有__iter__()方法的数据类型都是可迭代的——可迭代协议
在for循环时,第一件事就是找是否存在__iter__()方法,若不存在此方法就报错。
2、迭代器的概念
可以被for循环的都是可迭代的,可迭代的内部都有__iter__方法;可迭代器一定可迭代,但可迭代的不一定是迭代器,要看他是否含有__next__方法。可迭代器中的.__iter__()方法可得到一个迭代器,__next__()方法可以一个一个的获取值
可迭代对象包含迭代器
如果一个对象拥有__iter__方法,是可迭代对象;如果一个对象拥有next方法,其就是迭代器
生成器是一种特殊的迭代器,生成器自动实现了“迭代器协议”,即(__iter__和__next__方法)
3、for循环
只有是可迭代对象的时候才能用for,当我们遇到一个新的变量,不确定能否使用for循环的时候,就判断它是否可迭代。
for i in l: pass iterator = l.__iter__() print(iterator.__next__())
当报错时,迭代终止。
迭代器的好处:
(1)从容器类型中一个一个的取值,会把所有的值都取到。
(2)可以节省内存空间。迭代器不会在内存中占用一大块内存,而是随着循环每次生成一个,每次next每次给我一个。
# 生产200万个字符串。随意取值,可用迭代器 def func(): for i in range(2000000): i = 'wahaha%s'%i
print(range(1000000)) # range(0, 1000000) #可迭代对象,并不是一次性生成的,而是一个一个给的 print(list(range(100000))) #得到一个从0到99999的列表
l = [1,2,3,4,5] iterator = l.__iter__() while True: print(iterator.__next__()) #执行完后,自动抛出异常。
在执行时,迭代器最好,for循环替用户做了,不需要用户再__iter__(),__next__()方法了。
3、为什么列表、字典、字符串等是可迭代的,但不是迭代器。
这是因为python的迭代器对象表示的是一个数据流,迭代器对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出stopiteration错误。可以把这个数据流看作是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过__next__()函数实现按需计算下一个数据,所以iterator的计算是惰性的,只要在需要返回下一个数据时它才会计算。
生成器函数——本质上就是我们自己写的函数
1、只要含有yield关键字的函数都是生成器函数,yeild关键字只能写在函数里,且yeild和return不能同时存在。
(1)生成器表达式
列表推导式: sum([i+1 for i in range(1000000000)]) 前面是一个表达式,表示结果,后面跟一个for加一个可迭代的对象,再后面还可以跟# 一个if else 语句进行判断 生成器表达式: sum((i+1 for i in range(1000000000)))
(2)生成器函数,带yield的函数
生成器时一个特殊的函数,可以用作控制循环的迭代行为,python中生成器时迭代器的一种,使用yield代替return返回,每次调用yield会暂停,而可以使用next()函数和send()函数恢复生成器。
def myList(num): # 定义生成器 now = 0 # 当前迭代值,初始为0 while now < num: val = (yield now) # 返回当前迭代值,并接受可能的send发送值;yield在下面会解释 now = now + 1 if val is None else val # val为None,迭代值自增1,否则重新设定当前迭代值为val
生成器函数执行后会得到一个生成器作为返回值。
def generator(): print(1) yield 'a' # 生成器函数:执行之后会得到一个生成器作为返回值 ret = generator() print(ret) ''' <generator object generator at 0x0000021859F797C8> '''
def generator(): print(1) yield 'a' # 生成器函数:执行之后会得到一个生成器作为返回值 ret = generator() print(ret) print(ret.__next__()) # 生成器就是一个迭代器 ''' <generator object generator at 0x0000021859F797C8> 1 a '''
2、yield和return的区别
解释一:
就像打电玩一样,你蓄力发大招的时候,如果执行了return,就直接把大招发出去了,蓄力结束,如果执行了yield,就相当于返回一个生成器对象,每调用一次next(),就发一个大招。
解释二:
return是函数返回值,当执行到return,后续的逻辑代码不再执行
yield是创建迭代器,可以用for来遍历,有点事件触发的意思。
def generator(): print(1) yield 'a' print(2) yield 'b' g = generator() # g为生成器,generator()为生成器函数 ret = g.__next__() print(ret) ''' 1 a '''
def generator(): print(1) yield 'a' print(2) yield 'b' yield 'c' g = generator() # g为生成器,generator()为生成器函数 ret = g.__next__() print(ret) ret = g.__next__() print(ret) ret = g.__next__() print(ret) ''' 1 a 2 b c '''
def generator(): print(1) yield 'a' print(2) yield 'b' yield 'c' g = generator() # g为生成器,generator()为生成器函数 for i in g: print(i) ''' 1 a 2 b c '''
#哇哈哈%i def wahaha(): for i in range(20000): yield '哇哈哈%s'%i g = wahaha() count = 0 for i in g: count += 1 print(i) if count>5:#从生成的函数中取5个值 break print('****', g.__next__()) # 如果需要的话,还可以接着再取 ''' 哇哈哈0 哇哈哈1 哇哈哈2 哇哈哈3 哇哈哈4 哇哈哈5 **** 哇哈哈6 '''
3、为什么列表、字典、字符串等是可迭代的,但不是迭代器。
这是因为python的迭代器对象表示的是一个数据流,迭代器对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出stopiteration错误。可以把这个数据流看作是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过__next__()函数实现按需计算下一个数据,所以iterator的计算是惰性的,只要在需要返回下一个数据时它才会计算。
l = [1,2,3,4] for i in l: print(i) if i == 2: break for i in l: print(i) #循环结束后再次执行函数,从头开始
def wahaha(): for i in range(20000): yield '哇哈哈%s'%i g = wahaha() #g,g1相当于两个迭代器,各走各的。 g1 = wahaha() print(g.__next__()) print(g.__next__()) print(g.__next__()) print(g1.__next__()) ''' 哇哈哈0 哇哈哈1 哇哈哈2 哇哈哈0 '''
def tail(filename): f = open('d:\\huangshurui.txt',encoding='utf-8') while True: line = f.readline() if line: print('****',line.strip('\r\n')) tail('huangshurui.txt') ''' **** 22111 **** 月儿 **** xinger **** 月儿 **** 星儿 **** nihao **** 噢噢噢噢 哦哦 '''
针对以上函数,我想实现在每行前面加上***后打印,除此之外还想在在每行加上。。。后打印,解决此问题,可采用生成器函数
def tail(filename): f = open('d:\\huangshurui.txt',encoding='utf-8') while True: line = f.readline() if line: yield line.strip('\r\n') g = tail('huangshurui.txt') for i in g: if '月儿' in i: #监听过滤的效果 print('****',i)
(一)迭代器
双下方法__inter__(),__next__():很少直接调用的方法,一般情况下是通过其他语法促发的。
可迭代的——含有__inter__()的方法,判断是否含有__inter__方法的语法:'__inter__' in dir(数据类型)
可迭代器协议:含有__inter__和__next__方法
迭代器一定是可迭代的,而可迭代的不一定是迭代器,可迭代的通过调用__iter__()方法就可以得到一个迭代器。
迭代器的优点:
很方便使用,且只能从头到尾的取数据
节省内存空间,按照需要取数据
(二)生成器
1、生成器的本质就是迭代器
2、生成器的表现形式:
生成器函数
生成器表达式
(1)生成器函数
含有yield关键字的函数就是生成器函数
特点:调用函数的时候函数不执行,返回一个生成器;每次调用next方法时会取到一个值,直到取完最后一个,再执行next会报错
(2)从生成器中取值的几个方法
next
for
数据类型的强制转换list(g),但是它不够好,因为太过于占内存
Archiver|手机版|科学网 ( 京ICP备07017567号-12 )
GMT+8, 2024-5-20 05:43
Powered by ScienceNet.cn
Copyright © 2007- 中国科学报社