可爱的Python: 用基于生成器的状态机和协同程序增加效率

Python 2.2 中引进的简单生成器可用于简化状态机以及模仿协同程序。David 在“可爱的 Python”专栏较早前的一个部分中介绍了一个 状态机处理的抽象模式。从那时起,简单生成器的引进就为描述机器提供了一些更自然的范例。协同程序是一种“外来”流机制,广泛使用的语言几乎都不支持这种机制(甚至连非 Stackless Python 都不支持它)。然而,Python 的新生成器 几乎完全支持协同程序,几乎不用模仿任何额外的步骤。在本文中,David 通过说明性代码样本解释了所有相关概念。

您需要花上一段时间才能完全“了解”Python 2.2 的新生成器。即使在“可爱的 Python”前面一部分中已经写过了 介绍简单生成器,我还是不能说已经完全理解了生成器的“完整结构(gestalt)”。本文介绍了一些供生成器使用的附加模式,并且希望它能让我本人和读者更深入地了解“可恢复函数”这一理念体系。

简单生成器有许多优点。生成器除了能够用更自然的方法表达一类问题的流程之外,还极大地改善了许多效率不足之处。在 Python 中,函数调用代价不菲;除其它因素外,还要花一段时间解决函数参数列表(除了其它的事情外,还要分析位置参数和缺省参数)。初始化框架对象还要采取一些建立步骤(据 Tim Peters 在 comp.lang.python 上所说,有 100 多行 C 语言程序;我自己还没检查 Python 源代码呢)。与此相反,恢复一个生成器就相当省力;参数已经解析完了,而且框架对象正“无所事事地”等待恢复(几乎不需要额外的初始化)。当然,如果速度是最重要的,您不应该使用字节码已编译过的动态语言;但即使在速度不是主要考虑因素的情况下,快点总比慢点好。

回忆状态机

在“可爱的 Python”前面的另一篇文章中,我介绍了 StateMachine 类 ,给定的机器需要多少状态处理程序,它就允许用户添加多少状态处理程序。在模型中,将一个或多个状态定义为终态(end state),仅将一个状态定义为初始状态(start state)(调用类方法对此进行配置)。每个处理程序都有某种必需的结构;处理程序将执行一系列操作,然后过一会儿,它带着一个标记返回到 StateMachine.run() 方法中的循环内,该标记指出了想得到的下一个状态。同样,用 cargo 变量允许一个状态把一些(未处理的)信息传递给下一个状态。

我介绍的 StateMachine 类的典型用途是以一个有状态的方式使用输入。例如,我所用的一个文本处理工具(Txt2Html)从一个文件中读取数行内容;依据每行所属的类别,需要以特殊的方式对其进行处理。然而,您经常需要看看前面几行提供的上下文来确定当前行属于哪个类别(以及应该怎样处理它)。构建在 StateMachine 类上的这个过程的实现可以定义一个 A 处理程序,该处理程序读取几行,然后以类似 A 的方式处理这些行。不久,满足了一个条件,这样下一批的几行内容就应该由 B 处理程序来处理了。 A 把控制传递回 .run() 循环,同时指示切换到 B 状态 ― 以及任何 A 不能正确处理的、 B 应该在阅读额外的几行之前处理的额外的行。最后,某个处理程序将它的控制传递给某个被指定为终态的状态,处理停止(halt)。

对于前面一部分中的具体代码示例,我使用了一个简化过的应用程序。我处理由迭代函数产生的数字流,而不是处理多行内容。每个状态处理程序仅打印那些在期望的数字范围内的数字(以及关于有效状态的一些消息)。当数字流中的一个数字传到一个不同的范围内,另一个不同的处理程序就会接管“处理”。对于这一部分,我们将看看另一种用生成器实现相同数字流处理的方式(有一些额外的技巧和功能)。但是,一个更复杂的生成器示例有可能对更象上一段中提到的输入流进行处理。我们再来看看前一个状态机删减过代码的版本:

清单 1. statemachine_test.py

from statemachine import StateMachine
def ones_counter(val):
  print "ONES State:  ",
  while 1:
    if val <= 0 or val >= 30:
      newState = "Out_of_Range" ; break
    elif 20 <= val < 30:
      newState = "TWENTIES";   break
    elif 10 <= val < 20:
      newState = "TENS";     break
    else:
      print " @ %2.1f+" % val,
    val = math_func(val)
  print " >>"
  return (newState, val)
# ... other handlers ...
def math_func(n):
  from math import sin
  return abs(sin(n))*31
if __name__== "__main__":
  m = StateMachine()
  m.add_state("ONES", ones_counter)
  m.add_state("TENS", tens_counter)
  m.add_state("TWENTIES", twenties_counter)
  m.add_state("OUT_OF_RANGE", None, end_state=1)
  m.set_start("ONES")
  m.run(1)

读者如果接下来对导入的 StateMachine 类以及它的方法如何工作感兴趣,应该看看前面的文章。

时间: 2024-09-12 06:39:59

可爱的Python: 用基于生成器的状态机和协同程序增加效率的相关文章

可爱的Python: 用Python生成器实现“轻便线程”

在 "可爱的 Python"的前面一部分中,David 介绍了一种用生成器和简单的调度程序模拟完整协同程序的方法.我们也许可以用直观的方式来扩展这种调度程序,使其允许对多进程使用极为轻量级的线程.与 Stackless Python 微线程非常相似,伪协同程序"轻便线程"几乎不需要 OS(甚至用户区)线程的上下文切换和内存开销.David 在这里介绍了轻便线程,一种巧妙的解决方案,它用来解决使用普通解决方案处理将涉及大量协同进程的问题. 微线程领域(至少在 Pyth

可爱的Python: 使用Spark模块解析

Spark 是一种用 Python 编写的强大的.通用的解析器/编译器框架.在某些方面,Spark 所提供的比 SimpleParse 或其它 Python 解析器提供的都要多.然而,因为它完全是用 Python 编写的,所以速度也会比较慢.David 在本文中讨论了 Spark 模块,给出了一些代码样本,解释了它的用途,并对其应用领域提供了一些建议. 继"可爱的 Python"系列中专门讲述 SimpleParse 的 前一篇文章之后,我将在本文中继续介绍一些解析的基本概念,并对 S

可爱的Python:Curses编程

某一类 Python应用程序最好使用交互式用户界面,这样可以消除图形环境的系统开销或复杂性.交互式文本模式程序(在Linux/UNIX 中),例如封装在 Python 的标准 curses模块中的 ncurses 库,正是您所需要的.本文中,DavidMertz 讨论了在 Python 中 curses 的用法.他使用从前端到 Txt2Html程序的样本源代码阐述了 curses 环境. curses 库 ( ncurses ) 提供了控制字符屏幕的独立于终端的方法.curses 是大多数类似于

来写一个 Python 说明文档生成器吧

本文讲的是来写一个 Python 说明文档生成器吧, 我一开始学习 Python 的时候,我最喜欢的一件事就是坐在编译器前,使用内置的 help 函数检查类和方法,然后决定我接下来要怎么写.这个函数会引入一个对象并检查其内部成员,生成说明并且输出类似帮助文档的内容,帮助你了解该对象的使用方法. 将 help 函数置入标准库最为美妙的一点就是它能直接从代码中输出说明内容,这也间接地助长了一些人的懒惰,比如像我这种不愿意多花时间来维护文档的人.尤其是你已经为你的变量和函数起好了直白的名字,help 

python实现基于两张图片生成圆角图标效果的方法_python

本文实例讲述了python实现基于两张图片生成圆角图标效果的方法.分享给大家供大家参考.具体分析如下: 使用pil的蒙版功能,将原图片和圆角图片进行叠加,并将圆角图片作为mask,生成新的圆角图片 from PIL import Image flower = Image.open('flower.png') border = Image.open('border.png') source = border.convert('RGB') flower.paste(source, mask=bord

可爱的Python:Decorator简化元编程

Python 使元编程成为可能,不过每个版本的 Python 都有一些细微的区别(并且不是完全兼容),这使我们实现元编程的道路变得更加崎岖.一类函数对象的使用由来已久,同样还有一些技术用于探索和实现魔术般的属性.在版本 2.2 中,Python 增加了一种很有帮助的定制元类机制,但是其代价就是令用户绞尽脑汁.最近,在 2.4 版本中,Python 增加了 "decorator" ,这是适于执行大部分元编程的最新方式 -- 也是到目前为止对用户最友好的方式. 少劳多得 Decorator

可爱的Python:将XML和Python结合起来

开始在 Python 中使用 XML 的一个主要要素是排列出所有可用模块的可比性能力.在他的新 Python 专栏"可爱的 Python"的第一部分中,David Mertz 简要描述了最流行和实用的关于 XML 的 Python 模块,并指出可以下载的单独模块以及可供阅读的参考资料.本文有助于确定哪些模块最适合特定任务. 在许多情况下,Python 是使用 XML 文档的理想语言.像 Perl.REBOL.REXX 和 TCL 一样,它是一种灵活的脚本语言,并且有强大的文本操作能力.

可爱的Python:动态重新装入

与大多数其它编程语言相比,Python 的一大优点就是其强大的运行时动态能力.感谢方便的 reload() 函数,我们可以编写持续运行的程序,但它可以在进程运行期间装入经过修改的组件(对于那些持续运行时间至关重要的服务来说, 相当有用).b本文在 David 以前的文章中讨论的对 Txt2Html 前端的某些增强基础上,说明了运行时程序修改.特别是,我们的样本程序将对因特网上 Txt2Html 转换库的新版本进行后台检查, 并下载和重新装入所需的新版本,无需用户手工介入. 让我们描绘一下本文的情

可爱的Python: 重温Python的XML工具

David Mertz 创作的 可爱的 Python的第一.第二部分概述了在 Python 中使用XML.然而,在那些最初的文章出现后,Python 中的 XML工具有了很大的发展.不幸的是,这些改进中的大多数并不向后兼容.在这个特别部分中,重温了作者先前对XML 工具的讨论,并提供最新的代码示例. 在许多情况下,Python 是使用 XML 文档的理想语言.像 Perl.REBOL.REXX 和 TCL 一样,它是一种灵活的脚本语言,并且有强大的文本操作能力.而且,除了对多数类型的文本文件(或