1. 可迭代对象Iterable:

如果对象实现了__iter__方法或实现了__getitem__方法,且其参数是从零开始索引的,则称为可迭代对象(Iterable)

凡是可作用于for循环的对象都是可迭代对象。

python内置的数据类型,像listtupledictsetstr等都是可迭代对象。

 

可迭代对象不是迭代器!!!python从可迭代对象中获取迭代器。

Iterable.__iter__方法应该返回一个Iterator 实例。

 

判断是否可迭代对象的方法:

最准确方法是使用iter函数,但要注意假如不是可迭代对象,Python 会抛出TypeError异常。要做好异常处理。

不建议使用isinstance(x, Iterable)的方法。因为该函数判断时没考虑该类只有__getitem__的时候,所以当自建类时没有自定义__iter__方法,即使可迭代,仍会判断为False

 

内置的iter函数有以下作用: (1) 检查对象是否实现了__iter__方法,如果实现了就调用它,获取一个迭代器。

(2) 如果没有实现__iter__方法,但是实现了__getitem__方法,Python 会创建一个迭代器,尝试按顺序(从索引 0 开始)获取元素。 (3) 如果尝试失败,Python 抛出TypeError异常,通常会提示XXX object is not iterable

  • 每次调用 iter(iterable object) ,成功的话都新建一个独立的迭代器。

 

2. 迭代器Iterator:

表示数据流的对象。在流中,能够被next()函数调用并不断返回下一个元素,直到没有元素抛出StopIteration异常。所以迭代器只能往前不能后退。

由于迭代器一定会有__iter__方法,所以它自身也是一个可迭代对象。

2.1 标准的迭代器含有两个方法:

__next__:返回下一个可用的元素,如果没元素则抛出StopIteration异常。

__iter__:返回self,以便在应该使用可迭代对象的地方使用迭代器。

 

2.2 判断是否迭代器的方法:

from collections.abc import Iterator

isinstance(x, Iterator)

 

2.3 可迭代对象生成迭代器:

iter(Iterable object)

 

2.4 迭代器遍历的方法

#使用for语句进行遍历。
迭代时,for 机制的是获取生成器对象,然后每次迭代时隐式调用 next(...)。


#也可以使用next(iterator[, default])函数,该对象需要为迭代器
it = iter(object)       #把可迭代对象创建一个迭代器
next(it)

 

3. 生成器Generator:

只要python函数体中有yield关键字,该函数就是生成器函数。调用生成器函数时,会返回一个生成器对象。也就是说,生成器函数是生成器工厂。

>>> def gen():          #生成器函数
...     yield 1
...

>>> gen
<function gen at 0x000002193DCCC160>

>>> gen()           #生成器对象
<generator object gen at 0x000002193DD23F90>

把生成器传给next()函数时,生成器函数会向前,执行函数定义体中的下一个 yield 语句,返回产出值,并在函数定义体的当前位置暂停。最终,函数的定义体返回时,外层的生成器对象会抛出StopIteration异常。与迭代器协议一致。

所以,生成器是迭代器!

 

生成器的本质:函数定义体体现算法,不会一次性构建出结果,按需惰性地生成元素,从而节省了大量内存。调用者在函数体外使用next()函数跳转到生成器函数内继续执行函数,遇到yield关键字则携带其元素值跳回到调用者函数,直到遇到下一个next()函数跳回到生成器上次跳出的地方继续执行,如此重复。

 

3.1 创建生成器的方法:

  1. 函数体中含有yield关键字

  2. 利用列表生成式。

    把列表生成式的[]改为(),即创建了生成器

 

3.2 生成器的方法:

PEP342中新增了2,3,4方法。

  1. __next()__:使生成器前进到下一个yield 语句,然后从生成器中获取数据。
  2. send():除了拥有和__next()__方法一样的功能外,还允许将参数传给生成器。即不管传什么参数,该参数都会成为生成器函数定义体中对应的 yield 表达式的值。也就是说,send() 方法允许在客户代码和生成器之间双向交换数据。这改变了生成器的性质,变成了==协程==。
    • 虽然在协程中会使用 yield 产出值,但只是借用了该关键字的性质,与迭代已完全无关!!!
  3. throw():让调用方抛出异常
  4. close():终止生成器

 

4. 自定义可迭代对象类型简化的演化例子:

  1. 典型的Sentence类

    import re
    import reprlib
    
    RE_WORD = re.compile('\w+')
    
    class Sentence:
       def __init__(self, text):
           self.text = text
           self.words = RE_WORD.findall(text)
       def __repr__(self):
           return 'Sentence(%s)' % reprlib.repr(self.text)
       def __iter__(self):
           return SentenceIterator(self.words)
    
    class SentenceIterator:
       def __init__(self, words):
           self.words = words 
           self.index = 0
       def __next__(self):
           try:
               word = self.words[self.index]
           except IndexError:
               raise StopIteration()
           self.index += 1
           return word
       def __iter__(self):
           return self
    
  2. 优化迭代器类,用生成器函数代替

    class Sentence:
       def __init__(self, text):
           self.text = text
           self.words = RE_WORD.findall(text)
       def __repr__(self):
           return 'Sentence(%s)' % reprlib.repr(self.text)
    
       def __iter__(self):
           for word in self.words:
               yield word
           return
    
  3. 将正则匹配改为惰性

    class Sentence:
       def __init__(self, text):
           self.text = text
    
       def __repr__(self):
           return 'Sentence(%s)' % reprlib.repr(self.text)
    
       def __iter__(self):
           for word in RE_WORD.finditer(self.text):
               yield word.group()
    
  4. 将生成器函数变成生成器表达式

    class Sentence:
       def __init__(self, text):
           self.text = text
    
       def __repr__(self):
           return 'Sentence(%s)' % reprlib.repr(self.text)
    
       def __iter__(self):
           return (word.group() for word in RE_WORD.finditer(self.text))
    

There are 0 comments