专注于互联网--专注于架构

最新标签
网站地图
文章索引
Rss订阅

首页 »Python » 迭代器:可爱的 Python: 迭代器和简单生成器 »正文

迭代器:可爱的 Python: 迭代器和简单生成器

来源: 发布时间:星期四, 2009年1月8日 浏览:30次 评论:0
  欢迎来到奇妙流控制世界Python 2.2(现在是 alpha 发行版第 3版 ― 参见本文后面 参考资料)将给员提供些新选项这些在较早 Python 版本是没有 ― 或者至少不是很方便

  虽然 Python 2.2 所给予我们不能象 Stackless Python 中完全连续性和微线程那样容易理解但还是可以说生成器和迭代器行为和传统和类会有点区别

  由于迭代器比较容易理解让我们先来看它基本上 迭代器是含有 .next 思路方法对象这样定义不十分正确但非常接近事实上当迭代器应用新 iter 内置大多数迭代器上下文希望得到个可以生成迭代器对象为使用户定义类(该类含有必不可少 .next 思路方法)返回迭代器需要使 __iter__ 思路方法返回 self 本文中举例会清楚地介绍说明这如果迭代有个逻辑终止则迭代器 .next 思路方法可能决定抛出 StopIteration 异常

  生成器要稍微复杂和般化但生成器最典型用途是用来定义迭代器;所以不值得总是为些细微的处而担心 生成器是这样它记住上次返回时在体中位置对生成器第 2次(或第 n 次)跳转至该中间而上次所有局部变量都保持不变

  在某些方面生成器就象本专栏前面文章讨论型编程中“终止”(参见 参考资料)象“终止”生成器“记住”了它数据状态但生成器比“终止”要更进步:生成器还“记住”了它在流控制构造(在命令式编程中这种构造不只是数据值)中位置由于连续性使您在执行框架间任意跳转而不总是返回到直接上下文(如同生成器那样)因此它仍是比较

  幸运使用生成器比理解流和状态所有概念性问题容易得多实际上稍加实战的后就可以象普通那样容易地使用生成器

  随机遍历

  让我们考虑个相当简单问题可以用多种思路方法来解决它 ― 新思路方法和旧思路方法都可以假设我们想要串正随机数字流它比服从向后参考约束数字流要小明确我们希望每个后续数字比前个数字至少大或小 0.4而且数字流本身不是无限在几个随机步骤后结束这个举例中当数字流中产生小于 0.1 数字时我们将简单地结束它上述约束有点象可以在“随机遍历”算法找到约束结束条件类似“统计”或“局部最小值”结果 ― 但当然这要比大多数现实世界中简单

  在 Python 2.1 或更早版本中我们有几种思路方法来解决这个问题种思路方法是简单地生成流中数字列表并返回它可能看起来象:

  RandomWalk_List.py

  
     import
    
     random
  
     def
  
     randomwalk_list
    
    :
  last, rand = 1, random.random
    
    # init candidate elements
  nums =            
    
    # empty list
    
     
     while
    
     rand > 0.1:       
    
    # threshhold terminator
         
     
     
    
     abs(last-rand) >= 0.4: 
    
    # accept the number
      last = rand
      nums.append(rand)   
    
    # add latest candidate to nums
         
     
     
    
    :
  
     
     pr
    
     '*',       
    
    # display the rejection
    rand = random.random   
    
    # candidate
  nums.append(rand)       
    
    # add the final small element
    
     
     
    
     nums
   


  利用这个就象如下所示般简单:

  随机遍历列表迭代

for num in randomwalk_list:
  pr num,


  上面这种思路方法中有几个值得注意局限性这个特定举例中极不可能产生庞大数字列表但只通过将阀值终结符定义得较严格就可以创建任意大流(随机精确大小但可以预见数量级)在某种程度上内存和性能问题可能使得这种思路方法不切实际以及没有必要同样是这个问题使得 Python 较早版本中添加了 xrange 和 xreadlines 更重要许多流取决于外部事件并且当每个元素可用时才处理这些流例如流可以侦听个端口或者等待用户输入试图在流的外创建完整列表并不就是这些情形中

  在 Python 2.1 和较早版本中我们诀窍是使用“静态”局部变量来记住有关些事情显而易见全局变量可以做同样工作但它们带来了大家熟知全局性名称空间污染问题并会因非局部性而引起这里如果您不熟悉这个诀窍可能会感到诧异 ― Python 没有“正式”静态范围声明然而如果赋予了命名参数可变缺省值那么参数就可以用作以前持久存储器明确列表是些便利可变对象他们甚至可以方便地保留多个值

  使用“静态”思路方法可以编写如下:

  RandomWalk_Static.py

  
     import
    
     random
  
     def
  
     randomwalk_
    
    (last=[1]):  
    
    # init the "" var(s)
  rand = random.random     
    
    # init a candidate value
     
     
     
    
     last[0] < 0.1:       
    
    # threshhold terminator
         
     
     
    
     None        
    
    # end-of-stream flag
     
     
     while
    
     abs(last[0]-rand) < 0.4: 
    
    # look for usable candidate
         
     
     pr
    
     '*',         
    
    # display the rejection
    rand = random.random   
    
    # candidate
  last[0] = rand         
    
    # update the "" var
     
     
     
    
     rand
   


  这个是十分友好存储器它只需要记住个以前返回个单个数字(不是个数字大列表)并且和此类似可以返回取决于(部分地或完全地)外部事件连续不利面是利用这个有点不够简练且相当不灵活

  静态随机遍历迭代

num = randomwalk_
while num is not None:
  pr num,
  num = randomwalk_


  新遍历思路方法

  实质上Python 2.2 序列都是迭代器Python 常见习惯使用方法 for elem in lst: 现在实际上让 lst 产生个迭代器然后 for 循环反复这个迭代器 .next 思路方法直到它遇到 StopIteration 异常为止幸运由于所有常见内置类型自动产生它们迭代器所以 Python 员不需要知道这里发生了什么实际上现在字典里有 .iterkeys 、 .iteritems 和 .itervalues 思路方法来产生迭代器;首要是在新习惯使用方法 for key in dct: 中使用了什么同样通过 .readline 迭代器支持新习惯使用方法 for line in file:

  但是如果实际所产生是在 Python 解释器内则显而易见要用定制类来产生它们自己迭代器而不是专使用内置类型迭代器定制类支持直接使用 randomwalk_list 以及个元素这种“极度节省” randomwalk_ 它是简单易懂:

  RandomWalk_Iter.py

  
     import
    
     random
  
     
  
     randomwalk_iter
    
    :
  
     
     def
  
     __init__
    
    (self):
    self.last = 1       
    
    # init the prior value
    self.rand = random.random
    
    # init a candidate value
     
     
     def
  
     __iter__
    
    (self):
  
     
     
    
     self        
    
    # simplest iterator creation
     
     
     def
  
     next
    
    (self):
  
     
     
    
     self.rand < 0.1:    
    
    # threshhold terminator
             
     
     raise
    
     StopIteration  
    
    # end of iteration
         
     
     
    
    :           
    
    # look for usable candidate
             
     
     while
    
     abs(self.last-self.rand) < 0.4:
  
     
     pr
    
     '*',     
    
    # display the rejection
        self.rand = random.random
    
    # candidate
      self.last = self.rand 
    
    # update prior value
              
     
     
    
     self.rand
   


  这个定制迭代器看起来确实如同由生成真实列表样:

  随机遍历类迭代

for num in randomwalk_iter:
  pr num,


  事实上即使支持习惯使用方法 elem in iterator 它仅尝试为确定真值所需要那么多迭代器元素(如果最终值为 false当然它就需要测试所有元素)

  美中不足

  上述思路方法对于手边问题非常好用但没有种思路方法能很好地解决这样情形:例程在运行中创建了大量局部变量并把它运行简化为循环和条件嵌套如果带静态(或全局)变量迭代器类或取决于多个数据状态则出现两个问题个是般性问题:创建多个例子属性或静态列表元素来保留每个数据值更为重要问题是计算如何确切地返回到和数据状态相符流逻辑相关部分非常容易忘记区别数据间相互作用和互相依存

  生成器完全绕过了整个问题生成器“返回”时带关键字 yield 但“记住”了它“返回”所有确切执行位置下次生成器时它再接着上次位置 — 包括流和变量值这两个方面

  在 Python 2.2+ 中不直接 写生成器相反编写它时返回生成器这可能看起来有点古怪但“工厂”是 Python 常见特性并且“生成器工厂”明显是这个概念性扩展在 Python 2.2+ 中使成为生成器工厂是它主体某处个或多个 yield 语句如果 yield 发生 定只发生在没有伴随任何返回值情况中然而个较好选择是安排体以便于完成所有 yield 的后执行就“跳转到结束”但如果遇到 它导致产生生成器抛出 StopIteration 异常而不是进步生成值

  从我观点来看过去对生成器工厂语法选择有点欠缺 yield 语句可以非常好地存在于体中您可能无法确定是否定会在体最初 N 行内某处作为生成器工厂而存在当然对于工厂也存在这样问题但是由于工厂不改变实际 语法(并且有时允许体返回普通值尽管这可能不是出自良好设计)对于我来说新关键字 ― 比如 generator 代替 def ― 会是个比较好选择

  先不考虑语法生成器来担当迭代器时生成器有良好状况来自动担当迭代器这里不需要象类 .__iter__ 思路方法遇到每个 yield 都成为生成器 .next 思路方法返回值为了清楚起见我们来看个最简单生成器:

  最简单可行 Python 2.2 生成器

>>>
  
     from
    
     __future__
  
     import
    
     generators
>>>
  
     def
  
     gen
    
    :
    yield 1
>>> g = gen
>>> g.next
1
>>> g.next
Traceback (most recent call last):
 File "<pyshell#15>", line 1,
  
     in
    
     ?
  g.next
StopIteration
   


  让我们使生成器工作在我们样本问题中:

  RandomWalk_Generator.py

  
     from
    
     __future__
  
     import
    
     generators 
    
    # _disibledevent= randomwalk_generator
try:
  while 1: pr gen.next,
except StopIteration:
  pass


  然而更多情况下可能将生成器作为迭代器来使用这样更为简练(并且看起来又象只是个老式序列):

  作为迭代器随机遍历生成器

for num in randomwalk_generator:
  pr_(num)


  结束语

  Python 员需要花点时间来熟悉生成器来龙去脉最初这样个简单构造所增加能力是令人惊奇;并且我预言甚至熟练员(象 Python 开发人员自己)也需要花些时间来继续发现使用生成器过程中些微妙新技术

  作为结束让我再介绍个生成器举例它来自随 Python 2.2 同分发 test_generators.py 模块假定个树对象并且想要以从左到右顺序搜索它叶子使用状态监控变量让类或来做这个工作是困难而使用生成器做这件工作简单得几乎令人眉开眼笑:

>>>> # A recursive generator that generates Tree leaves in in-order.
>>> def inorder(t):
...   t:
...     for x in inorder(t.left):
...       yield x
...     yield t.label
...     for x in inorder(t.right):
...       yield x




相关文章

读者评论

  • 共0条 分0页

发表评论

  • 昵称:
  • 内容: