浅析python迭代器和生成器

可迭代对象

概念

一个对象(在Python里面一切都是对象)只要实现了只要实现了__iter__()方法,那么这个对象就是可迭代对象

常见的可迭代对象

  1. 集合或序列类型(如list、tuple、set、dict、str)或文件对象
  2. 在类中定义了__iter__()方法的对象,可以被认为是 Iterable对象,但自定义的可迭代对象要能在for循环中正确使用,就需要保证__iter__()实现必须是正确的
  3. 在类中实现了如果只实现__getitem__()的对象可以通过iter()函数转化成迭代器但其本身不是可迭代对象。所以当一个对象能够在for循环中运行,但不一定是Iterable对象。

    针对第一点可以使用print(isinstance([], Iterable)) 进行校验,返回True则是可迭代对象。

    关于第二点我们可以通过print(hasattr([], "__iter__"))返回True知道内部实现了__iter__方法,但是这里我们需要注意的是要想被for循环遍历,能够被内置的iter()函数调用并转化成Iterator对象(如下代码所示)

from collections import Iterable
class IterObj:
    
    def __iter__(self):
        return self 

print(isinstance([], Iterable))         
it = IterObj()
print(iter(it))

关于 第四点可以根据如下实例理解

from collections import Iterable, Iterator, Generator


class IterObj:

    def __init__(self):
        self.a = [3, 5, 7, 11, 13, 17, 19]

    def __getitem__(self, i):
        return self.a[i]


it = IterObj()
print(isinstance(it, Iterable))  # false

print(hasattr(it, "__iter__"))  # false
print(isinstance(iter(it), Iterable))  # true

for i in it:
    print(i)  # 将打印出3、5、7、11、13、17、19

迭代器

概念

迭代器对象要求支持迭代器协议的对象,在Python中,支持迭代器协议就是实现对象的__iter__()和next()方法。其中__iter__()方法返回迭代器对象本身;next()方法返回容器的下一个元素,在结尾时引发StopIteration异常。简单来说:一个对象实现了__iter__()和__next__()方法,那么它就是一个迭代器对象。

实例

class IterObj:

    def __init__(self, n):
        self.a = n

        self.n = len(self.a)
        self.i = 0

    def __iter__(self):
        return iter(self.a)

    def __next__(self):
        while self.i < self.n:
            v = self.a[self.i]
            self.i += 1
            return v
        else:
            self.i = 0
            raise StopIteration()
t = IterObj([3, 5, 7, 11, 13, 17, 19])
# 可以通过for遍历
print([ i for i in t])

# 也可以通过next(因为遍历到没有数据时候,抛出异常所以需要捕捉下)
while True:
    try:
        print(t.__next__())
    except:
        break

区分iterable,iterator与itertion

  • itertion: 就是迭代,一个接一个(one after another),是一个通用的概念,比如一个循环遍历某个数组。

  • iterable: 这个是可迭代对象,属于python的名词,范围也很广,可重复迭代,满足如下其中之一的都是iterable:

    • 可以for循环: for i in iterable

    • 可以按index索引的对象,也就是定义了__getitem__方法,比如list,str;

    • 定义了__iter__方法。可以随意返回。

    • 可以调用iter(obj)的对象,并且返回一个iterator

  • iterator迭代器对象,也属于python的名词,只能迭代一次。需要满足如下的迭代器协议

    • 定义了__iter__方法,但是必须返回自身

    • 定义了next方法,在python3.x是__next__用来返回下一个值,并且当没有数据了,抛出StopIteration

    • 可以保持当前的状态

优缺点

迭代器优点:

  1. 提供了一种通用不依赖索引的迭代取值方式
  2. 同一时刻在内存中只存在一个值,更节省内存

迭代器缺点:

  1. 取值不如按照索引的方式灵活,不能取指定的某一个值,只能往后取,不能往前去
  2. 无法预测迭代器的长度

生成器

概念

生成器就是一种自定义的迭代器,本质为迭代器,但凡函数内包含yield关键字,调用函数不会执行函数体代码,会得到一个返回值,该返回值就是生成器对象。在廖雪峰教程中解释为:在Python中,这种一边循环一边计算的机制,称为生成器(Generator)

注意: 生成器既是可迭代的也是迭代器

定义生成器有两种方式:

  1. 列表生成器
  2. 使用yield定义生成器函数  (yield是一个语法糖,内部实现了迭代器协议,同时保持状态可以挂起)

列表生成器

from collections import *

g = (x  for x in range(3)) # 0~18的偶数生成器
print(isinstance(g, Iterable)) # true
print(isinstance(g, Iterator)) # true
print(isinstance(g, Generator)) # true
print(hasattr(g, "__iter__")) # true
print(hasattr(g, "__next__")) # true
print(next(g)) # 0
print(next(g)) # 1
print(next(g)) # 2

使用yield定义生成器函数

def gen():
    for i in range(10):
        yield i

# 检索数据,开始执行
a = gen()
print(list(a))
    
# 调用gen()并没有真实执行函数,而是只是返回了一个生成器对象
# 执行第一次a.next()时,才真正执行函数,执行到yield一个返回值,然后就会挂起,
# 保持当前的名字空间等状态。然后等待下一次的调用,从yield的下一行继续执行。
a = gen()
print(a.__next__())

这里yield的作用就相当于return,这个函数就是顺序地返回[0,9]的之间的自然数,可以通过next()或使用for循环来遍历。当程序遇到yield关键字时,这个生成器函数就返回了,直到再次执行了next()函数,它就会从上次函数返回的执行点继续执行,即yield退出时保存了函数执行的位置、变量等信息,再次执行时,就从这个yield退出的地方继续往下执行。

迭代器和生成器的区别

生成器能做到迭代器能做的所有事,而且因为自动创建了 iter()和 next()方法,生成器显得特别简洁,而且生成器也是高效的,使用生成器表达式取代列表解析可以同时节省内存。
 

 

 

参考链接

python的迭代器为什么一定要实现__iter__方法?

Python:range 对象并不是迭代器(英文)

Python:range 对象并不是迭代器

python迭代器与生成器小结

python迭代器和for循环区别

Python迭代器,生成器--精华中的精华

你可能感兴趣的:(python,python)