14.3. roman.py, 第 3 阶段

现在 toRoman 对于有效的输入 (13999 整数) 已能正确工作,是正确处理那些无效输入 (任何其他输入) 的时候了。

例 14.6. roman3.py

这个文件可以在例子目录下的 py/roman/stage3/ 目录中找到。

如果您还没有下载本书附带的样例程序, 可以 下载本程序和其他样例程序

"""Convert to and from Roman numerals"""

#Define exceptions
class RomanError(Exception): pass
class OutOfRangeError(RomanError): pass
class NotIntegerError(RomanError): pass
class InvalidRomanNumeralError(RomanError): pass

#Define digit mapping
romanNumeralMap = (('M',  1000),
                   ('CM', 900),
                   ('D',  500),
                   ('CD', 400),
                   ('C',  100),
                   ('XC', 90),
                   ('L',  50),
                   ('XL', 40),
                   ('X',  10),
                   ('IX', 9),
                   ('V',  5),
                   ('IV', 4),
                   ('I',  1))

def toRoman(n):
    """convert integer to Roman numeral"""
    if not (0 < n < 4000):                                             
        raise OutOfRangeError, "number out of range (must be 1..3999)" 
    if int(n) <> n:                                                    
        raise NotIntegerError, "non-integers can not be converted"

    result = ""                                                        
    for numeral, integer in romanNumeralMap:
        while n >= integer:
            result += numeral
            n -= integer
    return result

def fromRoman(s):
    """convert Roman numeral to integer"""
    pass
这个写法很 Pythonic:一次进行多个比较。这等价于if not ((0 < n) and (n < 4000)),但是更容易让人理解。这是在进行范围检查,可以将过大的数、负数和零查出来。
你使用 raise 语句引发自己的异常。你可以引发任何内建异常或者已定义的自定义异常。第二个参数是可选的,如果给定,则会在异常未被处理时显示于追踪信息 (trackback) 之中。
这是一个非整数检查。非整数无法转化为罗马数字表示。
函数的其他部分未被更改。

例 14.7. 观察 toRoman 如何处理无效输入

>>> import roman3
>>> roman3.toRoman(4000)
Traceback (most recent call last):
  File "<interactive input>", line 1, in ?
  File "roman3.py", line 27, in toRoman
    raise OutOfRangeError, "number out of range (must be 1..3999)"
OutOfRangeError: number out of range (must be 1..3999)
>>> roman3.toRoman(1.5)
Traceback (most recent call last):
  File "<interactive input>", line 1, in ?
  File "roman3.py", line 29, in toRoman
    raise NotIntegerError, "non-integers can not be converted"
NotIntegerError: non-integers can not be converted

例 14.8. 用 romantest3.py 测试 roman3.py 的结果

fromRoman should only accept uppercase input ... FAIL
toRoman should always return uppercase ... ok
fromRoman should fail with malformed antecedents ... FAIL
fromRoman should fail with repeated pairs of numerals ... FAIL
fromRoman should fail with too many repeated numerals ... FAIL
fromRoman should give known result with known input ... FAIL
toRoman should give known result with known input ... ok 
fromRoman(toRoman(n))==n for all n ... FAIL
toRoman should fail with non-integer input ... ok        
toRoman should fail with negative input ... ok           
toRoman should fail with large input ... ok
toRoman should fail with 0 input ... ok
toRoman 仍然能通过已知值测试,这很令人鼓舞。所有第 2 阶段通过的测试仍然能通过,这说明新的代码没有对原有代码构成任何负面影响。
更令人振奋的是所有的无效输入测试现在都通过了。testNonInteger 这个测试能够通过是因为有了 int(n) <> n 检查。当一个非整数传递给 toRoman 时,int(n) <> n 检查出问题并引发 NotIntegerError 异常,这正是 testNonInteger 所期待的。
testNegative 这个测试能够通过是因为 not (0 < n < 4000) 检查引发了 testNegative 期待的 OutOfRangeError 异常。

======================================================================
FAIL: fromRoman should only accept uppercase input
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage3\romantest3.py", line 156, in testFromRomanCase
    roman3.fromRoman, numeral.lower())
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: InvalidRomanNumeralError
======================================================================
FAIL: fromRoman should fail with malformed antecedents
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage3\romantest3.py", line 133, in testMalformedAntecedent
    self.assertRaises(roman3.InvalidRomanNumeralError, roman3.fromRoman, s)
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: InvalidRomanNumeralError
======================================================================
FAIL: fromRoman should fail with repeated pairs of numerals
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage3\romantest3.py", line 127, in testRepeatedPairs
    self.assertRaises(roman3.InvalidRomanNumeralError, roman3.fromRoman, s)
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: InvalidRomanNumeralError
======================================================================
FAIL: fromRoman should fail with too many repeated numerals
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage3\romantest3.py", line 122, in testTooManyRepeatedNumerals
    self.assertRaises(roman3.InvalidRomanNumeralError, roman3.fromRoman, s)
  File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
    raise self.failureException, excName
AssertionError: InvalidRomanNumeralError
======================================================================
FAIL: fromRoman should give known result with known input
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage3\romantest3.py", line 99, in testFromRomanKnownValues
    self.assertEqual(integer, result)
  File "c:\python21\lib\unittest.py", line 273, in failUnlessEqual
    raise self.failureException, (msg or '%s != %s' % (first, second))
AssertionError: 1 != None
======================================================================
FAIL: fromRoman(toRoman(n))==n for all n
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\docbook\dip\py\roman\stage3\romantest3.py", line 141, in testSanity
    self.assertEqual(integer, result)
  File "c:\python21\lib\unittest.py", line 273, in failUnlessEqual
    raise self.failureException, (msg or '%s != %s' % (first, second))
AssertionError: 1 != None
----------------------------------------------------------------------
Ran 12 tests in 0.401s

FAILED (failures=6) 
你已将失败降至 6 个,时时彩计划软件公式:而且它们都是关于 fromRoman 的:已知值测试、三个独立的无效输入测试,大小写检查和完备性检查。这意味着 toRoman 通过了所有可以独立通过的测试 (完备性测试也测试它,但需要 fromRoman 编写后一起测试)。这就是说,你应该停止对 toRoman 的代码编写。不必再推敲,不必再做额外的检查 “恰到好处”。停下来吧!现在,别再敲键盘了。
全面的单元测试能够告诉你的最重要的事情是什么时候停止编写代码。当一个函数的所有单元测试都通过了,停止编写这个函数。一旦整个模块的单元测试通过了,停止编写这个模块。
诺亚时时彩 时时彩直选终极版 拉菲时时彩平台总代理 内蒙时时彩11选5走势图 时时彩投注平台推荐
太原时时彩玩法 时时彩软件百度网盘 易语言时时彩软件源码 聚宝盆时时彩正版软件 时时彩助赢彩票
时时彩软件urssc 杏彩时时彩平台1990奖金 飞翔时时彩软件手机版 重庆时时彩平刷论坛lm0 合乐888时时彩平台
什么时时彩软件最好 时时彩冷热遗漏软件 宝盈娱乐平台违法么 天机时时彩软件 诺亚时时彩平台官网
灵灵发北京pk10破解版 北京赛车新玩 pc蛋蛋幸运28凤凰预测 北京时时彩的官网 幸运农场技巧
河南快3 黑龙江时时彩11选5 内蒙古十一选五遗漏 江西11选5最大遗漏期 海南飞鱼投注技巧
彩票365 吉林快三今天推荐号码 新疆时时彩 巴黎人 22选5河南福彩网
河南福彩快3号码走势图 大乐透走基本走势图 天津时时彩开奖数据 体彩十一选五软件下载﹤计划﹥ 白小姐一肖中特