迭代器
迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束(即可通过for循环遍历),其只能往前不会后退。他有两个基本的方法:iter() 和 next()。
- iter(iterable): 用于对iterable创建迭代器对象
- next(iterable): 可以输出迭代器的下一个元素
字符串,列表或元组对象都可用于创建迭代器
list=[1,2,3,4]
it = iter(list)
print (next(it))
print (next(it))
>>>
1
2
把一个类作为一个迭代器使用需要在类中实现两个方法 __iter__() 与 __next__() 。
- __iter__(self): 返回一个特殊的迭代器对象, 这个迭代器对象实现了 __next__() 方法并通过 StopIteration 异常标识迭代的完成。
- __next__(self):返回下一个迭代器对象。
- StopIteration: 用于标识迭代的完成,防止出现无限循环的情况,在 __next__() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。
class Puke:
def __init__(self):
self.count = 0
self.num = [0, 1, 2, 3, 4, 5, 6, 7, 8]
def __iter__(self):
return self
def __next__(self):
if self.count > 5:
raise StopIteration
else:
self.count += 1
return self.num[self.count-1]
k = Puke()
for x in k:
print(x, end=" ")
>>>
0 1 2 3 4 5
生成器
生成器的本质是一个返回迭代器的函数,只能用于迭代操作
通过列表生成式可以简单直接地创建一个列表,但是受到内存限制,列表容量肯定是有限的。创建一个包含100万个元素的列表,不仅占用很大的存储空间,而且如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那就可以在循环的过程中不断推算出后续的元素,这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器(Generator),生成器可以按需生成并“返回”结果,而不是一次性产生所有的返回值
要创建一个generator,有很多种方法。
方式一:
第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:
num= [x*2 for x in range(10)]
generator1 =(x*2 for x in range(10))
print(num)
print(type(generator1))
print(generator1) #打印生成器,输出的是地址。
"""
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
at 0x00000239FE00A620>
"""
创建mylist和gen的区别仅在于最外层的[]和(),mylist是一个list,而gen是一个generator。
我们可以直接打印出list的每一个元素,如果要打印generator可以通过generator的内置next()方法:
generator1.next()
#0
generator1.next()
#2
generator1.next()
#4
…………
generator保存的是算法,每次调用next(),就计算出下一个元素的值,直到计算到最后一个元素,没有更多的元素时,返回StopIteration的错误。
此外,可以用for循环来代替next()方式
for x in generator1:
print(x,end=' ')
#0 2 4 6 8 10 12 14 16 18
方式二:
如果上述中的推算算法比较复杂,使用方法一无法实现的时候,可以使用函数来实现。比如斐波拉契数列使用列表生成式写不出来,但可以使用函数把它打印出来:
def fib(max):
n = 0
a, b = 0, 1
while n < max:
print(b)
a, b = b, a + b
n = n + 1
fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这与generator的逻辑类似。
只需要把print b改为yield b,即可将fib函数变成generator:
def fib(max):
n = 0
a, b = 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
generator和普通函数的执行流程区别在于:普通函数是顺序执行,遇到return语句或者最后一行函数语句就返回;generator在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。