geekdoc-python-zh/docs/pythonlibrary/python-code-kata-fizzbuzz.md

14 KiB
Raw Blame History

Python 代码 Kata: Fizzbuzz

原文:https://www.blog.pythonlibrary.org/2019/09/18/python-code-kata-fizzbuzz/

代码形是计算机程序员练习编码的一种有趣方式。他们也经常被用来学习如何在编写代码时实现测试驱动开发(TDD)。其中一个流行的编程招式叫做 FizzBuzz 。这也是计算机程序员普遍的面试问题。

FizzBuzz 背后的概念如下:

  • 写一个程序打印数字 1-100每一行一个
  • 对于每个是 3 的倍数的数字打印“Fizz ”,而不是数字
  • 对于每个是 5 的倍数的数字打印“Buzz”而不是数字
  • 对于每个都是 3 和 5 的倍数的数字打印“FizzBuzz”而不是数字

现在你知道你需要写什么了,你可以开始了!


创建工作空间

第一步是在您的机器上创建一个工作空间或项目文件夹。例如,你可以创建一个 katas 文件夹,里面有一个 fizzbuzz

下一步是安装源代码控制程序。最流行的一个是 Git但是您也可以使用像 Mercurial 这样的东西。出于本教程的目的,您将使用 Git。可以从 Git 网站获取。

如果你是 Windows 用户,现在打开一个终端或者运行 cmd.exe。然后在终端中导航到您的 fizzbuzz 文件夹。你可以使用 cd 命令来实现。进入文件夹后,运行以下命令:

git init

这将把 fizzbuzz 文件夹初始化为 Git 存储库。你在 fizzbuzz 文件夹中添加的任何文件或文件夹现在都可以添加到 Git 并进行版本控制。


嘶嘶测试

为了简单起见,您可以在 fizzbuzz 文件夹中创建您的测试文件。许多人会将他们的测试保存在名为测试测试的子文件夹中,并告诉他们的测试运行人员将顶层文件夹添加到 sys.path 中,以便测试可以导入它。

**注意:**如果你需要温习如何使用 Python 的 unittest 库,那么你可能会发现 Python 3 测试:unittest 简介很有帮助。

继续在你的 fizzbuzz 文件夹中创建一个名为 test_fizzbuzz.py 的文件。

现在,在 Python 文件中输入以下内容:


import fizzbuzz
import unittest

class TestFizzBuzz(unittest.TestCase):

    def test_multiple_of_three(self):
       self.assertEqual(fizzbuzz.process(6), 'Fizz')

if __name__ == '__main__':
    unittest.main()

Python 附带了内置的 unittest 库。要使用它,你需要做的就是导入它并子类化 unittest。测试用例。然后,您可以创建一系列函数来表示您想要运行的测试。

请注意,您还导入了 fizzbuzz 模块。您还没有创建那个模块,所以当您运行这个测试代码时,您将收到一个 ModuleNotFoundError 。除了导入之外,您甚至不需要添加任何代码就可以创建这个文件,并且测试会失败。但是为了完整起见,您继续断言 fizzbuzz.process(6) 返回正确的字符串。

修复方法是创建一个空的 fizzbuzz.py 文件。这只会修复 ModuleNotFoundError ,但是它将允许您运行测试并查看其输出。

您可以通过以下方式运行测试:

python test_fizzbuzz.py

输出将如下所示:

`ERROR: test_multiple_of_three (main.TestFizzBuzz)

Traceback (most recent call last): File "/Users/michael/Dropbox/code/fizzbuzz/test_fizzbuzz.py", line 7, in test_multiple_of_three self.assertEqual(fizzbuzz.process(6), 'Fizz') AttributeError: module 'fizzbuzz' has no attribute 'process'`

在 0.001 秒内完成一项测试

失败(错误=1)

所以这告诉你你的 fizzbuzz 模块缺少一个叫做进程的属性。

您可以通过在您的 fizzbuzz.py 文件中添加一个 process() 函数来解决这个问题:


def process(number):
    if number % 3 == 0:
        return 'Fizz'

该函数接受一个数字,并使用模数运算符将该数字除以 3并检查是否有余数。如果没有余数那么你知道这个数可以被 3 整除所以你可以返回字符串“Fizz”。

现在,当您运行测试时,输出应该如下所示:

`.

Ran 1 test in 0.000s`

好的

上面第一行中的句点表示您运行了一个测试,它通过了。

让我们快速回到这里。当测试失败时,它被认为处于“红色”状态。当测试通过时,这是一个“绿色”状态。这指的是红色/绿色/重构的测试驱动开发(TDD)咒语。大多数开发人员会通过创建一个失败的测试(红色)来开始一个新项目。然后他们会编写代码使测试通过,通常是以最简单的方式(绿色)。

当您的测试为绿色时,这是提交您的测试和代码变更的好时机。这允许您拥有一段可以回滚的工作代码。现在,您可以编写一个新的测试或重构代码来使它变得更好,而不用担心您会丢失您的工作,因为现在您有一个简单的方法来回滚到代码的前一个版本。

要提交代码,可以执行以下操作:

git add fizzbuzz.py test_fizzbuzz.py git commit -m "First commit"

第一个命令将添加两个新文件。不需要提交 。pyc 文件,只是 Python 文件。有一个方便的文件叫做*。gitignore** 您可以添加到您的 Git 存储库中,您可以使用它来排除某些文件类型或文件夹,如* . pyc。Github 有一些默认的 gitignore 文件,用于各种语言,如果您想查看示例,您可以获得这些文件。

第二个命令是如何将代码提交到本地存储库。“-m”表示消息后面是关于您正在提交的更改的描述性消息。如果你也想把你的修改保存到 Github(这对于备份来说很好),你应该看看这篇文章。

现在我们准备编写另一个测试了!


嗡嗡声测试

你可以写的第二个测试可以是 5 的倍数。要添加一个新的测试,您可以在 TestFizzBuzz 类中创建另一个方法:


import fizzbuzz
import unittest

class TestFizzBuzz(unittest.TestCase):

    def test_multiple_of_three(self):
        self.assertEqual(fizzbuzz.process(6), 'Fizz')

    def test_multiple_of_five(self):
        self.assertEqual(fizzbuzz.process(20), 'Buzz')

if __name__ == '__main__':
    unittest.main()

这一次,您希望使用只能被 5 整除的数字。当您调用 fizzbuzz.process()应该会得到“buzz”的返回。但是当您运行测试时您将收到以下内容:

`F.

FAIL: test_multiple_of_five (main.TestFizzBuzz)

Traceback (most recent call last): File "test_fizzbuzz.py", line 10, in test_multiple_of_five self.assertEqual(fizzbuzz.process(20), 'Buzz') AssertionError: None != 'Buzz'`

在 0.000 秒内运行 2 次测试

失败(失败次数=1)

哎呀!现在,您的代码使用模数运算符来检查除以 3 后的余数。如果数字 20 有余数,这个语句就不会运行。函数的默认返回值是 None ,所以这就是为什么你会得到上面的失败。

继续将 process() 函数更新如下:


def process(number):
    if number % 3 == 0:
        return 'Fizz'
    elif number % 5 == 0:
        return 'Buzz'

现在你可以用 3 和 5 来检查余数。当您这次运行测试时,输出应该如下所示:

`..

Ran 2 tests in 0.000s`

好的

耶!您的测试通过,现在是绿色的!这意味着您可以将这些更改提交到您的 Git 存储库中。

现在你已经准备好为 FizzBuzz 添加一个测试了!


嘶嘶声测试

你能写的下一个测试将是当你想要得到“FizzBuzz”的时候。你可能还记得只要这个数能被 3 和 5 整除,你就会得到 FizzBuzz。继续添加第三个测试:


import fizzbuzz
import unittest

class TestFizzBuzz(unittest.TestCase):

    def test_multiple_of_three(self):
        self.assertEqual(fizzbuzz.process(6), 'Fizz')

    def test_multiple_of_five(self):
        self.assertEqual(fizzbuzz.process(20), 'Buzz')

    def test_fizzbuzz(self):
        self.assertEqual(fizzbuzz.process(15), 'FizzBuzz')

if __name__ == '__main__':
    unittest.main()

对于这个测试, test_fizzbuzz ,您要求您的程序处理数字 15。这应该还不能正常工作但是继续运行测试代码来检查:

`F..

FAIL: test_fizzbuzz (main.TestFizzBuzz)

Traceback (most recent call last): File "test_fizzbuzz.py", line 13, in test_fizzbuzz self.assertEqual(fizzbuzz.process(15), 'FizzBuzz') AssertionError: 'Fizz' != 'FizzBuzz'`

在 0.000 秒内运行了 3 次测试

失败(失败次数=1)

进行了三次测试,只有一次失败。你现在回到红色。这次的错误是**‘嘶嘶’!= 'FizzBuzz'** 而不是拿 None 和 FizzBuzz 比较。原因是你的代码检查 15 是否能被 3 整除所以它返回“Fizz”。

因为这不是您想要发生的,所以您将需要更新您的代码,在检查 3:


def process(number):
    if number % 3 == 0 and number % 5 == 0:
        return 'FizzBuzz'
    elif number % 3 == 0:
        return 'Fizz'
    elif number % 5 == 0:
        return 'Buzz'

在这里,首先对 3 和 5 进行整除检查。然后像以前一样检查另外两个。

现在,如果您运行您的测试,您应该得到以下输出:

`...

Ran 3 tests in 0.000s`

好的

到目前为止一切顺利。然而,你没有返回不能被 3 或 5 整除的数字的代码。该进行另一项测试了!


最终测试

您的代码需要做的最后一件事是,当该数除以 3 和 5 后还有余数时,返回该数。让我们用几种不同的方法来测试它:


import fizzbuzz
import unittest

class TestFizzBuzz(unittest.TestCase):

    def test_multiple_of_three(self):
        self.assertEqual(fizzbuzz.process(6), 'Fizz')

    def test_multiple_of_five(self):
        self.assertEqual(fizzbuzz.process(20), 'Buzz')

    def test_fizzbuzz(self):
        self.assertEqual(fizzbuzz.process(15), 'FizzBuzz')

    def test_regular_numbers(self):
        self.assertEqual(fizzbuzz.process(2), 2)
        self.assertEqual(fizzbuzz.process(98), 98)

if __name__ == '__main__':
    unittest.main()

对于这个测试,您将使用 test_regular_numbers() 测试来测试正常数字 2 和 98。这些数字在被 3 或 5 除时总会有余数,所以它们应该被返回。

当您现在运行测试时,您应该得到类似这样的结果:

`...F

FAIL: test_regular_numbers (main.TestFizzBuzz)

Traceback (most recent call last): File "test_fizzbuzz.py", line 16, in test_regular_numbers self.assertEqual(fizzbuzz.process(2), 2) AssertionError: None != 2`

在 0.000 秒内运行了 4 次测试

失败(失败次数=1)

这一次,您又回到了将 None 与 number 进行比较,这就是您可能怀疑的输出。

继续更新**进程()**函数,如下所示:


def process(number):
    if number % 3 == 0 and number % 5 == 0:
        return 'FizzBuzz'
    elif number % 3 == 0:
        return 'Fizz'
    elif number % 5 == 0:
        return 'Buzz'
    else:
        return number

那很容易!此时您需要做的就是添加一个返回数字的 else 语句。

现在,当您运行测试时,它们应该都通过了:

`....

Ran 4 tests in 0.000s`

好的

干得好!现在你的代码工作了。您可以通过将以下内容添加到您的 fizzbuzz.py 模块来验证它是否适用于 1-100 的所有数字:


if __name__ == '__main__':
    for i in range(1, 101):
        print(process(i))

现在,当您自己使用 python fizzbuzz.py 运行 fizzbuzz 时,您应该会看到本教程开头指定的适当输出。

这是提交您的代码并将其推送到云的好时机。


包扎

现在你知道了使用测试驱动开发来驱动你解决编码问题的基础。Python 的 unittest 模块拥有比这篇简短教程中所涵盖的更多类型的断言和功能。您还可以修改本教程以使用 pytest这是另一个流行的第三方 Python 包,您可以用它来代替 Python 自己的 unittest 模块。

进行这些测试的好处是,现在您可以重构代码,并通过运行测试来验证您没有破坏任何东西。这也允许您在不破坏现有功能的情况下更容易地添加新功能。只要确保在添加更多特性时添加更多测试。


相关阅读