本文介绍了 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