可迭代对象
# 可迭代对象: 可以用for操作的对象都是可迭代对象
# list tuple str set dict
for i in [1, 2, 3]:
print(i)
print(hasattr(list, "__iter__")) # True
print(hasattr(tuple, "__iter__")) # True
print(hasattr(str, "__iter__")) # True
print(hasattr(set, "__iter__")) # True
print(hasattr(dict, "__iter__")) # True
迭代器
迭代器协议:
1.迭代器类型必须实现__iter__属性和__next__属性
2.iter 方法必须返回self
3.next 必须返回下一个值,如果没有下一个则抛出StopIterator异常
4.对迭代器进行for操作时,每次操作都会执行__next__函数
5.只能迭代一遍
6.for语句的迭代,会忽略StopIteration异常
from typing import Iterator
obj = range(1, 2)
print(isinstance(obj, Iterator)) # False
obj = iter(range(1, 2)) #把range(1, 2)转换为Iterator类型
print(isinstance(obj, Iterator)) # True
可以看到range(1, 2)不是迭代器,但是iter(range(1, 2))是迭代器
先看一看list的属性
for attr in dir(list):
print(attr)
重点是__iter__这个属性,这个属性代表是可迭代对象
再看一看obj的属性
for attr in dir(obj):
print(attr)
可以看到不仅有__iter__属性,还有__next__这个属性
所以,如何判断某个对象是不是迭代器有两种方法。
方法一:isinstance(obj, Iterator)
方法二:看对象有没有__iter__属性和__next__属性
下面来简单实现一个迭代器
class Next(object):
def __init__(self, stop, start=0):
self.start = 0
self.stop = stop
def __iter__(self):
return self
def __next__(self):
"""
如果有下一个数,返回下一个数。如果没有下一个数,则抛出StopIteration异常
:return:
"""
if self.start >= self.stop - 1:
raise StopIteration
else:
self.start += 1
return self.start
if __name__ == '__main__':
obj = Next(5)
for i in obj:
print(i)
输出结果:
每次循环的时候自动执行了__next__函数
快捷键:
在进行迭代的时候,直接输入iter,按回车后:
for in self:
输入itere,按回车后:
for i, in enumerate(self):
应用场景
1.存数据1~10000的数据
list:占10000整数的内存
迭代器:占用几个整数的内存
2.爬虫(用Python批量下载图片)
list:把所有的图片都存进去
迭代器:每次获得一张图片,删除上一张图片,把新图片保存
生成器
生成器的意义:为了快速方便的创建一个迭代器,所以生成器一定是一个迭代器。
生成器的使用:yield关键字,在函数中使用。
如果一个函数中有yield关键字,调用函数的时候就不会执行函数的内容,会返回一个对象(这个对象是生成器类)
def f():
print("1")
yield 1
yield 2
if __name__ == '__main__':
obj = f()
像这样的话,f就不会执行,不会打印1
现有需求:用迭代器实现平方, 比如传(1,3)返回1 4 9
用正常的写法:
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(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(value)
最简便的方法(自动使用yield):
squares2 = (i * i for i in range(1, 4))
原理
当要访问生成器中的__next__方法时,函数会变成runing状态,当执行完yield时,函数变成非runing状态(挂起)。只有再次执行生成器对象的__next__方法时,函数才会被唤醒。
运用场景
一个image_spider.py文件,他的功能是批量下载网站所有的图片
方法一:
get_all_urls() 获得网站所有页码对应的url
urls = []
return urls
get_all_image_path(url) 获得某url页面中所有的图片地址
images = []
return images
download(image_path) 根据图片地址下载图片
whit open (...) as f:
f.write(...)
main:
urls = get_all_urls() # 获得所有网页的地址
for url in urls:
img_path_list = get_all_image_path(url) # 获得当前页所有图片
for img_path in img_path_list:
download(img_path)
# 太占内存 必须先获取url再执行下面的,有阻塞
方法二:
get_all_image_path() 获得某url页面中所有的图片地址
image_path = 'http://......'
yield image_path
download(image_path) 根据图片地址下载图片
whit open (...) as f:
f.write(...)
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)