Python 迭代器与生成器详解!

什么是迭代

迭代是可以通过遍历的方式依次把某个对象中的元素取出的方法,在python中,迭代是通过使用for….in….语句完成的

可迭代对象

可以被直接作用于for语句的对象都可以被称为可迭代对象(iterable)。而可以直接作用于for语句的数据类型有以下两种:

  1. 集合数据类型:str,list,tuple,dict,set…..
  2. 生成器(generator),包括生成器和带yield的生成器函数

对于某个数据是否属于某种数据类型,可以用isinstance函数来判断

from collections import Iterable

print(isinstance(123, Iterable))  # int类型,输出:False
print(isinstance("123", Iterable))  # str类型,输出:True
print(isinstance([1, 2, 3], Iterable))  # list类型,输出:True
print(isinstance((1, 2, 3), Iterable))  # tuple类型,输出:True
print(isinstance({"a": 1, "b": 2, "c": 3}, Iterable))  # dict类型,输出:True
print(isinstance({1, 2, 3}, Iterable))  # set类型,输出:True

迭代器(iterator)

在python中,可以被next函数调用并不断返回下一个值的对象被称为迭代器(iterator),我们也可以通过isinstance函数对其进行判断

from collections import Iterator

print(isinstance("123", Iterator))  # str类型,输出:False
print(isinstance([1, 2, 3], Iterator))  # list类型,输出:False
print(isinstance((1, 2, 3), Iterator))  # tuple类型,输出:False
print(isinstance({"a": 1, "b": 2, "c": 3}, Iterator))  # dict类型,输出:False
print(isinstance({1, 2, 3}, Iterator))  # set类型,输出:False

从上面可以看出,可迭代对象可以不是迭代器,如果我们想把他们转换为迭代器,可以使用iter函数

from collections import Iterator

print(isinstance(iter("123"), Iterator))  # str类型,输出:True
print(isinstance(iter([1, 2, 3]), Iterator))  # list类型,输出:True
print(isinstance(iter((1, 2, 3)), Iterator))  # tuple类型,输出:True
print(isinstance(iter({"a": 1, "b": 2, "c": 3}), Iterator))  # dict类型,输出:True
print(isinstance(iter({1, 2, 3}), Iterator))  # set类型,输出:True

从上面可以看出,是可迭代对象的,不一定是迭代器,是迭代器的,一定是可迭代对象

from collections import Iterator
print(isinstance(iter(123), Iterator))  #  TypeError: 'int' object is not iterable

如果是非可迭代对象之间转换为迭代器,可能会报错

迭代器中有两个基本方法:iter(),next()。使用iter函数创建一个迭代器后,就可以通过next函数获取迭代器的下一个值,如果通过next()不断调用并返回下一个值,那么等到最后没有下一个值了,就会抛出异常:StopIteration

a = [1, 2, 3]
iter_a= iter(a)  # 创建迭代器对象

print(next(iter_a))  # 输出:1
print(next(iter_a))  # 输出:2
print(next(iter_a))  # 输出:3
print(next(iter_a))  # iter_a没有下一个值了,报错:StopIteration

在python中,for语句本质就是不断调度next函数实现的

a = [1, 2, 3]
for i in a:
  print(i)

iter_a = iter(a)
while True:
  try:
    print(next(iter_a))
  except StopIteration:
    break

如同这样使用,两者之间其实没有区别

for语句可以循环可迭代对象,迭代器和生成器,所以这里我们可以用for语句循环类对象。但是我们需要在类中写两个魔法方法,__iter__和__next__,如果没有写,则会报错

class a:
  def __init__(self):
    pass
b = a()
for i in b:  # TypeError: 'a' object is not iterable
  print(b)

写了则是这样的

class a:
  def __init__(self):
    pass
  
  def __iter__(self):
    return self    # 此次返回当前实例,作为迭代器

  def __next__(self):
    pass
  
b = a()
for i in b:
  print(b)   # <__main__.a object at 0x000002958BBD1FD0>

这个时候,我们或许可以用其来实现一个小玩意儿:斐波那契数列

class a:
  def __init__(self, n):
    self.n = n
    self.a = 0
    self.b = 1
  
  def __iter__(self):
    return self

  def __next__(self):
    if self.n > 0:
      self.n -= 1
      self.a, self.b = self.b, self.a + self.b
      return self.a
    else:
      raise StopIteration
  

b = a(11)
for i in b:
  print(i)  # 输出:1 1 2 3 5 8 13 21 34 55 89 

生成器(generator)

在python中,生成器(generator)也是用在迭代操作中,其本质上可以理解为一个特殊的迭代器,生成器具有和迭代器一样的特性。但是它们在实现方式上不一样。我们可以通过两种方式创建生成器:生成器表达式,生成器函数。

  1. 生成器表达式
    生成器表达式和列表推导式差不多,我们只需要包列表推导式的[]改为(),这样就是一个生成器表达式了。需要注意的是,列表推导式返回的是一个列表对象,而生成器表达式返回的是一个生成器对象,因此我们可以通过生成器表达式来创建一个生成器。
a = [i for i in range(5)]
print(type(a))  # <class 'list'>
print(a)  # [0, 1, 2, 3, 4]
print(next(a)) # TypeError: 'list' object is not an iterator

b = (i for i in range(5))
print(type(b))  # <class 'generator'>
print(b)  # <generator object <genexpr> at 0x0000021781CCEE40>
print(next(b)) # 0
  1. 生成器函数
    在Python中,普通函数一般通过 return 来返回一个值,当我们使用关键字 yield 来返回值,那么这个带有 yield 的函数就变成了生成器函数
def demo1(n):
  return n

def demo2(n):
  yield n

a = demo1(100)
print(type(a))  # <class 'int'>

print(next(a))  # TypeError: 'int' object is not an iterator

b = demo2(100)
print(type(b))  # <class 'generator'>
print(next(b))  # 100 
print(next(b))  # StopIteration

生成器函数和普通函数在执行流程上是有点区别的。对于普通函数,按顺序执行时遇到 return 或最后一行函数语句就会返回;对于有 yield的生成器函数,每次调用 next() 方法遇到 yield 语句才返回,如果再次调用 next() 方法,那么就会从上次返回的 yield 语句位置继续执行,请看下面的例子:

def demo3():
    print("aaa")
    yield 1
    print("bbb")
    yield 2
    print("ccc")
    yield 3


g = demo3()
next(g)  # 输出:aaa
next(g)  # 输出:bbb
next(g)  # 输出:ccc
next(g)  # 没有下一个值了,报错:StopIteration

生成器是一个特殊的迭代器,它使用起来更简单,代码更简洁。如果使用其实现斐波那契数列,则是这样的:

def a(n):
  b = 0
  c = 1
  while True:
    if n > 0:
      n -= 1
      b, c = c, b + c
      yield b


for i in a(11):
  print(i) # 输出:1 1 2 3 5 8 13 21 34 55 89

生成器还可以用于处理海量数据的场景,比如读取大文件时,如果直接一次性读取可能会导致内存溢出,这个时候我们就可以借助 yield 生成器来灵活控制读取,防止内存占用过大。

© 版权声明
THE END
喜欢就支持一下吧
点赞15 分享