迭代器、生成器详解

一、迭代器

首先来理解一下什么是迭代

1、迭代:

概念:不断重复某一动作,这一次的操作是基于上一次的结果完成的,中间不能断掉,比如:使用for循环遍历列表获取值的过程

for value in [2, 3, 4]:
    print(value)

 2、可迭代对象:

用for循环遍历取值的对象叫做可迭代对象, 比如:列表、元组、字典、集合、range、字符串

3、判断对象是否是可迭代对象:

from collections import Iterable

result = isinstance((3,5),Iterable)
print("元组是否是可迭代对象:", result)

result = isinstance([3, 5], Iterable)
print("列表是否是可迭代对象:", result)

result = isinstance({"name": "张三"}, Iterable)
print("字典是否是可迭代对象:", result)

result = isinstance("hello", Iterable)
print("字符串是否是可迭代对象:", result)

result = isinstance({3, 5}, Iterable)
print("集合是否是可迭代对象:", result)

result = isinstance(range(5), Iterable)
print("range是否是可迭代对象:", result)

result = isinstance(5, Iterable)
print("整数是否是可迭代对象:", result)

# 以后还根据对象判断是否是其它类型,比如以后可以判断函数里面的参数是否是自己想要的类型
result = isinstance(5, int)
print("整数是否是int类型对象:", result)

运行结果:

元组是否是可迭代对象: True
列表是否是可迭代对象: True
字典是否是可迭代对象: True
字符串是否是可迭代对象: True
集合是否是可迭代对象: True
range是否是可迭代对象: True
整数是否是可迭代对象: False
整数是否是int类型对象: True

由上面的运行结果可以知道:只有整型不是可迭代对象,那么,python内部是如何知道一个对象是否为可迭代对象呢?原因是在每一种数据类型对象中,都会有有一个__iter__()方法,正是因为这个方法,才使得这些基本数据类型变为可迭代。 

4、自定义可迭代对象

在类里面定义__iter__方法创建的对象就是可迭代对象

from collections import Iterable

class MyList(object):

    def __init__(self):
        self.my_list = list()

    # 添加指定元素
    def append_item(self, item):
        self.my_list.append(item)

    def __iter__(self):
        # 可迭代对象的本质:遍历可迭代对象的时候其实获取的是可迭代对象的迭代器, 然后通过迭代器获取对象中的数据
        pass

my_list = MyList()
my_list.append_item(1)
my_list.append_item(2)
result = isinstance(my_list, Iterable)
print(result)

for value in my_list:
    print(value)

运行结果:

True

 

TypeError: iter() returned non-iterator of type 'NoneType'

通过执行结果可以看出来,遍历可迭代对象依次获取数据需要迭代器

5、自定义迭代器对象:

在类里面定义__iter____next__方法创建的对象就是迭代器对象

# 自定义可迭代对象: 在自定义类型里面提供一个__iter__的方法,创建的对象是可迭代对象
from collections import Iterable
from collections import Iterator


# 自定义可迭代类型
class MyList(object):

    def __init__(self):
        # 定义列表属性保存用户添加的数据
        self.my_list = list()

    # 添加数据的方法
    def append_data(self, data):
        self.my_list.append(data)

    # 在类里面提供__iter__方法,那么保证创建的对象是可迭代对象
    def __iter__(self):
        # 可迭代对象的本质: 是通过迭代器帮助可迭代对象依次迭代对象中的每一个数据
        # 真正完成获取数据的操作是通过迭代器完成的
        my_iterator = MyIterator(self.my_list)
        result = isinstance(my_iterator, Iterator)
        print("my_iterator是否是迭代器:", result)
        return my_iterator


# 自定义迭代器对象: 在自定义类里面提供__iter__和__next__的方法,创建的对象就是迭代器对象
# 自定义迭代器类型
class MyIterator(object):

    def __init__(self, current_list):
        # 保存外界传入过来的数据对象
        self.current_list = current_list
        # 记录获取数据的下标
        self.current_index = 0

    def __iter__(self):
        return self

    # 使用__next__方法获取指定对象中的数据
    def __next__(self):
        # 判断下标是否越界
        if self.current_index < len(self.current_list):
            # 根据下标获取数据对象中的指定数据
            value = self.current_list[self.current_index]
            # 获取数据成功对下标加上1
            self.current_index += 1
            return value
        else:
            # 代码执行到此说明下标越界,停止取值操作,抛出停止迭代异常
            raise StopIteration


# 通过自定义可迭代类型创建自定义可迭代对象
my_iterable = MyList()
my_iterable.append_data(1)
my_iterable.append_data(2)
result = isinstance(my_iterable, Iterable)
print(result)

for value in my_iterable:
    print(value)

运行结果:

True
my_iterator是否是迭代器: True
1
2

6、迭代器

作用:就是是记录当前数据的位置以便获取下一个位置的值,迭代器与可迭代对象区别在于:__next__()方法

iter()函数与next()函数

  1. iter函数: 获取可迭代对象的迭代器,会调用可迭代对象身上的__iter__方法
  2. next函数: 获取迭代器中下一个值,会调用迭代器对象身上的__next__方法
# 自定义可迭代对象: 在类里面定义__iter__方法创建的对象就是可迭代对象
class MyList(object):

    def __init__(self):
        self.my_list = list()

    # 添加指定元素
    def append_item(self, item):
        self.my_list.append(item)

    def __iter__(self):
        # 可迭代对象的本质:遍历可迭代对象的时候其实获取的是可迭代对象的迭代器, 然后通过迭代器获取对象中的数据
        my_iterator = MyIterator(self.my_list)
        return my_iterator


# 自定义迭代器对象: 在类里面定义__iter__和__next__方法创建的对象就是迭代器对象
# 迭代器是记录当前数据的位置以便获取下一个位置的值
class MyIterator(object):

    def __init__(self, my_list):
        self.my_list = my_list

        # 记录当前获取数据的下标
        self.current_index = 0

    def __iter__(self):
        return self

    # 获取迭代器中下一个值
    def __next__(self):
        if self.current_index < len(self.my_list):
            self.current_index += 1
            return self.my_list[self.current_index - 1]
        else:
            # 数据取完了,需要抛出一个停止迭代的异常
            raise StopIteration

# 创建了一个自定义的可迭代对象
my_list = MyList()
my_list.append_item(1)
my_list.append_item(2)

# 获取可迭代对象的迭代器
my_iterator = iter(my_list)
print(my_iterator)
# 获取迭代器中下一个值
# value = next(my_iterator)
# print(value)

# 循环通过迭代器获取数据
while True:
    try:
        value = next(my_iterator)
        print(value)
    except StopIteration as e:
        break

 7、for循环的本质

l = [a,b,c,d,e]
for i in l:
    print(i)

根据之前的分析,我们知道 l = [1,2,3,4,5]是一个可迭代对象。而且可迭代对象是不可以直接从其中取到元素。那么为啥我们还能从列表L中取到元素呢?这一切都是因为for循环内部实现。在for循环内部,首先L会调用__iter__()方法,将列表L变为一个迭代器,然后这个迭代器再调用其__next__()方法,返回取到的第一个值,这个元素就被赋值给了i,接着就打印输出了。

8、迭代器的应用场景

我们发现迭代器最核心的功能就是可以通过next()函数的调用来返回下一个数据值。如果每次返回的数据值不是在一个已有的数据集合中读取的,而是通过程序按照一定的规律计算生成的,那么也就意味着可以不用再依赖一个已有的数据集合,也就是说不用再将所有要迭代的数据都一次性缓存下来供后续依次读取,这样可以节省大量的存储(内存)空间

举个例子,比如,数学中有个著名的斐波拉契数列(Fibonacci),数列中第一个数为0,第二个数为1,其后的每一个数都可由前两个数相加得到:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...

现在我们想要通过for...in...循环来遍历迭代斐波那契数列中的前n个数。那么这个斐波那契数列我们就可以用迭代器来实现,每次迭代都通过数学计算来生成下一个数。

# 迭代器完成斐波那契的好处:1. 节省内存空间,因为每次根据算法只生成一个值 2.生成数列的个数没有上限控制
class Fibonacci(object):

    def __init__(self, num):
        # num: 表示根据个数生成fibonacci数列
        self.num = num
        # 保存fibonacci数列前面两个值
        self.first_value = 0
        self.second_value = 1
        # 记录生成斐波那契数列的下标
        self.current_index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.current_index < self.num:
            result = self.first_value
            self.first_value, self.second_value = self.second_value, self.first_value + self.second_value
            # 生成数据完成以后对下标加上1
            self.current_index += 1
            return result
        else:
            # 表示数列生成完成
            raise StopIteration


# 创建生成5个斐波那契数列的对象(迭代器)
fib = Fibonacci(5)
# value = next(fib)
# print(value)
# value = next(fib)
# print(value)
for value in fib:
    print(value)

运行结果:

0
1
1
2
3

 总结迭代器优点:

    1.节约内存

    2.不依赖索引取值

    3.实现惰性计算(什么时候需要,在取值出来计算)

二、生成器

1. 生成器的概念

生成器是一类特殊的迭代器,它不需要再像上面的类一样写__iter__()和__next__()方法了, 使用更加方便,它依然可以使用next函数和for循环取值

2. 创建生成器方法

a:生成器表达式(此方法很简单,只要把一个列表生成式的 [ ] 改成 ( ) 就可以)

# 生成器是一个特殊的迭代器,可以使用next函数和for循环获取生成器中的数据

# 列表推导式
my_list = [i + 1 for i in range(3)]
print(my_list)

# 创建一个生成器
my_generator = (i + 1 for i in range(3))
print(my_generator)

# value = next(my_generator)
# print(value)

for value in my_generator:
    print(value)

执行结果:

[1, 2, 3]
<generator object <genexpr> at 0x000002517F8D1DB0>
1
2
3

b:生成器函数(以斐波那契数列为例)

在def函数里面看到有yield关键字那么就是生成器

def fibonacci(num):
    a = 0
    b = 1
    # 记录生成fibonacci数字的下标
    current_index = 0
    print("--11---")
    while current_index < num:
        result = a
        a, b = b, a + b
        current_index += 1
        print("--22---")
        # 代码执行到yield会暂停,然后把结果返回出去,下次启动生成器会在暂停的位置继续往下执行
        yield result
        print("--33---")


fib = fibonacci(5)
value = next(fib)
print(value)
value = next(fib)
print(value)

value = next(fib)
print(value)

# for value in fib:
#     print(value)

在使用生成器实现的方式中,我们将原本在迭代器__next__方法中实现的基本逻辑放到一个函数中来实现,但是将每次迭代返回数值的return换成了yield,此时新定义的函数便不再是函数,而是一个生成器了。

生成器小结:

  • 使用生成器表达式取代列表推导式可以同时节省 cpu 和 内存(RAM)
  • 是可迭代对象
  • 实现了延迟计算,省内存

注意:生成器只能遍历一次


完结,(*^▽^*)

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值