python基础(11):迭代器和生成器

一、迭代器

1.什么是迭代器?

迭代(iterate):重复多次,就像循环那样。
我们大多只使用for循环迭代过序列字典,实际上也可迭代其他对象:只要这个对象的类实现了方法__iter__。如:

>>> class Fibs:
...       def __init__(self):
...           self.a=0
...           self.b=1
...       def __next__(self):
...           self.a,self.b=self.b,self.a+self.b
...
>>> fibs=Fibs()
>>> for f in fibs:
...       if f>1000:
...           print(f)
...           break
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'Fibs' object is not iterable

上述例子中,作者没有为Fibs这个类定义__iter__这个方法,所以,产生了错误。在添加了__iter__方法后,可以看到:没有任何错误提示了。

>>> class Fibs:
...       def __init__(self):
...           self.a=0
...           self.b=1
...       def __next__(self):
...           self.a,self.b=self.b,self.a+self.b
...           return self.a
...       def __iter__(self):
...           return self  #返回一个迭代器
...
>>> fibs=Fibs()
>>> for f in fibs:
...     if f>1000:
...        print(f)
...        break
...
1597

2.迭代器的方法

迭代器包括 __next__方法:

  1. 若在class中漏写了next方法,for循环获取内部值的时候,会报错 TypeError: iter() returned non-iterator of type 'Fibs'
  2. 调用__next__方法,如果迭代器没有可供返回的值,应引发StopIteration异常
  3. xx.__next__() == next(xx)
  4. 可迭代对象 → 迭代器:调用内置函数 iter()
>>> it=iter(['a','b','c'])
>>> next(it)
'a'
>>> next(it)
'b'
  1. 迭代器 → 可迭代对象(list为例):
>>> class TestIterator:
...      value=0
...      def __next__(self):
...          self.value+=1
...          if self.value > 10 :raise StopIteration
...          return self.value
...      def __iter__(self):
...          return self
...
>>> ti=TestIterator()
>>> ti
<__main__.TestIterator object at 0x000001C3032524A8>
>>> next(ti)  
1
>>> next(ti)
2
>>> list(ti)
[3, 4, 5, 6, 7, 8, 9, 10]  #注:似乎next一个,会消失一个
>>> next(ti)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in __next__
StopIteration

二、生成器

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator

1.定义生成器

1.1 使用yield语句:

注意:

  • 包含yield语句的函数都被称为生成器。
  • generator 和 函数的执行流程不一样
    • 函数:顺序执行,遇到return语句/最后一行函数语句,就返回。
    • generator:在每次调用next()的时候执行,遇到 yield语句 返回,再次执行时从上次返回的 yield语句 处继续执行。
  • yield 与 return
    • yield:应生成一个值,
    • return:生成器应停止执行(即不再生成值;仅当在生成器调用return时,才能不提供任何参数)。

举个简单的例子,定义一个generator,依次返回数字1,3,5:

>>> def odd():
...     print("Step 1:")
...     yield 1
...     print("Step 2:")
...     yield 3
...     print("Step 3:")
...     yield 5
...

>>> o=odd()
>>> next(o)
Step 1:
1       #遇到yield 1,第一次中断
>>> next(o)   #从yield 1 后面开始执行
Step 2:
3       #遇到yield 3,第二次中断
>>> next(o)   #从yield 3 后面开始执行
Step 3:
5       #遇到yield 5,第二次中断
>>> next(o)   #从yield 5 后面开始执行
Traceback (most recent call last):   #无数据了
  File "<stdin>", line 1, in <module>
StopIteration

1.2 使用生成器表达式

生成器推导(也叫生成器表达式)。其工作原理与列表推导相同,但不是创建一个列表(即不立即执行循环),而是返回一个生成器,让你能够逐步执行计算。

>>> g = ((i+2)**2 for i in range(2,27))  #把一个列表生成式的[]改成()
>>> g
<generator object <genexpr> at 0x000001C303038408>
>>> next(g)
16
# 注:
#直接在一对既有的圆括号内(如在函数调用中)使用生成器推导时,无需再添加一对圆括号。换而言之,可编写下面这样非常漂亮的代码:
#    sum(i ** 2 for i in range(10))

2.几种生成器

2.1 递归式生成器

>>> def flatten(nested):
...     try:  #递归条件(可迭代对象):遍历子序列
...        for sublist in nested:  
...            for element in flatten(sublist):
...                yield element
...     except TypeError:  #基线条件(只有单个元素,如一个数,迭代不了,报错)
...        yield nested
...
>>> list(flatten([[[1],2],3,4,[5,[6,7]],8]))
[1, 2, 3, 4, 5, 6, 7, 8]

# 注意:这种方法存在问题:
#         如果nested是字符串或类似于字符串的对象
#         它就属于序列,因此不会引发TypeError异常
#         可你并不想对其进行迭代,所以产生了下面一种代码

改进后:

>>> def flatten(nested):
...     try:
...             try: nested + ''  # 检查其行为是否类似于字符串,即能否与字符串拼接
...             except TypeError: pass
...             else: raise TypeError
...             for sublist in nested:
...                     for element in flatten(sublist):
...                             yield element
...     except TypeError: yield nested
...
>>> list(flatten(['foo',['bar',['baz']]]))
['foo', 'bar', 'baz']

2.2 通用生成器

生成器 = 生成器的函数 + 生成器的迭代器
生成器函数:由def语句定义的,其中包含yield。
生成器的迭代器:生成器函数返回的结果。

# 定义生成器函数
>>> def simple_generator(): 
 yield 1 
... 

# 生成器函数
>>> simple_generator 
<function simple_generator at 153b44> 
# 生成器的迭代器
>>> simple_generator() 
<generator object at 1510b0>

3.生成器的方法

3.1 send方法

在生成器开始运行后,可使用 生成器和外部之间通信渠道向它提供值。这个通信渠道包含如下两个端点

  • 外部世界:可访问生成器的方法send,这个方法类似于next,但它接受一个参数(要发送的“消息”,可以是任何对象)。
  • 生成器:在挂起的生成器内部,yield可能用作表达式而不是语句。换而言之,当生成器重新运行时,yield返回一个值——通过send从外部世界发送的值。如果使用的是next,yield将返回None

注意:
        仅当生成器被挂起(即遇到第一个yield)后,使用send(而不是next)才有意义。

>>> def repeater(value):
...     while True:
...          new =(yield value)
...          if new is not None:value=new
...
>>> r=repeater(42)
>>> next(r)
42
>>> r.send("Hello,world!")
'Hello,world!'

3.2 throw和close方法

       throw:用于在生成器中(yield表达式处)引发异常,调用时可提供一个异常类型、一个可选值和一个traceback对象

       close:用于停止生成器,调用时无需提供任何参数


[ 参考文档 ]

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值