python 迭代器和生成器

可迭代对象

  • 可迭代对象 有__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)

什么是迭代器

如何判断某个对象是不是迭代器

  1. isinstance(object, Iterator) # iterator是一种内置类型,类似于list turpe dict int
  2. 看对象是否有__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

迭代器协议:

  1. 迭代器类型必须实现 iter 和__next__(python2 中是next)
  2. __iter__方法必须 返回 self
  3. next 必须返回下一个值,如果没有下一个则抛出StopIteration异常
  4. 对迭代器进行for操作时,每次擦欧洲哦都会执行__next__方法
  5. 只能迭代一遍
  6. for语句的迭代,会忽略StopIteration异常
  7. 迭代器与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
迭代器应用场景,迭代的意义
  1. list存1~10000的数据 (占 10000个整数的内存),而迭代器 占用几个整数的内存
  2. 爬虫(用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使用生成器对延迟操作提供了支持。所谓的延迟操作,是指在需要的时候才产生结果,而不是立即产生结果。所以生成器也有了如下的好处:

  1. 节省资源消耗,和声明序列不同的是生成器在不使用的时候几乎不占内存,也没有声明计算过程!
  2. 使用的时候,生成器是随用随生成,用完即刻释放,非常高效!
  3. 可在单线程下实现并发运算处理效果。
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)
生成器方法实现
  1. 方法一
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)
  1. 方法二
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'>
  1. 方法三
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
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值