python迭代器和生成器理解

前言

大家都知道Python有功能强大三个器:装饰器、迭代器、以及生成器
但是我对这三个工具的理解过程可是花费了不少的时间,踩了不少的坑。

上篇文章谈了一下我对Python装饰器的理解
那么这篇文章再谈一谈我对迭代器以及生成器的理解。
内容仅供参考,如有错误,欢迎指正

迭代器(作用:节省空间)

  1. 迭代(iterate)意为着重复多次,就像循环那样
  2. 实现了__iter__方法的对象是可迭代的,而同时实现了__iter__和__next__的对象是迭代器
  3. 调用__next__方法时或者(next()),迭代器返回其下一个值
  4. 如果迭代器没有可供返回的值,触发StopIteration异常
  5. 迭代器的作用是保存一个方法 而不是结果 减少内存空间的占用

如果一个对象是迭代器 那么他一定实现了_iter__和_next__方法

首先理解for循环遍历原理(重点)

for i in [1,2,3,4,5]:
    print(i)
# for循环遍历的对象一定是一个可迭代的对象
# 可以使用isinstance(对象, Iterable) 返回T/F 来判断是否为可迭代对象
# 实现了__iter__方法的对象就是一个可迭代的对象
# 以双下划线开头的方法是类的魔法方法  在创建对象的时候会自动调用
# for 循环遍历其实是执行了一下一系列操作
for temp in obj:
    pass
# 1.判断obj是否是可迭代对象(即看obj对象中是否有__iter__方法)
# 2.在第一步成立的情况下,调用iter函数,即iter(obj) 执行这个方法会去调用类中实现的__iter__方法
# 然后得到了__iter__方法的返回值
# 3.__iter__方法的返回值应该是一个迭代器对象(即实现了__iter__和__next__方法的对象)
# 4.每次遍历都会执行迭代器对象中的__next__方法

面试题 range和xrange的区别

在python2解释器下,range(10)会直接返回一个0-9的列表 占用系统空间较多

而xrange(10)并不会直接返回列表,他返回的是一个xrange()迭代器对象

这个对象中保存了range的方法

即xrange返回的是一个可迭代对象,什么时候用,什么时候去,占用系统空间极少

在python3中,range()方法已经完全变成了xrange()的用法 返回可迭代对象

实例中理解

两个类实现迭代器,目的:能够使用for循环遍历出类中定义的列表中的数据

**

import time
from _collections_abc import Iterable
from _collections_abc import Iterator


class Classmate(object):
    # 初始化方法定义了一个名为names的空列表
    def __init__(self):
        self.names = list()
	# 使用add方法向names中添加数据
    def add(self, name):
        self.names.append(name)
    # 只有实现了__iter__方法的对象,才是一个可迭代的对象
    def __iter__(self):
        return ClassIterator(self)   # __iter__方法返回的应该是一个迭代器对象


class ClassIterator(object):
	# 这个类中实现了__iter__和__next__方法 因此这个类创建出来的实例对象就是一个迭代器对象
    def __init__(self, obj):
        self.obj = obj
        self.current = 0
    def __iter__(self):
        pass
    def __next__(self):
        if self.current < len(self.obj.names):
            ret = self.obj.names[self.current]
            self.current += 1
            return ret
        else:
            raise StopIteration



classmate = Classmate()
classmate.add('老王')
classmate.add('老李')
classmate.add('老张')
# iter返回迭代器对象
# classmate_iterator = iter(classmate)
# print('判断classmate_iterator是否为迭代器', isinstance(classmate_iterator, Iterator))  #  True
# print('判断classmate是否为可迭代对象', isinstance(classmate, Iterable))    # True
# next函数会是迭代器对象自动调用__next__()方法
# print(next(classmate_iterator))
# print(next(classmate_iterator))

for name in classmate:
    print(name)
    time.sleep(1)
# 输出---------------------------------------------
老王
老李
老张

进阶:一个类实现迭代器,目的:能够使用for循环遍历出类中定义的列表中的数据

import time
from _collections_abc import Iterable
from _collections_abc import Iterator


class Classmate(object):
    def __init__(self):
        self.names = list()
        self.current = 0

    def add(self, name):
        self.names.append(name)
    # 只有实现了__iter__方法的对象,才是一个可迭代的对象
    def __iter__(self):   # __iter__返回的一定是一个迭代器对象  只有实现了__iter__和__next__方法的对象才是迭代器对象
        return self		 # 因此这里直接返回self,self就是该类实例对象的代表,但是此类中只实现了__iter__方法 

    def __next__(self):   # 因此在实现__next__方法即可 这样就实现了
        if self.current < len(self.names):
            ret = self.names[self.current]
            self.current += 1
            return ret
        else:
            raise StopIteration

classmate = Classmate()
classmate.add('老王')
classmate.add('老李')
classmate.add('老张')
# iter返回迭代器对象
# classmate_iterator = iter(classmate)
# print('判断classmate_iterator是否为迭代器', isinstance(classmate_iterator, Iterator))
# print('判断classmate是否为可迭代对象', isinstance(classmate, Iterable))
# next函数会是迭代器对象自动调用__next__()方法
# print(next(classmate_iterator))
# print(next(classmate_iterator))

for name in classmate:
    print(name)
    time.sleep(1)
# 输出-------------------------------------
老王
老李
老张

生成器(作用:同样是节省空间)

  1. 生成器是一种使用普通函数语法定义的迭代器
  2. 包含yield语句的函数都被称为生成器
  3. 不使用return返回一个值,而是可以生成多个值,每次一个

yield和return的区别(个人理解)相当于移交控制权

# return和yield的区别
def test():
    # return a
    # yield a
    print('hahaha')
# 执行return后  会真真正正的返回a的值,但是下面的代码不会再运行
# 使用yield后  相当于该函数变成了一个生成器模板 就像类是对象的模板一样
# 如果下面有生成器对象  当生成器被唤醒后,即调用了next或者send方法之后 yield会卡住 并且将a的值传递给next后的结果
# 当生成器再次被唤醒时, 会接着执行yield下面的代码

实例中理解

使用生成器实现斐波那契数列

# 使用生成器完成斐波那契数列

# yield 如何理解 
# yield 相当于移交控制权 执行到yield后会卡住,不在继续执行,然后yield的返回值拿到for循环调用处使用, 可以理解为暂时中止,等到for循环执行后
# 接着执行yield下面的语句

def create_num(all_num):
    a, b = 0, 1
    current = 0
    while current < all_num:
        yield a  # 如果一个函数中有yield语句,那么这个就不是函数了 而是一个生成器模板 相当于类 和类是一个效果
        a, b = b, a+b
        current += 1
    return 'ok'    # 只有在程序抛出异常的时候才会有返回值 因为yield的存在,会反复执行并且递交数据

# 如果在调用create_num的时候,发现函数中有yield,那么此时不是调用函数,而是创建一个生成器对象
obj = create_num(10)   # obj就是生成器对象   create_num相当于类
# print(obj)
res = next(obj)
print(res)
res = next(obj)
print(res)
print('-' * 40)
for i in obj:
    print(i)
# 输出---------------------------------------
0
1
----------------------------------------
1
2
3
5
8
13
21
34

生成器两种唤醒方式 next()和send()

# 使用生成器完成斐波那契数列

def create_num(all_num):
    a, b = 0, 1
    current = 0
    while current < all_num:
        ret = yield a  # 如果一个函数中有yield语句,那么这个就不是函数了 而是一个生成器模板
        print('>>>ret>>>>', ret)
        a, b = b, a+b
        current += 1
# 生成器对象obj
obj = create_num(10)
# 唤醒方式1.调用next方法
res = next(obj)
print(res)
# 唤醒方式2.调用send()方法  区别在于是否可以传入参数
# 传入参数是谁就相当于yield返回值是谁 
ret = obj.send('hahha')
print(ret)
# 输出------------------------------
0
>>>ret>>>> hahha
1

使用迭代器和生成器模拟range函数迭代效果

def use_range():
    '''python内置range函数'''
    for i in range(5,10):
        print(i)


class IterRange(object):
    '''使用迭代器来模拟range函数'''
    def __init__(self, start, end):
        self.start = start - 1
        self.end = end

    def __next__(self):
        self.start += 1
        if self.start >= self.end:
            raise StopIteration
        return self.start

    def __iter__(self):
        return self


class GenRange(object):
    '''使用生成器模拟range函数'''
    def __init__(self, start, end):
        self.start = start - 1
        self.end = end

    def gen_num(self):
        while True:
            if self.start >= self.end - 1:
                break
            self.start += 1
            yield self.start


def get_num(start, end):
    '''使用函数模拟range函数功能'''
    start -= 1
    while True:
        if start >= end - 1:
            break
        start += 1
        # 使用用yield的函数就是一个生成器对象
        yield start


if __name__ == '__main__':
    use_range()
    print('迭代器模拟range'+'-' * 50)
    iter = IterRange(5, 10)
    l = list(iter)
    print(l)
    print('生成器模拟range'+'-' * 50)
    gen = GenRange(5, 10).gen_num()
    print(gen)
    print(list(gen))
    print('函数模拟range'+'-' * 50)
    res = get_num(5, 10)
    print(res)
    print(list(res))
    
# 输出结果---------------------------------------
5
6
7
8
9
迭代器模拟range--------------------------------------------------
[5, 6, 7, 8, 9]
生成器模拟range--------------------------------------------------
<generator object GenRange.gen_num at 0x000002451F3DE7C8>
[5, 6, 7, 8, 9]
函数模拟range--------------------------------------------------
<generator object get_num at 0x000002451F3DE848>
[5, 6, 7, 8, 9]

以上是我对Python迭代器和生成器的理解,希望能够帮助到大家。
如果有错误,请大家指出谢谢。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值