生成器
通过列表生成式(列表推导式),我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。
而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的 list,从而节省大量的空间。
在Python中,这种一边循环一边计算的机制,称为生成器: generator。
优点: 需要新的数字,就生成一个新的,不同于列表,需要一下子全出来,更占用内存。
通过以下实例代码可看出其实际的时间消耗和内存消耗:
import time
import sys
# 通过 for 循环创建列表
start = time.time()
list1 = []
for x in range(10000000):
list1.append(x)
end = time.time()
l_time = end - start
print(f'通过 for 循环创建列表时间开销:{l_time}')
print(f'通过 for 循环创建列表内存开销:{sys.getsizeof(list1)}')
# 通过列表推导式创建列表
time.clock()
list2 = [x for x in range(10000000)]
l_time = time.clock()
print(f'通过列表推导式创建列表时间开销:{l_time}')
print(f'通过列表推导式创建列表内存开销:{sys.getsizeof(list2)}')
# 创建生成器
time.clock()
g = (x for x in range(10000000))
g_time = time.clock()
print(f'创建生成器时间开销:{g_time}')
print(f'创建生成器内存开销:{sys.getsizeof(g)}')
运行结果:
通过 for 循环创建列表时间开销:1.5724666118621826
通过 for 循环创建列表内存开销:81528056
通过列表推导式创建列表时间开销:0.7601992
通过列表推导式创建列表内存开销:81528056
创建生成器时间开销:0.7602516
创建生成器内存开销:88
得到生成器的方式
- 通过列表推导式的形式得到生成器
g = (x for x in range(0, 1000000, 2))
print(type(g))
# 运行结果:
<class 'generator'>
- 通过定义函数时使用 yield 关键字得到生成器
def create_generator():
for x in range(0, 1000000, 2):
yield x
g = create_generator()
print(type(g))
# 运行结果:
<class 'generator'>
得到生成器中元素的方式
- 通过调用 __next__() 方法得到生成器中的元素
g = (x for x in range(0, 1000000, 2))
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
# 运行结果:
0
2
4
6
- 调用系统内置方法 next(生成器对象)
g = (x for x in range(0, 1000000, 2))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
# 运行结果:
0
2
4
6
- 通过调用 send() 方法获取元素
def create_generator():
for x in range(0, 1000000, 2):
temp = yield x
print(temp)
g = create_generator()
print(next(g))
print(g.send('aaa'))
# 运行结果:
0
aaa
2
注意:首次调用获取生成器元素不传参或者传递非None参数都会导致异常
解决方案:
- 首次调用使用__next__(),不使用 send()
- 首次使用 send(None)
- 使用 for 循环遍历生成器
g = (x for x in range(0, 10, 2))
for i in g:
print(i, end='\t')
# 运行结果:
0 2 4 6 8
迭代器
迭代的概念
- 迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。
- 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。
- 迭代器只能往前不会后退。
- 可以被 next() 函数调用并不断返回下一个值的对象称为迭代器:Iterator。
可迭代对象(能够用于 for 循环的对象)
- 生成器
- 元组、列表、集合、字典、字符串
如何判断一个对象是否是可迭代?
调用系统内置方法:isinstance(对象, 类)进行判断:
from collections import Iterable
def example():
list_ = [1, 4, 3, 6]
g = (x + 1 for x in range(10))
print(isinstance(list_, Iterable))
print(isinstance(g, Iterable))
print(isinstance('abc', Iterable))
print(isinstance(100, Iterable))
if __name__ == '__main__':
example()
# 运行结果:
True
True
True
False
迭代器
- 可迭代的不一定是迭代器。
- 在所有可以用于 for 循环的对象中,迭代器可以被 next() 函数不断调用并生成下一个值,直到抛出 StopIteration 异常表示无法继续为止。
- 像生成器这种可以被 next() 函数调用并不断生成下一个值的对象被称为迭代器(Iterator)。
- 可通过 iter() 函数转换成迭代器:可以使用 for 循环遍历迭代器,也可以使用 next() 函数 或者 __next()__ 方法访问迭代器。
from collections import Iterator
# 创建列表
list_ = [x for x in range(0, 10, 2)]
# 创建生成器
g = (x for x in range(0, 10, 2))
# 判断列表是否为迭代器
print(isinstance(list_, Iterator))
# 判断生成器是否为迭代器
print(isinstance(g, Iterator))
# 列表调用 iter() 转换成迭代器
list_iterable = iter(list_)
print(isinstance(list_iterable, Iterator))
for i in list_iterable:
print(i, end='\t')
# 迭代器无法生成下一个值报 StopIteration 异常
list_iterable.__next__()
运行结果:
Traceback (most recent call last):
File "E:/PythonProject/test3.py", line 24, in <module>
list_iterable.__next__()
StopIteration
False
True
True
0 2 4 6 8