思考:
一、迭代器/可迭代对象
1.可迭代对象(费内存)
老版本python:range比较费内存(需要多少数据,全部存在list中,在数据量比较大的情况下非常耗内存)
range实现过程:
from typing import Iterable def range_test(stop): start = 0 result = [] while start < stop: result.append(start) start += 1 return result if __name__ == '__main__': print(isinstance(range_test(10),Iterable)) # 判断返回结果是不是可迭代,True print(range_test(10)) # output:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
2.可迭代对象(省内存)
from typing import Iterable # Next含有__next__()方法,每次变量+1,一次只占用一个数的内存 class Next(object): """Next class""" def __init__(self, stop, start=-1): self.start = start self.stop = stop def __next__(self): if self.start >= self.stop - 1: raise StopIteration self.start += 1 return self.start
# 含有__iter__()方法,return调用Next class MyRange: def __init__(self, stop): self.stop = stop def __iter__(self): return Next(self.stop)
使用调式模式,单步执行,可看到程序执行Next,每执行一次,生成一个数据。不耗内存
for是迭代(自动执行__iter__,__next__),while是循环(手动指定函数)
if __name__ == '__main__': print(Next(10).__next__()) # 0 for item in MyRange(10): # for循环自动执行__iter__,__next__方法 print(item) # while循环 num = MyRange(10).__iter__() i = 0 while i < 10: print(num.__next__()) # 0,1,2,3,4,5,6,7,8,9 i += 1
3.可迭代对象定义
可以被for迭代的对象都是可迭代对象
对象有__iter__方法的,都是可迭代对象(如果没有__iter__方法,他不可以被for迭代),__iter__要求返回值必须是一个迭代器(严格来说,返回值必须有__next__方法)
常见的可迭代对象;list、tuple、string、dict
判断某个每个对象是不是可迭代对象:
-
可通过dir()查看所有属性中是否有__iter__方法
-
也可以通过hasattr(list,'__iter__'),查看list是否有__iter__方法。返回True则说明有该方法
判断MyRange(10)是否是可迭代对象:
print(isinstance(MyRange(10),Iterable) 返回True,则MyRange(10)是可迭代对象,因为有__iter__方法
判断Next(10)是否是可迭代对象:
print(isinstance(Next(10),Iterable) 返回False,则Next(10)不是可迭代对象,因为没有__iter__方法
4.迭代器定义
迭代器必须有__iter__ __next__ 属性
核心:通过 __next__方法记住迭代的位置,有没有迭代完毕
迭代器是特殊的可迭代对象,
可以被for迭代,for迭代迭代器时,会自动执行__next__方法
迭代器比较省内存,一次只给一个数。
没有迭代器,则用不了for,只能用while
判断Next(10)是否是迭代器
print(isinstance(Next(10),Iterator) # Fasle 因为虽然有__next__方法,但是缺少__iter__方法
注意:__iter__必须返回一个迭代器(严格来说,返回值必须有__next__方法)
二、生成器
因为迭代器手工实现比较麻烦
所以python提供了生成器对象,生成器对象的目的就是获得迭代器对象(非常高效)
def fun(): print("在yield1前面") yield 1 print("在yield1后面,yield2前面") yield 2 if __name__ == '__main__': # 验证是否是生成器 print(fun()) # <generator object fun at 0x104d00150> 结果是一个生成器对象,但是没有运行函数 print(hasattr(fun(),"__iter__")) # True print(hasattr(fun(),"__next__")) # True # 运行生成器,必须要调用__next__ print(fun().__next__()) # 1 / 在yield1前面 # 运行出全部结果,for自动调用__next__ for i in fun(): print(i)
注意:
-
当python解释器看到函数中有yield的时候,不会立即去调用这个函数,而是先创建一个生成器对象。而这个生成器对象会记住函数的状态
-
在for循环中,运行到第一个yield时,就暂时退出了(类似return,但区别是return是永久跳出不会再回去了)。再次运行时(__next__记住位置),会从yield1后面开始运行
-
当执行next(生成器对象)时,会执行生成器对象的yield.
生成器是特殊的迭代器,迭代器是特殊的可迭代对象
生成器是可迭代对象
如果创建生成器对象(通过yield)
yield:关键字
记住函数执行的状态
当执行next(g)时,会执行g中的yield
yield和return的区别:
return:跳出函数,不回来了
yield:暂时跳出,还会回来
生成器的使用场景:
生成器为什么省内存:因为有__next__
生成器是为了快速获得含有__iter__ __next__的方法
场景一:我需要99999999999个数字,一下子把1——9999999999999个数字放到list中
场景二:我需要99999999999个数字,需要1生成1,需要2生成2~~~~~需要999999999999生成999999999999
场景二是next思想(生成器、迭代器)
转载仅供参考:http://testingpai.com/article/1605148018144