用JupyterNotebook教Python

有了 Jupyter、PyHamcrest,用一点测试的代码把它们连在一起,你就可以教任何适用于单元测试的 Python 内容。

成都创新互联专注于来凤网站建设服务及定制,我们拥有丰富的企业做网站经验。 热诚为您提供来凤营销型网站建设,来凤网站制作、来凤网页设计、来凤网站官网定制、微信小程序服务,打造来凤网络公司原创品牌,更为您提供来凤网站排名全网营销落地服务。

关于 Ruby 社区的一些事情一直让我印象深刻,其中两个例子是对测试的承诺和对易于上手的强调。这两方面最好的例子是 Ruby Koans,在这里你可以通过修复测试来学习 Ruby。

要是我们能把这些神奇的工具也用于 Python,我们应该可以做得更好。是的,使用 Jupyter Notebook、PyHamcrest,再加上一点类似于胶带的粘合代码,我们可以做出一个包括教学、可工作的代码和需要修复的代码的教程。

首先,需要一些“胶布”。通常,你会使用一些漂亮的命令行测试器来做测试,比如 pytest 或 virtue。通常,你甚至不会直接运行它。你使用像 tox 或 nox 这样的工具来运行它。然而,对于 Jupyter 来说,你需要写一小段粘合代码,可以直接在其中运行测试。

幸运的是,这个代码又短又简单:

 
 
 
  1. import unittest
  2.  
  3. def run_test(klass):
  4.     suite = unittest.TestLoader().loadTestsFromTestCase(klass)
  5.     unittest.TextTestRunner(verbosity=2).run(suite)
  6.     return klass

现在,装备已经就绪,可以进行第一次练习了。

在教学中,从一个简单的练习开始,建立信心总是一个好主意。

那么,让我们来修复一个非常简单的测试:

 
 
 
  1. @run_test
  2. class TestNumbers(unittest.TestCase):
  3. def test_equality(self):
  4. expected_value = 3 # 只改这一行
  5. self.assertEqual(1+1, expected_value)
 
 
 
  1. test_equality (__main__.TestNumbers) ... FAIL
  2. ======================================================================
  3. FAIL: test_equality (__main__.TestNumbers)
  4. ----------------------------------------------------------------------
  5. Traceback (most recent call last):
  6. File "", line 6, in test_equality
  7. self.assertEqual(1+1, expected_value)
  8. AssertionError: 2 != 3
  9. ----------------------------------------------------------------------
  10. Ran 1 test in 0.002s
  11. FAILED (failures=1)

“只改这一行” 对学生来说是一个有用的标记。它准确地表明了需要修改的内容。否则,学生可以通过将第一行改为 return 来修复测试。

在这种情况下,修复很容易:

 
 
 
  1. @run_test
  2. class TestNumbers(unittest.TestCase):
  3. def test_equality(self):
  4. expected_value = 2 # 修复后的代码行
  5. self.assertEqual(1+1, expected_value)
 
 
 
  1. test_equality (__main__.TestNumbers) ... ok
  2. ----------------------------------------------------------------------
  3. Ran 1 test in 0.002s
  4. OK

然而,很快,unittest 库的原生断言将被证明是不够的。在 pytest 中,通过重写 assert 中的字节码来解决这个问题,使其具有神奇的属性和各种启发式方法。但这在 Jupyter notebook 中就不容易实现了。是时候挖出一个好的断言库了:PyHamcrest。

 
 
 
  1. from hamcrest import *
  2. @run_test
  3. class TestList(unittest.TestCase):
  4. def test_equality(self):
  5. things = [1,
  6. 5, # 只改这一行
  7. 3]
  8. assert_that(things, has_items(1, 2, 3))
 
 
 
  1. test_equality (__main__.TestList) ... FAIL
  2. ======================================================================
  3. FAIL: test_equality (__main__.TestList)
  4. ----------------------------------------------------------------------
  5. Traceback (most recent call last):
  6. File "", line 8, in test_equality
  7. assert_that(things, has_items(1, 2, 3))
  8. AssertionError:
  9. Expected: (a sequence containing <1> and a sequence containing <2> and a sequence containing <3>)
  10. but: a sequence containing <2> was <[1, 5, 3]>
  11. ----------------------------------------------------------------------
  12. Ran 1 test in 0.004s
  13. FAILED (failures=1)

PyHamcrest 不仅擅长灵活的断言,它还擅长清晰的错误信息。正因为如此,问题就显而易见了。[1, 5, 3] 不包含 2,而且看起来很丑:

 
 
 
  1. @run_test
  2. class TestList(unittest.TestCase):
  3. def test_equality(self):
  4. things = [1,
  5. 2, # 改完的行
  6. 3]
  7. assert_that(things, has_items(1, 2, 3))
 
 
 
  1. test_equality (__main__.TestList) ... ok
  2. ----------------------------------------------------------------------
  3. Ran 1 test in 0.001s
  4. OK

使用 Jupyter、PyHamcrest 和一点测试的粘合代码,你可以教授任何适用于单元测试的 Python 主题。

例如,下面可以帮助展示 Python 从字符串中去掉空白的不同方法之间的差异。

 
 
 
  1. source_string = " hello world "
  2.  
  3. @run_test
  4. class TestList(unittest.TestCase):
  5. # 这是个赠品:它可以工作!
  6. def test_complete_strip(self):
  7. result = source_string.strip()
  8. assert_that(result,
  9. all_of(starts_with("hello"), ends_with("world")))
  10.  
  11. def test_start_strip(self):
  12. result = source_string # 只改这一行
  13. assert_that(result,
  14. all_of(starts_with("hello"), ends_with("world ")))
  15.  
  16. def test_end_strip(self):
  17. result = source_string # 只改这一行
  18. assert_that(result,
  19. all_of(starts_with(" hello"), ends_with("world")))
 
 
 
  1. test_complete_strip (__main__.TestList) ... ok
  2. test_end_strip (__main__.TestList) ... FAIL
  3. test_start_strip (__main__.TestList) ... FAIL
  4. ======================================================================
  5. FAIL: test_end_strip (__main__.TestList)
  6. ----------------------------------------------------------------------
  7. Traceback (most recent call last):
  8. File "", line 19, in test_end_strip
  9. assert_that(result,
  10. AssertionError:
  11. Expected: (a string starting with ' hello' and a string ending with 'world')
  12. but: a string ending with 'world' was ' hello world '
  13. ======================================================================
  14. FAIL: test_start_strip (__main__.TestList)
  15. ----------------------------------------------------------------------
  16. Traceback (most recent call last):
  17. File "", line 14, in test_start_strip
  18. assert_that(result,
  19. AssertionError:
  20. Expected: (a string starting with 'hello' and a string ending with 'world ')
  21. but: a string starting with 'hello' was ' hello world '
  22. ----------------------------------------------------------------------
  23. Ran 3 tests in 0.006s
  24. FAILED (failures=2)

理想情况下,学生们会意识到 .lstrip() 和 .rstrip() 这两个方法可以满足他们的需要。但如果他们不这样做,而是试图到处使用 .strip() 的话:

 
 
 
  1. source_string = " hello world "
  2.  
  3. @run_test
  4. class TestList(unittest.TestCase):
  5. # 这是个赠品:它可以工作!
  6. def test_complete_strip(self):
  7. result = source_string.strip()
  8. assert_that(result,
  9. all_of(starts_with("hello"), ends_with("world")))
  10.  
  11. def test_start_strip(self):
  12. result = source_string.strip() # 改完的行
  13. assert_that(result,
  14. all_of(starts_with("hello"), ends_with("world ")))
  15.  
  16. def test_end_strip(self):
  17. result = source_string.strip() # 改完的行
  18. assert_that(result,
  19. all_of(starts_with(" hello"), ends_with("world")))
 
 
 
  1. test_complete_strip (__main__.TestList) ... ok
  2. test_end_strip (__main__.TestList) ... FAIL
  3. test_start_strip (__main__.TestList) ... FAIL
  4. ======================================================================
  5. FAIL: test_end_strip (__main__.TestList)
  6. ----------------------------------------------------------------------
  7. Traceback (most recent call last):
  8. File "", line 19, in test_end_strip
  9. assert_that(result,
  10. AssertionError:
  11. Expected: (a string starting with ' hello' and a string ending with 'world')
  12. but: a string starting with ' hello' was 'hello world'
  13. ======================================================================
  14. FAIL: test_start_strip (__main__.TestList)
  15. ----------------------------------------------------------------------
  16. Traceback (most recent call last):
  17. File "", line 14, in test_start_strip
  18. assert_that(result,
  19. AssertionError:
  20. Expected: (a string starting with 'hello' and a string ending with 'world ')
  21. but: a string ending with 'world ' was 'hello world'
  22. ----------------------------------------------------------------------
  23. Ran 3 tests in 0.007s
  24. FAILED (failures=2)

他们会得到一个不同的错误信息,显示去除了过多的空白:

 
 
 
  1. source_string = " hello world "
  2.  
  3. @run_test
  4. class TestList(unittest.TestCase):
  5. # 这是个赠品:它可以工作!
  6. def test_complete_strip(self):
  7. result = source_string.strip()
  8. assert_that(result,
  9. all_of(starts_with("hello"), ends_with("world")))
  10.  
  11. def test_start_strip(self):
  12. result = source_string.lstrip() # Fixed this line
  13. assert_that(result,
  14. all_of(starts_with("hello"), ends_with("world ")))
  15.  
  16. def test_end_strip(self):
  17. result = source_string.rstrip() # Fixed this line
  18. assert_that(result,
  19. all_of(starts_with(" hello"), ends_with("world")))
 
 
 
  1. test_complete_strip (__main__.TestList) ... ok
  2. test_end_strip (__main__.TestList) ... ok
  3. test_start_strip (__main__.TestList) ... ok
  4. ----------------------------------------------------------------------
  5. Ran 3 tests in 0.005s
  6. OK

在一个比较真实的教程中,会有更多的例子和更多的解释。这种使用 Jupyter Notebook 的技巧,有的例子可以用,有的例子需要修正,可以用于实时教学,可以用于视频课,甚至,可以用更多的其它零散用途,让学生自己完成一个教程。

分享文章:用JupyterNotebook教Python
网址分享:http://www.shufengxianlan.com/qtweb/news10/276860.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联