因为该语言是Python,所以我将其命名为“玫瑰周围的Pythons”,并且在我的代码中的任何地方,单词“ rose”都将替换为“ python”。
此外,“ Potentate”也将替换为“ Pytentate”。
代码:
import random
def display_help():
example = get_dice_result()
print("The name of the game is Pythons Around the Rose.",
"The name is important. I will roll five dice,",
"and I will tell you how many pythons there are.",
"\nFor example:", sep = "\n")
print_dice(example)
print("Will result in ", get_answer(example), ".", sep = "")
print("\nIf you answer correctly 8 times in a row, you",
"will be declared a \"Pytentate of the Rose\".", sep = "\n")
def play():
streak = 0
while True:
dice = get_dice_result()
if play_once(dice):
streak += 1
else:
streak = 0
if streak == 8:
print('You are now declared a "Pytentate of the Rose"!')
break;
print("Thank you for playing!")
def get_dice_result():
result = [get_dice_roll(), get_dice_roll(),
get_dice_roll(), get_dice_roll(), get_dice_roll()]
return result
def get_dice_roll():
return random.randrange(6) + 1
def play_once(results):
print("How many pythons here?")
print_dice(results)
guess = get_guess()
answer = get_answer(results)
if guess == answer:
print("Correct!")
return True
print("Incorrect. The answer is ", answer, ".", sep = "")
return False
def get_guess():
guess = 0
valid = False
while not valid:
try:
guess = int(input("> "))
valid = True
except:
print("\nOops! That is not a number. Try again: ")
return guess
def get_answer(dice):
answer = 0
for i in dice:
if i == 3:
answer += 2
elif i == 5:
answer += 4
return answer;
def print_dice(dice):
rows = ["|", "|", "|"]
for i in dice:
if i == 1:
rows[0] += " |"
rows[1] += " . |"
rows[2] += " |"
elif i == 2:
rows[0] += " . |"
rows[1] += " |"
rows[2] += " . |"
elif i == 3:
rows[0] += " . |"
rows[1] += " . |"
rows[2] += " . |"
elif i == 4:
rows[0] += " . . |"
rows[1] += " |"
rows[2] += " . . |"
elif i == 5:
rows[0] += " . . |"
rows[1] += " . |"
rows[2] += " . . |"
elif i == 6:
rows[0] += " . . |"
rows[1] += " . . |"
rows[2] += " . . |"
print(rows[0], rows[1], rows[2], sep = "\n")
def main():
display_help()
play()
if __name__ == '__main__':
main()
我写它尽可能地基于标准约定。因为我是Java语言的人,所以我可能在代码中的某个地方添加了
;
,或者偶然地忘记了在某个地方遵循约定。#1 楼
总的来说,您的代码看起来不错,但是它确实表明您拥有另一种编程语言背景,因此您可以使用标准命令式方法来实现它,而不是使用某些更Python的方法。在这篇评论中,我故意讲一点,但作为回报,您会看到一些更特殊的pythonic构造。使用
__doc__
获得display_help
的docstring –在Python中,您可以并且应该使用docstrings描述您的代码,docstrings通常是带有三引号的文本,即""" ... """
。这些可以用来描述模块,函数,类(和方法)。它们通常在要记录的项目的定义之后出现。一件整洁的事情是,可以使用特殊变量
__doc__
或对于函数display_help.__doc__
来访问此文档字符串。然后可以将其用于打印输出或类似的内容。在命名方法上做更多的工作–在Python中,拥有
get_xxxx
方法并不是很常见,而是根据特定的函数来命名它们,例如roll_multiple_dice()
或roll_die()
。力求简单明了。使用get_answer()
表示您要我插入某些答案,而number_of_pythons()
表示将要计算出答案。列表理解是您的朋友–与其重复代码来构建列表,请使用
return [roll_die() for _ in range(number_of_dice)]
它实现了三个不错的功能:列表理解,它基于内部生成器构建列表,即
[ ... ]
在生成器中使用
for
, x for x in range(y)
,它将for
循环压缩到表达式中。如果我们不真正关心数字,而只希望执行0
循环前面的代码,则此函数会将y-1
到_
的数字返回使用
for
。 > 生成器表达式-最后一项的
for
循环也可以在列表推导之外使用,即sum(x for x in range(y))
,它将对从0
到y-1
的所有数字求和。列表推导和生成器之间的一个主要区别,是列表推导实际上构建了整个列表,而生成器在后台工作,并在需要时为您提供了另一个元素。
在求和示例中,这意味着如果您拥有
y=1000000
,分配的内存不能超过1个int,而如果使用列表推导,则会分配整个100万个元素的列表,然后对其求和。 Python三元数,即
a = b if c else d
–这可能要花一些时间才能习惯,但实际上它读起来很不错:如果条件a
为真,则将b
的值设置为c
免责声明:在我的重构代码中,这有点丑陋,因为我将这种构造的多个结合在一起。但是,它们还是有道理的。我在
d
和number_of_pythons()
中都使用了它们。避免一次使用中间变量-跳过中间变量是很常见的,特别是如果只使用一次并且直接使用表达式的话。即在
print_dice()
中只返回随机数,而在roll_die()
中直接返回列表。 在某些情况下,您可以使用布尔值求和–做
roll_multiple_dice()
是一种反模式,在某些情况下可以用if True: a += 1
代替,或者在您的情况下:a += True
。 由200_success找到的错误:在这里,简化太过分了,正如我通过简单地求和得出的那样,消除了条纹计数器的重置。
尽可能避免使用标记变量-最好在输入验证中使用
streak += play_once(...)
和valid
来编写get_guess()
中的while True
。跳过中间变量,您可以直接退出循环。尝试避免链接
break
命令,执行相同的操作-在if
和get_answer()
中,您都有print_dice()
链,其中块类型为一样。通常这表明您可以找到一种更好的处理方法。在
if
中,我选择了双三元组,进行get_answer()
。 在
answer += 2 if i == 3 else 4 if i == 5 else 0
中,我使用了一个六面模只有5种不同图案的事实,然后又使用了另一个双三元模从不同选项中进行选择。 免责声明:即使这段代码比您的代码短很多,但这并不意味着它会更好。但这确实显示了另一种方法,并且有多种方法可以打印管芯结果。请参见此处或此处。
使用常量而不是幻数-而不是在代码中隐藏
print_dice()
和5
,请在文件顶部使用常量,例如8
和REQUIRED_STREAK_RUN = 8
。这使您可以轻松地更改它们(并且仍然可以使用NUMBER_OF_DICE = 5
使文档反映更改。使用str.format()进行字符串格式化-这是一个非常有用的结构,值得一些您可以使用没有名称的名称(请参阅str.format()
的末尾),也可以像在play_once()
中那样命名变量。PS!如果使用Python 2,建议您使用
display_help()
进行获取功能:您不能在不成为Pythentate的情况下退出程序–我认为为用户提供出路很好。我实现了一个懒惰的版本如果输入否定答案,则可以终止操作。这样做可以做得更好,但是应该可以随时退出。
重构代码
这是使用上面大多数(如果不是全部)建议的代码:
"""Pythons Around the Rose
The name of the game is Pythons Around the Rose.
The name is important. I will roll {number_of_dice} dice,
and I will tell you how many pythons there are.
For example:
| . . | | . . | . | . . |
| . | . | | . | . |
| . . | | . . | . | . . |
Will result in 10 pythons. If you answer
correctly a given {streak_run} number of times you
will be declared a "Pythentate of the Rose".
"""
import random
import sys
REQUIRED_STREAK_RUN = 8
NUMBER_OF_DICE = 5
def display_help():
"""Use the docstring of the module."""
print(__doc__.format(number_of_dice = NUMBER_OF_DICE,
streak_run = REQUIRED_STREAK_RUN))
def play():
"""Play until declared a Pytentate..."""
streak = 0
while True:
# My bug: streak += play_once(roll_multiple_dice(NUMBER_OF_DICE)):
if play_once(roll_multiple_dice(NUMBER_OF_DICE)):
streak += 1
else:
streak = 0
if streak == REQUIRED_STREAK_RUN:
print('You are now declared a "Pytentate of the Rose"!')
break
print("Thank you for playing!")
def roll_multiple_dice(number_of_dice):
"""Return a list of die rolls."""
return [roll_die() for _ in range(number_of_dice)]
def roll_die():
"""Return a single die roll."""
return random.randrange(6) + 1
def play_once(results):
"""Play a single round, and return True if correct guess."""
print("How many pythons here?")
print_dice(results)
guess = get_guess_of_pythons()
# Simple bail out option... :-)
if guess < 0:
sys.exit(0)
answer = number_of_pythons(results)
if guess == answer:
print("Correct!")
return True
print("Incorrect. The answer is {}".format(answer))
return False
def get_guess_of_pythons():
"""Get input of pythons from user."""
while True:
try:
return int(input("> "))
except:
print("\nOops! That is not a number. Try again: ")
def number_of_pythons(dice):
"""Return correct number of pythons."""
return sum(2 if i == 3 else
4 if i == 5 else
0 for i in dice)
EYES = [" |",
" . |",
" . |",
" . |",
" . . |"]
def print_dice(dice):
"""Return a three-line string of all the dice."""
rows = ["|", "|", "|"]
for i in dice:
rows[0] += EYES[0] if i == 1 else \
EYES[2] if i < 4 else \
EYES[4]
rows[1] += EYES[4] if i == 6 else \
EYES[1] if i % 2 == 1 else \
EYES[0]
rows[2] += EYES[0] if i == 1 else \
EYES[3] if i < 4 else \
EYES[4]
print('\n'.join(rows))
def main():
display_help()
play()
if __name__ == '__main__':
main()
结论
我希望您不要被吓到答案的长度或内容的长度,因为您的代码看起来确实不错并且可以正常工作。但是有一些事情可以使它变得更加Pythonic,并且我希望您在这里获得了更多Python之美的要旨。
#2 楼
我将尝试通过您的代码逐个函数地进行操作。但是首先要对源代码进行一般性注释:在Python(以及大多数其他编程语言)中,最佳做法是为每个代码都使用简短的文档字符串功能。简短说明该功能可以满足您的情况。
display_help()
:打印帮助消息时,请使用
sep
- print
功能。如果您定义关键字参数,PEP8建议不要在=
符号周围留空格。Python允许您使用封闭
""" ... """
来定义多行字符串,该字符串应保留换行符。应在帮助文本中添加一个docstring。考虑
main
方法或模块文档字符串。这将使信息可供任何可能将其用作模块而不是脚本的人使用。play()
:从我的观点来看,除了这里我不多说从缺少的文档字符串(和
;
)中删除。您可以在play_once
中掷骰子,因为在其他任何地方都不需要该值。get_dice_result()
:您可以使用列表推导来节省您的键入时间。
def get_dice_result():
"""Roll the dice five times"""
return [get_dice_roll() for _ in range(5)]
get_dice_roll()
:random
模块具有功能randint(a, b)
,根据文档,它是randrange(a, b+1)
的别名。您的函数可以重写为def get_dice_roll():
"""Roll a dice once"""
return random.randint(1, 6)
play_once(results)
:关键字参数周围不应有空格(请参见上文)。
可以在这里掷骰子。除了使您不必一次传递数组外,我发现该分区更加直观。
print("Incorrect. The answer is ", answer, ".", sep = "")
可以重写为print("Incorrect. The answer is {}.".format(answer))
。get_guess()
:没有指定异常的
except
可能会产生一些您不希望出现的副作用。没有类型的except
也将捕获KeyboardInterrupt
,依此类推。有关更多信息,请参见此SO答案。像except ValueError:
# the input could not be parsed as int
print("\nOops! That is not a number. Try again: ")
except (EOFError, SyntaxError):
# nothing entered
print("\nOops! You did not enter anything. Try again: ")
会更好。
get_answer(dice)
:return语句中有分号。
print_dice(dice)
:打印语句可以重写为
print('{rows[0]:}\n{rows[1]:}\n{rows[2]:}'.format(rows=rows))
,但是您的语句更短,更清晰。
如果您保留代码,请不要忘记删除关键字参数周围的空格。
main()
:一个小的文档字符串会很好。
最后的注释
作为最终结果,我会说您的代码很不错阅读。在函数中添加一些文档字符串将进一步提高检查的便利性。我个人建议查看一下PyLint(http://www.pylint.org/),它可以自动执行大多数样式检查(是的,也可以进行
;
处理)。我在所有项目中都使用了它,这使编写干净的代码更加容易。此外,它将执行一些静态代码分析。我希望我的反馈意见对您有用,并能使您在编码中保持乐趣。
评论
\ $ \ begingroup \ $
嗨!欢迎来到代码审查。出色的工作,第一个答案!希望您回来并写一些更多的答案,并可能也提出一个问题!
\ $ \ endgroup \ $
–TheCoffeeCup
15年12月17日在1:51
#3 楼
程序的轮廓通常很好,因此我将逐个功能进行处理。display_help
为了便于阅读,请将
"""triple-quoted string"""
与format()
结合使用。请注意,这意味着应该更改print_dice()
来返回字符串。def display_help():
example = roll_dice()
print("""The name of the game is Pythons Around the Rose.
The name is important. I will roll five dice,
and I will tell you how many pythons there are.
For example:
{dice}
Will result in {answer}.
If you answer correctly 8 times in a row, you
will be declared a "Pytentate of the Rose".""".format(
dice=format_dice(example), answer=get_answer(example)
))
play
请使用适当的
while
条件。def play():
streak = 0
while streak < 8:
if play_once():
streak += 1
else:
streak = 0
print('You are now declared a "Pytentate of the Rose"!')
print("Thank you for playing!")
get_dice_result
和get_dice_roll
名称有点不对。您什么都不买;您正在产生新的结果。另外,请注意,“骰子”是“骰子”的复数形式。
您可以将两个函数组合为一个。
def roll_dice(num_dice=5):
return [1 + random.randrange(6) for _ in range(num_dice)]
play_once
play_once()
接受results
作为参数很奇怪。我希望模压辊将成为此功能的一部分。correct_answer
比answer
更精确的名称,因为“ answer”还可以引用用户的输入。def play_once():
dice = roll_dice()
print("How many pythons here?")
print(format_dice(dice))
correct_answer = get_answer(dice)
if get_guess() == correct_answer:
print("Correct!")
return True
print("Incorrect. The answer is {}.".format(correct_answer))
return False
get_guess
捕获所有异常是一个坏主意,因为异常处理程序的调用目的可能超出其预期目的。在这种情况下,它还会捕获
EOFError
和KeyboardInterrupt
,使用户无法选择退出游戏。 (如果要处理EOFError
和KeyboardInterrupt
,建议将这些处理程序放在main()
中。)guess
和valid
变量是没有意义的。def get_guess():
while True:
try:
return int(input("> "))
except TypeError:
print("\nOops! That is not a number. Try again: ")
get_answer
这更简洁,但这只是味道。
def get_answer(dice):
return sum(2 if n == 3 else
4 if n == 5 else
0
for n in dice)
print_dice
我就像您的源代码看起来像是人脸图像。重复的0、1、2有点乏味。这是您可以考虑的另一种方法。
def format_dice(dice):
FACES = [None, [
" |",
" . |",
" |",
], [
" . |",
" |",
" . |",
], [
" . |",
" . |",
" . |",
], [
" . . |",
" |",
" . . |",
], [
" . . |",
" . |",
" . . |",
], [
" . . |",
" . . |",
" . . |",
]
]
return "\n".join(
''.join(row) for row in zip('|||', *(FACES[n] for n in dice))
)
评论
\ $ \ begingroup \ $
我不同意的一件事是尝试使用print_dice中的一系列if语句清理骰子字符串。尽管比以前的代码短,但是它也很难阅读并且掩盖了代码的意图。
\ $ \ endgroup \ $
–今天轮到
2015年12月17日19:48
\ $ \ begingroup \ $
number_of_pythons函数的建议:使用.count()方法。我认为如果您这样写,会更清楚:return 2 * dice.count(3)+ 4 * dice.count(5)
\ $ \ endgroup \ $
– oliverpool
16年1月14日在10:16
\ $ \ begingroup \ $
@oliverpool我明白了你的意思,但是再一次,它将遍历骰子数组两次。只要列出的清单很小,这并不重要。但是我也很高兴有机会展示高级的列表理解能力。
\ $ \ endgroup \ $
– Holroy
16年1月14日在21:57
\ $ \ begingroup \ $
将两次遍历骰子数组,是的,但是算法仍然是线性的。此外,根据.count的实际实现,它可能会更快(分支可能非常昂贵)。在审查方面,我发现.count比嵌套三元运算符更容易阅读
\ $ \ endgroup \ $
– oliverpool
16年1月15日在16:08