Python迭代器和生成器学习笔记

目录

1.可迭代对象(Iterable):

2.迭代器对象(Iterator):

3.可迭代对象和迭代器对象的关联与区别:

4.迭代器的优缺点:

5.生成器(generator):

6.总结


学习迭代器之前我们先要学习一下可迭代对象。

1.可迭代对象(Iterable):

在 Python 中,可迭代对象(Iterable)是指可以逐一遍历其元素的对象。大部分容器(Container)都可以看作是可迭代对象,例如列表(list)、元组(tuple)、字典(dict)、集合(set)、字符串(str)等等。这些类型的对象都可以使用 for...in... 语句对其进行遍历。

# 列表
for i in [1, 2, 3, 4, 5]:
    print(i)
​
# 元组
for i in (1, 2, 3, 4, 5):
    print(i)
​
# 字符串
for char in 'hello':
    print(char)
​
# 字典
for key, value in {'a': 1, 'b': 2, 'c': 3}.items():
    print(key, value)
​
# 集合
for i in {1, 2, 3, 4, 5}:
    print(i)

对于列表(list)、元组(tuple)、字符串(str),这些对象包含多个元素,Python 提供了iter()方法来获取每个元素,所以它们是可迭代的。对于字典(dict),Python 提供了 iter() 方法来获取它的键,可以通过 keys() 方法获取所有的键,通过 values() 方法获取所有的值,或者通过 items() 方法获取所有的键值对,所以字典也是可迭代的。集合(set)数据类型也提供了 iter() 方法,用于获取集合中的每一个元素,所以集合也是可迭代的。

2.迭代器对象(Iterator):

这时候我们要介绍一下iter()函数和next()函数:

1.iter()函数:iter() 是 Python 的内置函数,它会尝试调用对象的 __iter__ 方法,并返回由该方法返回的迭代器,也就是说iter()函数的主要功能就是接收一个可迭代对象,然后返回一个迭代器对象。(具体来说,iter()函数会尝试调用传入对象的 __iter__()方法,这个方法需要返回一个迭代器对象。这个迭代器对象需要实现了 __next__()方法,用于不断获取或计算下一个值,直到抛出StopIteration异常表示迭代结束。)

这里需要注意的是,并非所有对象都可以使用iter(),它必须是一个可迭代对象。例如,我们熟知的列表、元组、字符串、字典、文件、生成器等,都是可迭代对象,因为它们的类都实现了 __iter__() 方法。

2.next()函数:next()是Python的一个内置函数,它用于获取迭代器的下一个项目。这个函数接受两个参数:第一个参数是迭代器对象,第二个参数是可选的,默认值为None,如果迭代器耗尽,那么返回这个默认值。如果你没有提供第二个参数,并且迭代已经到达了末尾,那么next()函数将会触发一个StopIteration异常,表示迭代已经完成。

3.iter()next()用法举例:

my_list = [1, 2, 3]
my_iter = iter(my_list)  # 将列表迭代对象转化为迭代器对象
​
print(next(my_iter))  # 输出: 1
print(next(my_iter))  # 输出: 2
print(next(my_iter))  # 输出: 3
​
# 现在迭代已经到达末尾
# 如果没有提供第二个参数,next()会触发StopIteration异常
# print(next(my_iter))
​
# 如果提供了第二个参数,next()会返回这个值,并不会触发异常
print(next(my_iter, '没有更多元素了'))  # 输出: "没有更多元素了"

3.可迭代对象和迭代器对象的关联与区别:

可迭代对象(Iterable)迭代器对象(Iterator)是Python的两个重要概念,它们之间存在紧密的联系,但也有明显的区别。

可迭代对象(Iterable)是指可以使用 iter() 函数生成迭代器的对象,包含 __iter__() 方法。例如,列表、字符串、字典、集合等都是可迭代对象。

my_list = [1, 2, 3]
iter_obj = iter(my_list)  # my_list 就是一个可迭代对象

迭代器对象(Iterator)则是指实现了 __next__() 方法和 __iter__() 方法(返回自身)的对象。通过调用 next() 函数或者 __next__() 方法可以获取下一项,如果没有更多的元素,则会抛出 StopIteration 异常。迭代器对象从某种意义上来说,也是一种可迭代对象,因为它有 __iter__() 方法。

class MyIterator:   
    # 使用关键字 class 来定义一个名为MyIterator的类
​
    def __init__(self):  
    # 定义初始化函数 __init__,在创建类的实例(对象)时,会自动调用这个方法。
​
        self.num_list = [1, 2, 3] 
        # 在对象被创建时,会在对象的命名空间中创建一个名为 num_list 的变量并赋值为 [1, 2, 3]
​
        self.index = -1
        # 在对象被创建时,会在对象的命名空间中创建一个名为 index 的变量并赋值为 -1
​
    def __iter__(self):       
    # 定义 __iter__ 方法,该方法在对实例进行迭代时被调用,例如在 for loop 中。如果一个类定义了此方法,那么它是一个可迭代对象。
​
        return self
        # 这个方法返回迭代器对象自己,这是迭代器协议的规定。
​
    def __next__(self):       
    # 定义了 __next__() 方法,该方法返回迭代器的下一个元素。如果所有元素都被迭代完毕,则抛出 StopIteration 异常。
​
        self.index += 1 
        # 每调用一次 __next__ 方法,index 就增加 1
​
        if self.index != len(self.num_list): 
        # 判断 index 是否大于 num_list 的长度,如果未超出,则返回 num_list 中相应位置上的元素。
​
            return self.num_list[self.index] 
            # 如果 index 小于 num_list 的长度,返回对应位置的元素
​
        raise StopIteration 
        # 如果 index 等于 num_list 的长度,那么就抛出 StopIteration 异常,表示迭代完毕,停止迭代。
​
my_iterator = MyIterator() 
# 创建 MyIterator 类的实例,并赋值给变量 my_iterator
​
for i in my_iterator: 
# 通过 for 循环来遍历 my_iterator 实例对象,这会自动调用 my_iterator 的 __iter__ 方法,并不断调用 __next__() 方法获取元素,
​
    print(i)

区别

  • 可迭代对象主要用于表示一系列元素,具有迭代能力,但是不具备生成下一个元素的能力。

  • 迭代器对象不仅具备迭代能力,还具备持续生成下一个元素的能力,直到耗尽所有元素。

  • 迭代器可以进行惰性计算,它记录当前的状态,仅在需要的时候通过 next() 函数获取下一个值,这对于处理大规模或者无限的序列特别有用。可迭代对象则无法进行此类操作,要创建和存储整个序列。

  • 迭代器只能被遍历一次,遍历完毕后,如果需要重新遍历,就需要再次使用 iter() 函数生成新的迭代器。可迭代对象则可以被多次遍历。

4.迭代器的优缺点:

在Python中使用迭代器有许多优点,但也存在一些缺点,具体如下:

优点:

  1. 内存效率:迭代器并不需要一次性加载所有数据到内存中,这意味着无论数据有多大,都可以迭代,因此对内存的使用非常有效率。

  2. 代码简洁:迭代器提供了一种干净简洁的编程语法,使得循环更简单,代码更易读。

  3. 通用性:迭代器可以用于任何可迭代的对象,比如列表、元组、字符串、字典、文件等,这提供了极大的便利性。

  4. 延迟计算(惰性评估):迭代器在需要时才计算下一个值,延迟了所有计算,可以提高性能和响应性。

缺点:

  1. 无法重用:迭代器不能重复使用。一旦元素被迭代读取,如果不重新创建迭代器,是不能够再次访问这些元素的。

  2. 保存状态:迭代器必须自己保存状态,因此可能会写出比普通代码更多、更复杂的代码。

  3. 只能单向前进:迭代器只能向前移动,不能向后移动,也不能跳跃读取,这有时对使用带来不便。

  4. 无法获取元素数量:通过迭代器我们无法事先知道数据的大小,除非把所有数据都迭代一遍。

总的来说,迭代器很好地平衡了内存使用、代码简洁性和处理大数据的能力。在考虑是否使用迭代器时,需要权衡这些优缺点。

5.生成器(generator):

1.生成器和迭代器的关系:生成器是一种特殊的迭代器。也就是说,任何生成器都是迭代器,它们都支持next()操作。

2.生成器的特点:

  1. 生成器通过yield关键字来返回值。当生成器执行到yield时,会暂停并保存当前所有的运行信息,以便下次调用next()方法时,从上一次暂停的地方继续运行。

  2. 使用生成器可以节省内存,特别适合用于大数据的处理,只需用到的数据才会被存储和处理。

3.yield工作原理:

  1. 当你首次调用一个包含 yield 语句的函数时,函数会运行,直到遇到 yield 语句为止。此时,函数值(即,yield 语句后面的表达式的值)会被返回,同时,函数的内部状态会被保存,包括当前执行到的位置以及所有的局部变量的值。

  2. 当你再次调用这个生成器函数(通常是通过 next() 函数或 for...in 循环),函数会从保存的状态恢复,然后从上次 yield 语句之后的下一条语句开始执行,直到遇到下一个 yield 语句。

4. yield 和 return 区别

yield可以用于返回值,但不同于return。函数一旦遇到return就结束了,销毁上下文(弹出栈帧),将控制权返回给调用者。而yield可以保存函数的运行状态,挂起函数,用来返回多次值。因此,以 yield 进行执行流控制的函数称为生成器函数,以 return 进行执行流控制的函数就是普通函数。由于可以临时挂起函数的执行,yield 可以充当其调用者和被挂起函数间交互的桥梁。

5.生成器使用示例:

def my_generator(data):
    for item in data:
        yield item
​
gen = my_generator([1, 2, 3, 4, 5])
​
for item in gen:
    print(item)
# 输出:
1
2
3
4
5

6.send()和close():

它们主要与生成器和yield表达式一起使用

send()方法:send()方法是生成器的一个非常重要的功能,它可以在生成器暂停的时候向其发送一个值,然后将此值作为暂停时yield表达式的结果,这样就可以实现生成器中的代码与外部代码之间的数据交互。

具体来说,当你开始迭代一个生成器时,首次应当使用next()函数,之后可以使用send()方法(除非用send(None))。在调用send()方法后,生成器会从上次暂停的地方(yield表达式)继续执行,并使用send()方法传入的值作为yield表达式的值。

以下是代码示例:

def my_generator():
    value_from_send = yield "hello"
    yield value_from_send
​
gen = my_generator()
​
print(next(gen))  # 输出: 'hello'
print(gen.send('world'))  # 输出: 'world'

close()方法:

close()方法用于停止生成器。调用generator.close()后,生成器内部抛出一个GeneratorExit异常。在生成器内部,可以捕获这个异常用来做清理工作。如果GeneratorExit异常在生成器函数环境内(除了忽略以外)没有被处理,生成器会在下一次next()send()调用时抛出StopIteration异常。

def my_generator():
    try:
        while True:
            yield 'Hello world!'
    except GeneratorExit:
        print('Generator has been closed!')
​
​
gen = my_generator()
next(gen)  # 输出: 'Hello world!'
gen.close()  # 输出: 'Generator has been closed!'

在某些情况下,你可能会想结构化生成工作,让生成器在完成后自动关闭。这可以通过使用with语句结合contextlib.closing()函数来实现。例如:

import urllib.request
from contextlib import closing
​
with closing(urllib.request.urlopen('http://www.python.org')) as page:
    for line in page:
        print(line)

7.生成器优点:

  1. 内存效率:生成器并不需要一次性加载所有数据到内存中,它会在每次迭代时生成下一个值。这意味着无论数据有多大,都可以迭代,因此对内存的使用非常有效率。

  2. 节省计算量:只有在需要时才会生成下一个值。如果你只需要前几个值,那么生成器会节省大量不必要的计算。

  3. 代码简洁:生成器提供了一种干净简洁的方式来编写迭代器。不需要再手动实现__iter____next__等方法,简化了代码的编写。

  4. 无需了解迭代内部逻辑:使用生成器时,只需了解如何获取下一个值,而无需了解迭代的内部实现逻辑,使代码更易维护和理解。

  5. 延迟操作:生成器推迟了计算过程,只有在需要下一个元素的时候才计算。这在处理大数据量或无限序列的时候特别有用。

6.总结:

可迭代对象(Iterable):

  • 可迭代对象是任何可以作为一个序列进行迭代的对象。在Python中,字符串、列表、元组、集合、字典都是可迭代对象。

  • 这些对象实现了 __iter__() 方法,该方法将返回一个迭代器。

  • 可以使用 for 循环直接遍历可迭代对象。

迭代器(Iterator):

  • 迭代器是一个可以记住迭代的位置的对象。

  • 它实现了 __next__() 方法,可以返回序列的下一项,如果没有更多的项,将抛出 StopIteration 异常。

  • 迭代器实现了 __iter__() 方法,使得迭代器也是可迭代的。

生成器(Generator):

  • 生成器是一种特殊的迭代器,可以使用简单的方式创建。

  • 生成器使用 yield 表达式,每次 yield 时,函数会暂停并保存当前的状态,然后在下次迭代时从保存的状态恢复。

  • 生成器是懒加载的,每次只生成一个值,只有在请求下一个值时才继续执行,这样可以节省内存。

总结来说,可迭代对象(Iterable)可以被迭代(即可以通过 for 循环获取每个元素),迭代器(Iterator)可以记住当前迭代到哪个元素,并可以获取下一个元素,生成器(Generator)是一种特殊的迭代器,它是懒加载的,即只有在需要获取下一个元素时才会计算。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值