基于我的《玫瑰周围的Java花瓣》,我认为再次用另一种语言编写内容是一种好习惯。

因为该语言是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)]它实现了三个不错的功能:


列表理解,它基于内部生成器构建列表,即[ ... ]

在生成器中使用forx for x in range(y),它将for循环压缩到表达式中。如果我们不真正关心数字,而只希望执行0循环前面的代码,则此函数会将y-1_的数字返回

使用for。 >


生成器表达式-最后一项的for循环也可以在列表推导之外使用,即sum(x for x in range(y)),它将对从0y-1的所有数字求和。

列表推导和生成器之间的一个主要区别,是列表推导实际上构建了整个列表,而生成器在后台工作,并在需要时为您提供了另一个元素。

在求和示例中,这意味着如果您拥有y=1000000,分配的内存不能超过1个int,而如果使用列表推导,则会分配整个100万个元素的列表,然后对其求和。


Python三元数,即a = b if c else d –这可能要花一些时间才能习惯,但实际上它读起来很不错:如果条件a为真,则将b的值设置为c

免责声明:在我的重构代码中,这有点丑陋,因为我将这种构造的多个结合在一起。但是,它们还是有道理的。我在dnumber_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命令,执行相同的操作-在ifget_answer()中,您都有print_dice()链,其中块类型为一样。通常这表明您可以找到一种更好的处理方法。


if中,我选择了双三元组,进行get_answer()

answer += 2 if i == 3 else 4 if i == 5 else 0中,我使用了一个六面模只有5种不同图案的事实,然后又使用了另一个双三元模从不同选项中进行选择。

免责声明:即使这段代码比您的代码短很多,但这并不意味着它会更好。但这确实显示了另一种方法,并且有多种方法可以打印管芯结果。请参见此处或此处。



使用常量而不是幻数-而不是在代码中隐藏print_dice()5,请在文件顶部使用常量,例如8REQUIRED_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之美的要旨。

评论


\ $ \ 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

#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_resultget_dice_roll


名称有点不对。您什么都不买;您正在产生新的结果。另外,请注意,“骰子”是“骰子”的复数形式。

您可以将两个函数组合为一个。

def roll_dice(num_dice=5):
    return [1 + random.randrange(6) for _ in range(num_dice)]


play_once

play_once()接受results作为参数很奇怪。我希望模压辊将成为此功能的一部分。

correct_answeranswer更精确的名称,因为“ 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

捕获所有异常是一个坏主意,因为异常处理程序的调用目的可能超出其预期目的。在这种情况下,它还会捕获EOFErrorKeyboardInterrupt,使用户无法选择退出游戏。 (如果要处理EOFErrorKeyboardInterrupt,建议将这些处理程序放在main()中。)

guessvalid变量是没有意义的。

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))
    )