【问题标题】:Python - breaking and grouping user inputPython - 打破和分组用户输入
【发布时间】:2016-12-22 14:04:14
【问题描述】:

我正在做一个练习,我在 Learn Python the Hard Way(ex 48) 中遇到过。目的是通过参考我们的词典对用户输入进行分组。我正在使用鼻子来测试我的脚本,但我得到了多个错误。当我进行鼻子测试时,我得到了 6 次失败中的 5 次。我不明白为什么我会收到这些错误。有什么帮助吗?

错误

FAIL: tests.ex48_tests.test_verbs
----------------------------------------------------------------------
Traceback (most recent call last):
  File "c:\python\lib\site-packages\nose\case.py", line 198, in runTest
    self.test(*self.arg)
  File "C:\Python\projects\skeleton2\tests\ex48_tests.py", line 13, in test_verbs
    assert_equal(scan("go").result, [('verb', 'go')])
AssertionError: <bound method scan.result of <ex48.lexicon.scan object at 0x03A8F3F0>> != [('verb', 'go')]

======================================================================
FAIL: tests.ex48_tests.test_stops
----------------------------------------------------------------------
Traceback (most recent call last):
  File "c:\python\lib\site-packages\nose\case.py", line 198, in runTest
    self.test(*self.arg)
  File "C:\Python\projects\skeleton2\tests\ex48_tests.py", line 21, in test_stops
    assert_equal(scan("the").result(), [('stop', 'the')])
AssertionError: Lists differ: [('stop', 'the'), ('error', 'the')] != [('stop', 'the')]

First list contains 1 additional elements.
First extra element 1:
('error', 'the')

- [('stop', 'the'), ('error', 'the')]
+ [('stop', 'the')]

======================================================================
FAIL: tests.ex48_tests.test_noun
----------------------------------------------------------------------
Traceback (most recent call last):
  File "c:\python\lib\site-packages\nose\case.py", line 198, in runTest
    self.test(*self.arg)
  File "C:\Python\projects\skeleton2\tests\ex48_tests.py", line 29, in test_noun
    assert_equal(scan("bear").result(), [('noun', 'bear')])
AssertionError: Lists differ: [('noun', 'bear'), ('error', 'bear')] != [('noun', 'bear')]

First list contains 1 additional elements.
First extra element 1:
('error', 'bear')

- [('noun', 'bear'), ('error', 'bear')]
+ [('noun', 'bear')]

======================================================================
FAIL: tests.ex48_tests.test_numbers
----------------------------------------------------------------------
Traceback (most recent call last):
  File "c:\python\lib\site-packages\nose\case.py", line 198, in runTest
    self.test(*self.arg)
  File "C:\Python\projects\skeleton2\tests\ex48_tests.py", line 35, in test_numbers
    assert_equal(scan("1234").result(), [('number', 1234)])
AssertionError: Lists differ: [('error', '1234')] != [('number', 1234)]

First differing element 0:
('error', '1234')
('number', 1234)

- [('error', '1234')]
?     ---    -    -

+ [('number', 1234)]
?    ++++


======================================================================
FAIL: tests.ex48_tests.test_errors
----------------------------------------------------------------------
Traceback (most recent call last):
  File "c:\python\lib\site-packages\nose\case.py", line 198, in runTest
    self.test(*self.arg)
  File "C:\Python\projects\skeleton2\tests\ex48_tests.py", line 45, in test_errors
    ('noun', 'princess')])
AssertionError: Lists differ: [('no[20 chars]r', 'bear'), ('error', 'IAS'), ('noun', 'princ[24 chars]ss')] != [('no[20 chars]r', 'IAS'), ('noun', 'princess')]

First differing element 1:
('error', 'bear')
('error', 'IAS')

First list contains 2 additional elements.
First extra element 3:
('noun', 'princess')

+ [('noun', 'bear'), ('error', 'IAS'), ('noun', 'princess')]
- [('noun', 'bear'),
-  ('error', 'bear'),
-  ('error', 'IAS'),
-  ('noun', 'princess'),
-  ('error', 'princess')]

----------------------------------------------------------------------
Ran 6 tests in 0.027s

FAILED (failures=5)

lexicon.py

class scan(object):
    dirs = ['north','south','east','west','down','up','left','right','back']
    verbs = ['go','stop','kill','eat']
    stops = ['the','in','of','from','at','it']
    nouns = ['door','princess','bear','cabinet']
    numbers = ['0','1','2','3','4','5','6','7','8','9']

    def __init__(self, user_input):
        self.user_input = user_input

    def result(self):
        words = self.user_input.split()
        results = []

        for item in words:
            if item in scan.dirs:
                result = ('direction', item.lower())
                results.append(result)
            if item in scan.verbs:
                result = ('verb', item.lower())
                results.append(result)
            if item in scan.stops:
                result = ('stop', item.lower())
                results.append(result)
            if item in scan.nouns:
                result =('noun', item.lower())
                results.append(result)
            if item in scan.numbers:
                result = ('number', int(item))
                results.append(result)
            if item not in (scan.dirs or scan.verbs or scan.stops or
                            scan.nouns or scan.numbers):
                result = ('error', item)
                results.append(result)

        return results

lexicon_test.py

from nose.tools import *
from ex48.lexicon import scan


def test_direction():
    assert_equal(scan('north').result(), [('direction', 'north')])
    result = scan("north east south").result()
    assert_equal(result, [('direction', 'north'),
                          ('direction', 'east'),
                          ('direction', 'south')])

def test_verbs():
    assert_equal(scan("go").result, [('verb', 'go')])
    result = scan("go kill eat").result()
    assert_equal(result, [('verb', 'go'),
                          ('verb', 'eat')
                          ('verb', 'kill')])


def test_stops():
    assert_equal(scan("the").result(), [('stop', 'the')])
    result = scan("the in of").result()
    assert_equal(result, [('stop', 'the'),
                          ('stop', ' in'),
                          ('stop', 'of')])


def test_noun():
    assert_equal(scan("bear").result(), [('noun', 'bear')])
    result = scan("bear princess").result()
    assert_equal(result, [('noun', 'bear'),
                           ('noun', 'princess')])

def test_numbers():
    assert_equal(scan("1234").result(), [('number', 1234)])
    result = scan("3 91234").result()
    assert_equal(result, [('number', 3),
                          ('number', 91234)])

def test_errors():
    assert_equal(scan("ASDFADFASDF").result(), [('error', 'ASDFADFASDF')])
    result = scan("bear IAS princess").result()
    assert_equal(result, [('noun', 'bear'),
                          ('error', 'IAS'),
                           ('noun', 'princess')])

【问题讨论】:

  • 在检查if item in scan.&lt;some list&gt;之前你不想lower这个词吗?
  • 将帖子缩减为一个失败的问题并包含错误消息。总是提供一个最小失败的例子,单靠努力可能会帮助你找到你的错误。 stackoverflow.com/help/mcve
  • if item not in (scan.dirs or scan.verbs or scan.stops or scan.nouns or scan.numbers): 没有做你想做的事。
  • 如果您查看 test_error(),您会注意到 IAS 保留了它的 case 样式。所以不能降低check前的词主要是因为error category.@depperm
  • >>> obj = scan("piGs can Fly") >>> obj.result() [('error', 'piGs'), ('error', 'can'), ('error', 'Fly')] 当我在 IDLE 中运行代码时,我得到了正确的结果,但现在当我使用 nosetests @PM2Ring 运行时得到正确的结果

标签: python python-3.x unit-testing user-input nose


【解决方案1】:

您的代码中有几个拼写错误和几个逻辑错误。

这是您的代码的修复版本,已修改为在没有 nose 模块(我没有)的情况下运行。

class scan(object):
    dirs = ['north','south','east','west','down','up','left','right','back']
    verbs = ['go','stop','kill','eat']
    stops = ['the','in','of','from','at','it']
    nouns = ['door','princess','bear','cabinet']
    numbers = ['0','1','2','3','4','5','6','7','8','9']

    def __init__(self, user_input):
        self.user_input = user_input

    def result(self):
        words = self.user_input.split()
        results = []

        for item in words:
            if item in scan.dirs:
                result = ('direction', item.lower())
                results.append(result)
            elif item in scan.verbs:
                result = ('verb', item.lower())
                results.append(result)
            elif item in scan.stops:
                result = ('stop', item.lower())
                results.append(result)
            elif item in scan.nouns:
                result =('noun', item.lower())
                results.append(result)
            elif all(c in scan.numbers for c in item):
                result = ('number', int(item))
                results.append(result)
            else:
                result = ('error', item)
                results.append(result)

        return results

def assert_equal(u, v):
    print(u, v, u == v)

def test_direction():
    assert_equal(scan('north').result(), [('direction', 'north')])
    result = scan("north east south").result()
    assert_equal(result, [('direction', 'north'),
                          ('direction', 'east'),
                          ('direction', 'south')])

def test_verbs():
    assert_equal(scan("go").result(), [('verb', 'go')])
    result = scan("go kill eat").result()
    assert_equal(result, [('verb', 'go'),
                          ('verb', 'kill'),
                          ('verb', 'eat')])


def test_stops():
    assert_equal(scan("the").result(), [('stop', 'the')])
    result = scan("the in of").result()
    assert_equal(result, [('stop', 'the'),
                          ('stop', 'in'),
                          ('stop', 'of')])


def test_noun():
    assert_equal(scan("bear").result(), [('noun', 'bear')])
    result = scan("bear princess").result()
    assert_equal(result, [('noun', 'bear'),
                           ('noun', 'princess')])

def test_numbers():
    assert_equal(scan("1234").result(), [('number', 1234)])
    result = scan("3 91234").result()
    assert_equal(result, [('number', 3),
                          ('number', 91234)])

def test_errors():
    assert_equal(scan("ASDFADFASDF").result(), [('error', 'ASDFADFASDF')])
    result = scan("bear IAS princess").result()
    assert_equal(result, [('noun', 'bear'),
                          ('error', 'IAS'),
                           ('noun', 'princess')])

tests = (
    test_direction,
    test_verbs,
    test_stops,
    test_noun,
    test_numbers,
    test_errors,
)

for test in tests:
    print('\n' + test.__name__)
    test()

输出

test_direction
[('direction', 'north')] [('direction', 'north')] True
[('direction', 'north'), ('direction', 'east'), ('direction', 'south')] [('direction', 'north'), ('direction', 'east'), ('direction', 'south')] True

test_verbs
[('verb', 'go')] [('verb', 'go')] True
[('verb', 'go'), ('verb', 'kill'), ('verb', 'eat')] [('verb', 'go'), ('verb', 'kill'), ('verb', 'eat')] True

test_stops
[('stop', 'the')] [('stop', 'the')] True
[('stop', 'the'), ('stop', 'in'), ('stop', 'of')] [('stop', 'the'), ('stop', 'in'), ('stop', 'of')] True

test_noun
[('noun', 'bear')] [('noun', 'bear')] True
[('noun', 'bear'), ('noun', 'princess')] [('noun', 'bear'), ('noun', 'princess')] True

test_numbers
[('number', 1234)] [('number', 1234)] True
[('number', 3), ('number', 91234)] [('number', 3), ('number', 91234)] True

test_errors
[('error', 'ASDFADFASDF')] [('error', 'ASDFADFASDF')] True
[('noun', 'bear'), ('error', 'IAS'), ('noun', 'princess')] [('noun', 'bear'), ('error', 'IAS'), ('noun', 'princess')] True

我发现的第一个逻辑错误是

if item not in (scan.dirs or scan.verbs or scan.stops 
    or scan.nouns or scan.numbers):

测试item 是否不在这些列表中。相反,它首先计算

scan.dirs or scan.verbs or scan.stops or scan.nouns or scan.numbers

使用 Python 的 or 运算符的标准规则。 scan.dirs 是一个非空列表,因此该表达式的结果就是 scan.dirs

所以if 语句等价于

if item not in scan.dirs:

这显然不是你打算做的。

有关orand 如何在Python 中工作的更多信息,请参阅我今年早些时候写的this answer

我们可以使用

实现该测试
if not any(item in seq for seq in (scan.dirs, scan.verbs, scan.stops, 
    scan.nouns, scan.numbers)):

但我们不需要这样做。相反,我们将大部分ifs 更改为elifs,然后任何未成功扫描的内容都必须是错误,因此我们可以在else 块中处理它。

第二个大逻辑错误是你的数字测试。您试图查看多位数字字符串是否是有效的(正)整数

if item in scan.numbers:

但该测试只有在 item 为单个数字时才会成功。

相反,我们需要检查数字的 _all_digits 实际上是数字,这就是

all(c in scan.numbers for c in item)

会。

但是,有一个更好的方法:我们只使用str 类型的.isdigit 方法:

if item.isdigit():

我没有在我的代码中使用它,因为我想使用您的扫描列表。此外,.isdigit 无法处理负数或小数点,但您可以轻松地将'-''.' 添加到scan.numbers

【讨论】:

  • 非常感谢,这确实有效。我没有意识到我在那个测试脚本中犯了多少错别字。
  • 我实际上从来不知道'all'关键字,我更喜欢 isdigit() 方法,因为 scan.numbers 只能在 0-9 之间。
  • @Jephthah all 及其“姐妹”函数 any 对于多个项目的一般测试非常有用。但是,当有内置的特定测试可以满足您的需求时,例如isdigit,您当然应该使用它们。
猜你喜欢
  • 2017-08-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-17
相关资源
最近更新 更多