这是Python之禅和他朋友们在知识星球的第2题:
题目:设计一个猜数字的游戏,系统随机生成一个1~100之间的整数,玩家有5次机会,每猜一次系统就会提示玩家该数字是偏大还是偏小,如果猜中了,则告知玩家并提前结束游戏,如果5次都没猜中,结束游戏并告知正确答案。
review完大家的代码,合并master分支,再把这篇文章写法发现已经凌晨1点了,截止到10月30日总共有18人提交了代码,有少部分朋友还没有学会如何使用GitHub,另外,每天出一个题对一部分人来说有点跟不上,所以为了能够控制好我们的节奏,咱们每隔一天发一道题(改成每周1,3,5),这样让大家有更多的时间去学习,同时也方便我有更充足的时间来看大家的代码。
大家的代码主要有几个问题:
1、函数、变量命名不规范
有些同学给函数、变量命名时,使用Java中的驼峰标识法,比如:guessNumber ,Python 官方的推荐写法是要求函数名全部小写,单词之间采用下划线连接,例如:guess_number。建议大家没事多阅读阅读PEP8代码风格指南
2、while 条件语句不需要括号
有代码中把while的判断语句用括号括起来,其实这是多余的操作,在Python中,条件判断语句不需要括号
while (chance == 4)
3、没有对异常进行处理
很多人直接把用户输入的内容转换为int,却没有对输入值做判断,如果输入的是非数字,程序就崩溃了,显然,如果在年会搞一个这样的抽奖系统,那样就糗大了。正确的做法应该是要对异常、边界情况都要做判断。
4、很多人的代码写得不够简洁,比如引入了一些没必要的临时变量,没有必要的 continue 关键字等等。
5、代码不够抽象
代码不够抽象是什么意思呢?先来看这位同学写的代码:
import random
answer = random.randint(1, 100)
i = 1
while i <= 5:
guess = int(input("猜一个0到100的整数:"))
if guess == answer:
print('厉害了,猜对了!')
break
else:
if guess > answer:
print('猜大了!')
else:
print('猜小了!')
chance = 5 - i
print('再猜,还有%d次机会!' % chance)
i += 1
print('游戏结束,正确答案是:%d' % answer)
这段代码虽然能完成的任务,但是没法灵活应对需求,在变化莫测的互联网时代,如何把我们的代码写得更灵活来应对产品的需求是程序员要思考的问题。比如这里要求提高中奖几率(缩小随机取值范围、增加中奖次数),我们不得不修改整个代码,最好的代码就是在产品需求发生变化时,代码改动尽可能少,这就要求我们的代码写得更抽象、更灵活。
我们来针对这段代码来进行重构,用面向对象的编程方式来实现,既然是面向对象,我们就要换一个角度来把问题抽象化,这个游戏有4个属性,分别是随机数的取值范围,min和max,目标值 target,猜测次数 choice,另有还有一个方法用于如何猜游戏,随着需求的变化,我们可以不断地加其他属性和方法。
import random
class GuessGame:
def __init__(self, min_num, max_num, choice):
"""
:param min_num: 最小值
:param max_num: 最大值
:param choice: 中奖机会
"""
self.max_num = max_num
self.min_num = min_num
self.target = random.randint(min_num, max_num)
self.choice = choice
def guess(self):
"""
猜数字
"""
choice = self.choice
while choice > 0:
choice -= 1
try:
num = int(input("输入幸运数字:"))
except ValueError as e:
print("请输入有效数字")
continue
if num == self.target:
print("恭喜你猜中了")
break
elif num <= self.target:
print("你猜的数字太小了,还剩 %d 次机会" % choice)
else:
print("你猜的数字太大,还剩 %d 次机会" % choice)
else:
print("很遗憾,%d 次机会都用完了,只差一点点就猜中了,正确答案是 %d" % (self.choice, self.target))
if __name__ == '__main__':
game = GuessGame(1, 100, 5)
# print(game.target)
game.guess()
现在如果产品要求更改猜测的次数或者扩大随机数的范围,我们不需要修改这个类里面的任何一行代码,只需要在调用的时候改变一下即可。话说回来,有时我们没必要为了一个很小的功能而这样去设计,但是,如果随着这个游戏的功能,复杂度越来越高的时候,这样的设计就很有必要了。然而在真实场景中,需求是一点点往上加的,如果我们能站在更高的角度来思考问题,就能避免被产品牵着鼻子走,走在产品的前面,面对他们的需求游刃有余。
大家可以和自己的代码对比一下,可以看到我使用了python中特有的 while ... else 语法,在这样的场景非常适合,免去了使用额外的标识变量来表示是否猜中,同样还有 for ... else 和 try ... else 语法,大家可以总结下这些语法是如何使用的。
当然,现在离一个完整的程序还差一步,就是缺少单元测试,单元测试是保证代码质量的重要手段,大家方便的时候可以学习一下如何写单元测试,把代码进行完善。
关注公众号「Python之禅」,回复「1024」免费获取Python资源