Python-迭代器、生成器、上下文管理器

本文介绍了 Python3 中的三个高级功能,就是强大的:迭代器、生成器和上下文管理器。本文总结了它们的使用方法和使用场景,并展示了简单的例子。之所以把它们放在一块,是因为它们是相互关联的。

迭代器

  • 迭代是访问集合元素的一种方式;
  • 迭代器是一个可以记住遍历的位置的对象;
  • 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束;
  • 迭代器只能往前不会后退。
经典用法
>>> l = [1, 2, 3, 4]
>>> it = iter(l)    # 通过列表对象创建一个迭代器对象
>>> print(next(it)) # 输出迭代器的下一个元素
1
>>> print(next(it)) 
2
...
>>> print(next(it)) # 如果没有后续元素了,next() 完成后引发 StopIteration 异常
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
使用规则
  • 使用 liter() 通过列表、元组等创建迭代器,参考上面的例子;
  • 创建自己的迭代器类:只需要实现__iter__()__next__()两个内置方法
    • __iter__()初始化并返回一个迭代对象
    • __next__()返回下一个元素
  • StopIteration异常用于标识迭代的完成,防止出现无限循环的情况,在__next__() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。
  • 集合数据类型如 list 、 dict 、 str 等是 Iterable 但不是 Iterator ,不过可以通过 iter() 函数获得一个 Iterator 对象。
  • 可以作用于 for 循环的对象称为可迭代对象(Iterable):
    • 集合数据类型据类型: list 、 tuple 、 dict 、 set 、 str 等;
    • generator :生成器、带 yield 的 generator function;
    • 可以通过isinstance([], Iterable)识别
实例代码
class MyNumbers:
    def __iter__(self):
        self.a = 1
        return self
 
    def __next__(self):
        if self.a <= 20:
            x = self.a
            self.a += 1
            return x
        else:
            raise StopIteration
 
mn = MyNumbers()
it = iter(mn)
print(next(it)) # 返回 1
print(next(it)) # 返回 2
for a in it:    # 不受之前的 next(it) 的影响,依旧是从 1 开始迭代,到 20 结束
    print(a)
生成器

  • 在 Python 中,使用了 yield 的函数被称为生成器(generator),跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作。
  • 在 Python 中,构建迭代器有很多开销:必须使用__iter__()__next__()方法实现一个类,跟踪内部状态,当没有值被返回时引发 StopIteration 异常,而生成器是创建迭代器的简单方法。
  • 生成器是表示无限数据流的绝佳媒介,无限流不能存储在内存中,由于生成器一次只能生成一个项目,因此可以表示无限数据流。
经典用法
>>> generator_ex = (x*x for x in range(10))  # 生成器
>>> print(generator_ex)
<generator object <genexpr> at 0x7f5dde98d1a8>
>>> next(generator_ex)                       # 生成器本身就是一个迭代器
0
>>> l = [x*x for x in range(10)]             # 使用生成器创建一个列表
>>> l
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
使用规则
  • 使用生成器表达式:x*x for x in my_list,参见上述例子
  • 使用yield创建生成器函数
    • 生成器函数包含一个或多个yield语句,yield是一个类似return的关键字
    • __iter__()__next__()之类的方法将自动实现,当生成器被调用时,它返回一个迭代器对象
    • 使用.send()next()来激活生成器返回的迭代器,并且它们都能让迭代器继续往下走一步,直到函数执行到yield,该函数将被暂停,并范围当前值
    • 执行过程中局部变量及其状态在连续调用之间被记住
    • .send()能传一个值,这个值可以强行修改上一个yield表达式值
    • 最后,当函数终止时,StopIteration 会在进一步的调用时自动引发
实例代码
def fibonacci(n): 		# 生成器函数,创建长度为 n 的斐波那契数列
    a, b, counter = 0, 1, 0
    while True:
        if (counter > n): 
            return
        yield a
        a, b = b, a + b
        counter += 1

f = fibonacci(10) 		# f 是一个迭代器,由生成器返回生成
 
while True:
    try:
        print (next(f), end=" ")
    except StopIteration:
        print()
        break
上下文管理器

  • Python 中的上下文管理器,也就是支持上下文管理协议的对象;
  • 简单点讲就是,实现了 __enter ____exit__两个方法的类,叫做上下文管理器的类;
  • 上下文管理器是为with语句而生,只要实现了__enter____exit__,就可以使用with语句。
经典用法
# 使用`with`语句打开文件后,不需要显示调用`f.close()`关闭文件了,上下文管理器会自动完成
with open('/tmp/a.txt','a') as f:
    f.write('whatever...')
使用规则
  • 标准库中遵循上下文管理协议的类,可以直接使用,常见用例:资源的加锁与解锁,文件的打开与关闭;
    • threading.Lock服从上下文管理协议:当进 入语句块时acquire()方法会被调用,退出语句块时release()会被调用
  • 如果你有两个需要结对执行的相关操作,然后,还要在他们中间放置一段代码,就可以用上下文管理协议来实现:
    • __enter__通常执行一些初始化操作,并且该函数的返回值会赋值给可选的 as target 中的target 变量。
    • __exit__执行资源清理工作。它接收三个参数,异常类型,异常实例,和异常栈,根据这些异常信息,__exit__可以选择进行相应的异常处理,并默认抛出异常,如果我们在让__exit__返回True,相当于告诉 Python :这些异常我都已经处理了。
实例代码
class Foo(object):
    def __init__(self):
        print('实例化一个对象')
    def __enter__(self):
        print('进入')
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('退出:' + str(exc_type))
        return True

obj = Foo()

with obj:
    raise ImportError
    print('正在执行')
参考文档

https://www.cnblogs.com/python-road/p/10504474.html
https://www.runoob.com/python3/python3-iterator-generator.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值