迭代器和生成器
可迭代对象
- 可迭代对象 有__iter__属性
def my_range(stop):
# 模拟了python2中的range(还有xrange)
# python2 中的range性能不太好,python3 去掉了xrange并优化了range函数
value = 1
result = []
while value < stop:
result.append(value)
value += 1
return result
if __name__ == '__main__':
print(my_range(10)) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
迭代器(iterator)
什么是迭代器
如何判断某个对象是不是迭代器
- isinstance(object, Iterator) # iterator是一种内置类型,类似于list turpe dict int
- 看对象是否有__iter__ 和__next__属性
# 需要先导入 Iterator
obj = iter(range(1, 2))
print(isinstance([1, 2], list)) # list :True
print(isinstance(obj, Iterator)) # 迭代器:True
print(isinstance([1, 2], Iterator)) # 迭代器: False
迭代器协议:
- 迭代器类型必须实现 iter 和__next__(python2 中是next)
- __iter__方法必须 返回 self
- next 必须返回下一个值,如果没有下一个则抛出StopIteration异常
- 对迭代器进行for操作时,每次擦欧洲哦都会执行__next__方法
- 只能迭代一遍
- for语句的迭代,会忽略StopIteration异常
- 迭代器与list相比,迭代器省内存
迭代器与可迭代对象的区别:
- 迭代器比可迭代对象多了__next__属性
实现一个迭代器
# range(1,5) 1,2,3,4
# Next(5) 1 2 3 4
class Next(object):
def __init__(self, stop, start=0):
self.start = 0
self.stop = stop
def __iter__(self):
return self
def __next__(self):
"""如果有下一个数,则返回下一个数;如果没有下一个数,则抛出StopIteration异常"""
if self.start >= self.stop - 1:
raise StopIteration
self.start += 1
return self.start
if __name__ == '__main__':
obj = Next(5)
# 输入itere 快速创建迭代
for i, value in enumerate(obj):
print(i, value)
# 输入iter 快速创建迭代
for i in obj:
print(i)
# 迭代器只能迭代一次
# print(obj.__next__()) # 异常,raise StopIteration
迭代器应用场景,迭代的意义
- list存1~10000的数据 (占 10000个整数的内存),而迭代器 占用几个整数的内存
- 爬虫(用Python批量下载图片) 把所有图片都放到list中,占用内存; 每次获得一个图片,把图片保存,
类似装水: 把一桶水 用杯子 转义到 另一个水桶中
list:50个杯子,
迭代器:1个杯子
# image_spider.py 功能:批量下载某网站所有的图片, 伪代码思路如下:
def get_all_urls(): # 获得网站所有页码对应的url
urls = []
return urls
get_all_image_path(url) :获得某url页面中所有的图片地址
img_path = 'http://adadf'
...
yield img_path
download(img_path) : 下载图片
with open(...) as f:
f.write(...)
def main():
urls = get_all_urls() # 获得所有网页的地址
for url in urls:
img_path_gen = get_all_image_path(url) # 获得当前页所有图片地址
for img_path in img_path_gen:
download(img_path)
生成器(generator)
生成器的定义
生成器的意义:为了快速方便地创建一个迭代器,所以生成器一定是一个迭代器
什么是生成器:
生成器函数在Python中与迭代器协议的概念联系在一起。简而言之,包含yield语句的函数会被特地编译成生成器。当函数被调用时,他们返回一个生成器对象,这个对象支持迭代器接口。函数也许会有个return语句,但它的作用是用来yield产生值的。
为什么要使用生成器:
Python使用生成器对延迟操作提供了支持。所谓的延迟操作,是指在需要的时候才产生结果,而不是立即产生结果。所以生成器也有了如下的好处:
- 节省资源消耗,和声明序列不同的是生成器在不使用的时候几乎不占内存,也没有声明计算过程!
- 使用的时候,生成器是随用随生成,用完即刻释放,非常高效!
- 可在单线程下实现并发运算处理效果。
yield关键字
- yield关键字来实现快速创建迭代器
- yield怎么用? 在函数中用
- 如果一个函数中有yield关键字,调用函数的时候回不会执行函数的内容,会返回一个对象(这个对象类型是生成器类)
- yield return 区别
共同点:都是Python的关键字
不同点:return 是结束函数并返回值,yield是暂时离开函数
实现一个生成器
手动实现:平方, 传参(1,3) 返回:1 4 9
传统方法实现:(占内存)
result = []
for i in [1, 2, 3]:
result.append(i * i)
print(result)
生成器方法实现
- 方法一
class Squares(object):
def __init__(self, start, stop):
self.start = start
self.stop = stop
def __iter__(self):
return self
def __next__(self):
if self.start > self.stop:
raise StopIteration
current = self.start * self.start
self.start += 1
return current
if __name__ == '__main__':
iterator = Squares(1, 3)
for i, value in enumerate(iterator):
print(i, value)
- 方法二
def squares(start, stop): # 第二种-生成器
for i in range(start, stop + 1):
yield i * i
if __name__ == '__main__':
iterator = squares(1, 3)
for i, value in enumerate(iterator):
print(i, value)
print(type(squares(1, 4))) # <class 'generator'>
- 方法三
squares2 = (i * i for i in range(1, 4)) # 简写的生成器
print(squares2)
print(type(squares2)) # <class 'generator'>
生成器是如何执行的
def f():
print("开始执行")
a = 1
yield a
print("~~~~~~~~~~~")
a = 2
yield a
def f2():
result = []
result.append(1)
result.append(2)
return result
# 当要访问生成器的__next__方法时,函数会编程running状态,当执行完yield时,函数变成非running状态(即挂起),
# 只有再次执行生成器对象的__next__方法时函数才会被唤醒。
# 什么情况下会执行生成器对象的__next__方法呢?(获取生成器下一个值的时候)
if __name__ == '__main__':
obj = f() # 如果一个函数中有yield关键字,调用函数的时候不会执行函数的内容,会返回一个对象(这个对象类型是生成器类)
for i, value in enumerate(obj):
print(i, value)
print(type(obj)) # <class 'generator'>
# 判断生成器是一个迭代器
print(hasattr(obj, "__next__")) # True
print(hasattr(obj, "__iter__")) # True