Python中可迭代对象、迭代器和生成器相关

来源:互联网 发布:java程序员培训学校 编辑:程序博客网 时间:2024/06/14 18:29

Blog地址:https://www.jiangdog.com/blog/iterator-and-generator

可迭代对象 Iterable

  1. 可迭代对象的定义
    • 如果一个对象实现了__iter__方法,且使用iter()内置函数可以获取迭代器对象,那么该对象就是可迭代对象。
    • 除上述之外,序列都是可迭代对象。且若一个对象实现了__getitem__方法,且其参数是从0开始的索引,这种对象也是可迭代对象。
  2. iter() 函数作用
    • 尝试调用该对象的__iter__方法获取一个迭代器。
    • 若没有实现__iter__方法,却实现了__getitem__方法,Python创造一个迭代器,并有序的(从0开始)获取元素。
    • 如果尝试失败,抛出TypeError异常。
  3. 判断是否是可迭代对象

    • 如果该对象实现了__iter__方法,则其一定是可迭代对象

      class Foo:    def __iter__(self):        passprint(issubclass(Foo, abc.Iterable))  # Trueprint(isinstance(Foo(), abc.Iterable))  # True
    • 任何Python的序列都是可迭代的,因为他们都实现了__getitem__方法,事实上Python中的标准序列也都实现了__iter__方法,对__getitem__做特殊处理是为了向后兼容,自定义序列时最好的做法是两者都实现。

    • 对于只实现了__getitem__方法的可迭代对象,使用isinstance()来判断并不准确,因为iter()函数考虑到了遗留的__getitem__问题,而abc.Iterable并没有。

      class MyList:    def __init__(self):        self.seq = [1, 2, 3]    def __getitem__(self, index):        return self.seq[index]    def __len(self)__:        return len(self.seq)    print(isinstance(MyList(), abc.Iterable))  # False
    • 最好的方法是在尝试迭代不可迭代对象时,会抛出TypeError异常,此时可以try/except捕获异常后再做进一步处理。

      class MyObject:    def __init__(self):        passtry:    iter(MyObject())except TypeError as e:    print(e)  # 'MyObject' object is not iterable
  4. iter() 函数的另一种用法

    • 内置iter() 函数

      def iter(source, sentinel=None): # known special case of iter  """  iter(iterable) -> iterator  iter(callable, sentinel) -> iterator  Get an iterator from an object.  In the first form, the argument must  supply its own iterator, or be a sequence.  In the second form, the callable is called until it returns the sentinel.  """  pass

      第二种用法种,接收一个可调用对象和终止哨符,此时返回一个迭代器,该迭代器会不停的产出传入的可调用对象返回的值,直到出现终止哨符,则耗尽,抛出StopIteration异常,且不产出终止哨符。

      def random_for_one(): return randint(1, 6)for_one_iterator = iter(random_for_one, 1)print(for_one_iterator)  # <callable_iterator object at 0x003F4550>for v in for_one_iterator:  print(v)  # 3 4 5 4 5

迭代器 Iterator

  1. 迭代器含义和特点

    • 标准的迭代器,需要实现__iter____next__两个方法。
    • __iter__返回迭代器本身,以便应该在使用可迭代对象的地方使用迭代器,如for循环。
    • __next__返回迭代器中的下一个有效元素,直到没有元素抛出StopIteration异常。
    • 利用isinstance(x, abc.Iterator)来判断对象x是否是迭代器。
    • 迭代器迭代到头耗尽,无法还原,需要重新构建迭代器。
    • 迭代器

    迭代器是这样的对象:实现了无参数的 __next__方法,返回序列
    中的下一个元素;如果没有元素了,那么抛出 StopIteration 异常。
    Python 中的迭代器还实现了 __iter__ 方法,因此迭代器也可以迭代。

  2. 迭代器和可迭代对象之间的关系

    • Python从可迭代对象获取迭代器。
    • 迭代器也是可迭代的,因为其实现了__iter__方法,且返回迭代器自身。
    • 可迭代对象一定不能实现__next__方法。
    • 为了支持多种遍历,如for,拆包等,必须一个可迭代实例中获取多个独立迭代器,且迭代器能够维护自身的状态,正确的做法是对可迭代对象上调用__iter()方法,每次都构建一个独立的新的迭代器;而不是在可迭代对象中实现__next__方法,使其是自身的迭代器,若这样做,其在一次迭代耗尽后将无法构建新的迭代器。

       # no __next__ in Iterable class ErrorSentence:     def __init__(self, text):         self.text = text         self.words = RE_WORD.findall(text)         self.index = 0     def __iter__(self):         return self     def __next__(self):         try:             word = self.words[self.index]         except IndexError:             raise StopIteration         self.index += 1         return worderror_sentence = ErrorSentence('x y z')error_iterator = iter(error_sentence)print(error_iterator)  # <__main__.ErrorSentence object at 0x000001C38F24CE10> print(isinstance(error_sentence, abc.Iterator))  # Truefor v in error_sentence:    print(v)  # x y z# 可迭代对象是自身的迭代器# 迭代器已耗尽,不能再次迭代for v in error_sentence:    print(v)
  3. for循环的实现
    • 判断对象是否是可迭代对象,如果不是则直接抛出TypeError
    • 如果该对象是可迭代对象,则通过iter(iterable)方法,调用其__iter__方法返回一个迭代器;若该对象未实现__iter__接口,但存在__getitem__方法,Python会创造一个迭代器。
    • 不断调用next(iterator),执行迭代器的__next__方法,使其按序的返回下一个可用的值。
    • 如果迭代到头,没有元素了,迭代器耗尽抛出StopIteration异常,Python语言内部将会处理这个异常(除了for之外,其他迭代上下文,如列表推导式、元组拆包等也会处理该异常)。
  4. 定义一个简单的迭代器

    • 由上述可知一个标准的迭代器需要实现__iter____next__方法,展示相关示例。

      RE_WORD = re.compile('\w+')class Sentence:    def __init__(self, text):        self.text = text        self.words = RE_WORD.find('text')    def __iter__(self):        return SentenceIterator(self.words) class SentenceIterator:    def __init__(self, words):        self.words = words        self.index = 0    def __iter__(self):        return self    def __next__(self):        try:            word = self.words[self.index]        except IndexError:            raise StopIteration        self.index += 1        return wordabc_sentence = Sentence('a b c')sentence_iterator = iter(abc_sentence)print(sentence_iterator)  # <__main__.SentenceIterator object at 0x0000025F31C5B9B0>print(isinstance(sentence_iterator, abc.Iterator))  # Truefor v in abc_sentence:    print(v)  # a b c
    • 使用生成器函数代替SentenceIterator迭代器类:

       RE_WORD = re.compile('\w+') class Sentence:     def __init__(self, text):         self.text = text         self.words = RE_WORD.findall(text)     def __iter__(self):         for word in self.words:             yield word sentence_iterator = iter(Sentence('a b c'))   print(sentence_iterator)  # <generator object Sentence.__iter__ at 0x00000249900C8308>

      此时这里调用iter()所获得的迭代器实际上是一个实现了迭代器接口的生成器对象。


生成器 Generator

  1. 生成器含义和特点
    • 只要Python函数的定义体中出现了yield关键字,则该函数就是生成器函数。调用该生成器函数返回一个生成器对象。
    • 把生成器传给next()函数时,生成器函数会向前,执行生成器函数中下一个yield语句返回产出值并在当前位置暂停。
    • 当生成器函数返回时,生成器对象抛出StopIteration异常。
  2. 生成器函数和生成器表达式

    • 普通函数和生成器函数唯一的区别就是,生成器函数在定义体中存在yield关键字。

       # 生成器函数 def gen_123():     yield 1     yield 2     yield 3 print(gen_123)  # <function gen_123 at 0x00000137C8FD6158> 函数对象 print(gen_123())  # <generator object gen_123 at 0x000002366A689360>  生成器对象for v in gen_123():    print(v)  # 1 2 3g = gen_123()while True:    # 调用next(g)来不断获取生成器的下一个值    try:        print(next(g))    except StopIteration:        # 生成器函数的定义体执行完后抛出异常后终止        break
    • 生成器表达式

      生成器表达式可以理解为列表推导的惰性版本:不会迫切地构建列表,而是返回一个生成器,按需惰性生成元素。也就是说,如果列表推导是制造列表的工厂,那么生成器表达式就是制造生成器的工厂。

      # 生成器表达式gen_ABC = (s for s in 'ABC')print(gen_ABC)  # <generator object <genexpr> at 0x000001F4038E9410>

      此时通过该生成器表达式构建了一个生成器对象。

  3. 标准库中的生成器函数

    • itertoolsfunctools以及内置方法中有很多生成器函数。
    • 最常见的是filtermapenumerateallanyreversed等等。
    • itertools中提供了很多过滤相关、映射相关(itertools.accumulate(it, [func]))、合并多个迭代对象相关(itertools.product()求笛卡儿积、itertools.zip_longest())、输入的各个元素扩展成多个输出元素的生成器函数(itertools.combinations(it, out_len)组合, itertools.combinations_with_replacement(it,
      out_len)
      包含相同元素的组合)。
  4. 生成器对象的其他方法和功能

    • generator.__next__()只允许客户从生成器中获取数据,generator.send(value)允许客户代码和生成器之间双向交换数据。

       # generator.send(value) def gen():     print('start')     value = 0     while True:         receive = yield value         print(receive)         if receive == 'end':             break         value += 1 g = gen() # 或next(g) 预激生成器 print(g.send(None))  # 0 print(g.send('a'))  # 1 print(g.send('b'))  # 2 try:     print(g.send('end')) except StopIteration:     print('end')
    • generator.close()能够关闭生成器,关闭生成器后继续调用next()方法后将会抛出异常。

       # generator.close() def gen():     yield 1     yield 2     yield 3 g = gen() print(next(g)) print(next(g)) g.close() try:     print(next(g)) except StopIteration:     pass

两者的比较

  1. 接口:

    • Python迭代器协议定义了两个方法:__iter____next__
    • 生成器对象实现了这两个方法,因此,所有生成器都是迭代器。

       g = (i for i in 'abc') print(g.__iter__)  # <method-wrapper '__iter__' of generator object at 0x02ACAF00> print(g.__next__)  # <method-wrapper '__next__' of generator object at 0x02ACAF00>
  2. 实现方式:
    • 生成器通过含有yield关键字的函数或生成器表达式来编写,这两种方式返回的生成器对象属于types.GeneratorType类型,且types.GeneratorType类型的实例实现了迭代器接口,则所有的生成器都是迭代器。
    • 但我们可以编写不是生成器的迭代器对象,如简单实现__iter____next__方法,或者用C语言编写拓展。
    • The type of generator-iterator objects, created by generator functions.
  3. 其他:
    • Iterator Types
    • Generator Types

参考

  1. 大部分总结参考自《Fluent Python》第五部分第14章节

  2. 生成器(generator)概念


其他

  1. 文中代码
原创粉丝点击