参考文献

注册

 

发新话题 回复该主题

PEP简单的生成器 [复制链接]

1#

(给Python开发者加星标,提升Python技能)

作者:豌豆花下猫(本文来自作者投稿)

摘要

这个PEP想在Python中引入生成器的概念,以及一个新的表达式,即yild表达式。

动机

当一个生产者函数在处理某些艰难的任务时,它可能需要维持住生产完某个值时的状态,大多数编程语言都提供不了既舒服又高效的方案,除了往参数列表中添加回调函数,然后每生产一个值时就去调用一下。

例如,标准库中的tokniz.py采用这种方法:调用者必须传一个toknatr函数给tokniz(),当tokniz()找到下一个tokn时再调用。这使得tokniz能以自然的方式编码,但程序调用tokniz会变得极其复杂,因为它需要记住每次回调前最后出现的是哪个tokn(s)。tabnanny.py中的toknatr函数是处理得比较好的例子,它在全局变量中维护了一个状态机,用于记录已出现的tokn和预期会出现的tokn。这很难正确地工作,而且也挺难让人理解。不幸的是,它已经是最标准的解决方法了。

有一个替代方案是一次性生成Python程序的全部解析,并存入超大列表中。这样tokniz客户端可以用自然的方式,即使用局部变量和局部控制流(例如循环和嵌套的if语句),来跟踪其状态。然而这并不实用:程序会变得臃肿,因此不能在实现整个解析所需的内存上放置先验限制;而有些tokniz客户端仅仅想要查看某个特定的东西是否曾出现(例如,futur声明,或者像IDLE做的那样,只是首个缩进的声明),因此解析整个程序就是严重地浪费时间。

另一个替代方案是把tokniz变为一个迭代器,每次调用它的nxt()方法时再传递下一个tokn。这对调用者来说很便利,就像前一方案把结果存入大列表一样,同时没有内存与“想要早点退出怎么办”的缺点。然而,这个方案也把tokniz的负担转化成记住nxt()的调用状态,读者只要瞄一眼tokniz.tokniz_loop(),就会意识到这是一件多么可怕的苦差事。或者想象一下,用递归算法来生成普通树结构的节点:若把它投射成一个迭代器框架实现,就需要手动地移除递归状态并维护遍历的状态。

第四种选择是在不同的线程中运行生产者和消费者。这允许两者以自然的方式维护其状态,所以都会很舒服。实际上,Python源代码发行版中的Dmo/thrads/Gnrator.py就提供了一个可用的同步通信(synchronizd-

分享 转发
TOP
发新话题 回复该主题