设为首页 收藏本站
查看: 1254|回复: 0

[经验分享] ·python·用生成器和迭代器实现自己的xrange

[复制链接]

尚未签到

发表于 2017-5-8 08:24:30 | 显示全部楼层 |阅读模式
·python·用生成器和迭代器实现自己的xrange

声明:本文由恋花蝶发表于http://blog.csdn.net/lanphaday,版权所有,欢迎转载。转载时应保留声明。谢谢。

用过python的朋友一定很熟悉下面这两行代码:
>>> for i in xrange(0,10,1):
print i
上面的两行代码是用一个循环打印0-9这十个数字。你也想实现像xrange这样的可以用在for语句里的函数(类)吗?那跟我来吧!
首先来介绍一下python的yield语句,Yield这个单词本身有产生、产出的意思,它的语法是:
yield 表达式
关于yield语句,官方manual是这样说的:yield语句仅用以定义生成器函数,而且它只能出现在生成器函数内;在函数定义中使用yield语句的充分理由是想实现以个生成器函数而不是普通函数。当生成器函数被调用,它返回一个视作生成器的迭代器的迭代器、更通俗地说是一个生成器。生成器函数的函数体将被生成器的next方法重复调用直到产生一个异常;当yield语句被执行的时候生成器的状态被冻结并且表达式的值返回给next()的调用者,所谓“冻结”我们可以理解成函数在这里被保存现场并切换了出去(如果你了解操作系统的进程管理的话,应该很容易理解这句话)。
嗯,太隐晦了些,看个实例吧。
>>> def simple_xrange (num):
  while(num):
  yield num
  num -= 1

>>> l = list(simple_xrange(8))
>>>print l
[8, 7, 6, 5, 4, 3, 2, 1]
在上例中我们实现了一个简单的xrange,生成倒序的数字系列。但还是看不出这个simple_xrange是怎么执行的,现在我们来看看下面的实验:
>>> it = simple_xrange (8)
>>> it.next()
8
>>> it.next()
7
>>> it.next()
6
……
>>> it.next()
1
>>> it.next()

Traceback (most recent call last):
File "<pyshell#48>", line 1, in -toplevel-
it.next()
StopIteration
现在我们从上面的实验中来看simple_xrange的执行过程:
1、 当执行it = simple_xrange(8)时,simple_xrange返回一个生成器,即it成为一个生成器。
2、 当执行it.next()时,simple_xrange的函数体被执行,当执行到yield num语句时,simple_xrange被“冻结”,然后返回num,即8
3、 再次执行it.next(),simple_xrange“解冻”,执行num -= 1,因为是循环,所以再执行while(num),这时又是执行yield num,simple_xrange被“冻结”,返回num,即7
4、 再一次次调用下去,直到simple_xrange的while(num)不成立,跳出循环,返回时next()函数抛出一个StopIteration异常,这时生成器函数就执行完结了。
把上面的1234条目跟上文python manual的说法对照一下,是相互呼应的,这样我们就理解了xrange的实现机理,从而可以利用yield语句写出自己的xrange了。
理解了yield之后,理解另一种实现xrange的方法就容易多了,这种方法就是定义自己的迭代器。对于迭代器,python manual的说法是这样的:python支持一种超越容器的迭代器观念,使得用户定义的类支持迭代。迭代器对象需要支持__iter__()和next()两个方法,其中__iter__()返回迭代器自身,next()返回系列的下一个元素。嗯,还是通过实例来说吧:
>>> class simple_xrange:
def __init__(self, num):
  self._num = num
def __iter__(self):
return self
def next(self):
if self._num <= 0:
  raise StopIteration
tmp = self._num
  self._num -= 1
return tmp

>>> l = list(simple_xrange(8))
>>> l
[8, 7, 6, 5, 4, 3, 2, 1]
哈哈,读一下源代码,似乎这个比yield语句更简明易懂,也许这就是在有了yield语句之后还要支持迭代器类型的原因吧!有了yield知识,理解这段源代码是很简单的了,我就不多言了。
搞了这么久,实现自己的xrange有必要吗?当然是有的,xrange只是产生了一个系列,如果要对这个系列有什么扩展的话,写出来的代码就比较难看了。举个在现实工作中我遇到的例子:我做一个纸牌游戏,我用list来表示将要打出的牌(我用0~53表示一副牌,其中0表示最小的牌——方块3),如[0,0,3,3]表示两对编号分别为0,3的牌,即由两个方块3两个黑桃3组成的(本游戏使用两副牌,所以可以有两个相同的牌ID)。后来修改了游戏规则,新的游戏规则规定大joker(牌ID为53)可以变化为任意牌,比如[0,0,3,53]也是一个。这时我写了下面的代码来判断一个list是不是一个正确的牌型:
#当cards里有big joker时调用本函数判断是否为有效牌型
def is_valid_pattern_with_big_joker(cards):
_cards = cards[:] #因为要改变cards,所以函数内使用cards的拷贝
be_replace_card = 53 #big joker将被替换
#枚举所有的可能
for i in xrange(53, -1,-1):
  _cards.remove(be_replace_card)
  _cards.append(i)
  be_replace_card = i
if is_valid_pattern(_cards)#如果还有big joker,is_valid_pattern会递归调用本函数
  return True
return False
看那for循环的循环体,多么复杂,又是remove又是append还有中间变量要保存,有没有办法简单点?有!使用迭代器吧。
class ReplacedBigJokerCards:
def __init__(self, cards):
  self._cards = cards[:]
  self._be_replaced_card = 53
  self._candidate = 52
def __iter__(self):
return self
def next(self):
if self._candidate < 0:
  raise StopIteration
self._cards.remove(self._be_replace_card)
self._cards.append(self._candidate)
self._be_replace_card = self._candidate
  self._candidate -= 1
return self._cards

def is_valid_pattern_with_big_joker(cards):
for _cards in ReplacedBigJokerCards(cards):
if is_valid_pattern(_cards)
  return True
return False

看,现在把大joker变牌的细节隐藏起来,is_valid_pattern_with_big_joker变得优雅多了。重要的另一点就是在游戏中,除了判定牌型外,还有智能提示等多个功能都能够重用ReplaceBigJokerCards,使用这样的定制迭代器,比散落在代码各处的remove/append比好得多。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-374408-1-1.html 上篇帖子: 零基础学python-18.4 函数对象:间接调用函数 下篇帖子: Head First 设计模式——观察者模式(Observer Pattern)——Python实现(1)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表