LPTHW ex49 在单元测试中得到 AttributeError

LPTHW ex49 get AttributeError in unit test

我正在练习 Learn Python th Hard Way 一书中的 ex49 练习。 我在 nosetest 中得到了 AttributeError,而我可以在 python shell 中通过执行命令。 我在 Ubuntu 15.10 中编码,python 版本是 3.4.3。 目录结构如下:

.:
bin  docs  ex49  lexicon.py  parser.py  readme  setup.py  tests

./bin:

./docs:

./ex49:
__init__.py

./tests:
ex49_test.py  __init__.py

lexicon.py:

#!/usr/bin/env python
# encoding: utf-8
direction_base = ['north', 'south', 'west', 'east', 'down', 'up', 'left', 'right', 'back']
verb_base = ['go', 'kill', 'eat', 'open']
stop_base = ['the', 'in', 'of', 'it']
noun_base = ['door', 'bear', 'princess', 'carbinet']


def convert_number(s):
    try:
        return int(s)
    except ValueError:
        return None

def scan(strInput):
    wordList = strInput.lower().split(' ')
    resultSentence = []

    for word in wordList:
        if word in direction_base:
            resultSentence.append(('direction', word))
        elif word in verb_base:
            resultSentence.append(('verb', word))
        elif word in stop_base:
            resultSentence.append(('stop', word))
        elif word in noun_base:
            resultSentence.append(('noun', word))
        elif convert_number(word) is not None:
            resultSentence.append(('number', int(word)))
        else:
            resultSentence.append(('error', word))

    return resultSentence

parser.py:

#!/usr/bin/env python
# encoding: utf-8

class ParserError(Exception):
    pass

class Sentence(object):
    def __init__(self, subject, verb, number, object):
        # remember we take ('noun','princess') tuples and convert them
        self.subject = subject[1]
        self.verb = verb[1]
        self.number = number[1]
        self.object = object[1]

    def to_tuple(self):
        return (self.subject, self.verb, self.number, self.object)

def peek(word_list):
    if word_list:
        word = word_list[0]
        return word[0]
    else:
        return None


def match(word_list, expecting):
    if word_list:
        word = word_list.pop(0)

        if word[0] == expecting:
            return word
        else:
            return None
    else:
        return None


def skip(word_list, word_type):
    while peek(word_list) == word_type:
        match(word_list, word_type)


def parse_verb(word_list):
    skip(word_list, 'stop')

    if peek(word_list) == 'verb':
        return match(word_list, 'verb')
    else:
        raise ParserError("Expected a verb next.")


def parse_object(word_list):
    skip(word_list, 'stop')
    next_item = peek(word_list)

    if next_item == 'noun':
        return match(word_list, 'noun')
    elif next_item == 'direction':
        return match(word_list, 'direction')
    else:
        raise ParserError("Expected a noun or direction next.")

def parse_number(word_list, number):
   skip(word_list, 'stop')

   if peek(word_list) == 'number':
       return match(word_list, 'number')
   else:
       return ('number', 1)

def parse_subject(word_list, subj):
    verb = parse_verb(word_list)
    number = parse_number(word_list)
    obj = parse_object(word_list)

    return Sentence(subj, verb, number, obj)


def parse_sentence(word_list):
    skip(word_list, 'stop')
    start_item = peek(word_list)

    if start_item == 'noun':
        subj = match(word_list, 'noun')
        return parse_subject(word_list, subj)
    elif start_item == 'verb':
        # assume the subject is the player then
        return parse_subject(word_list, ('noun', 'player'))
    else:
        raise ParserError("Must start with subject, object, or verb not: %s" % start_item)

ex49_test.py:

#!/usr/bin/env python
# encoding: utf-8
from nose.tools import *
import lexicon
import parser

def test_sentence_obj():
    s = parser.Sentence(('noun', 'bear'), ('verb', 'eat'), ('number', 1), ('noun', 'door'))
    assert_equal(s.subject, 'bear')
    assert_equal(s.verb, 'eat')
    assert_equal(s.number, 1)
    assert_equal(s.object, 'door')
    assert_equal(s.to_tuple(), ('bear', 'eat', 1, 'door'))

def test_peek():
    word_list = lexicon.scan('princess')
    assert_equal(parser.peek(word_list), 'noun')
    assert_equal(parser.peek(None), None)

def test_match():
    word_list = lexicon.scan('princess')
    assert_equal(parser.match(word_list, 'noun'), ('noun', 'princess'))
    assert_equal(parser.match(word_list, 'stop'), None)
    assert_equal(parser.match(None, 'noun'), None)

def test_skip():
    word_list = lexicon.scan('bear eat door')
    assert_equal(word_list, [('noun', 'bear'), ('verb', 'eat'), ('noun', 'door')])
    parser.skip(word_list, 'noun')
    assert_equal(word_list, [('verb', 'eat'), ('noun', 'door')])

def test_parse_verb():
    word_list = lexicon.scan('it eat door')
    assert_equal(parser.parse_verb(word_list), ('verb', 'eat'))
    word_list = lexicon.scan('bear eat door')
    assert_raise(parser.ParserError, parser.parse_verb, word_list)

def test_parse_object():
    word_list = lexicon.scan('the door')
    assert_equal(parser.parse_object(word_list), ('noun', 'door'))
    word_list = lexicon.scan('the east')
    assert_equal(parser.parse_object(word_list), ('direction', 'east'))
    word_list = lexicon.scan('the it')
    assert_raise(parser.ParserError, parser.parse_object, word_list)

def test_parse_subject():
    word_list = lexicon.scan('eat door')
    subj = ('noun', 'bear')
    s = parser.parse_subject(word_list, subj)
    assert_raise(s.to_tuple(), ('bear', 'eat', 1, 'door'))

def test_parse_sentence():
    word_list = lexicon.scan('the bear eat door')
    s = parser.parse_sentence(word_list)
    assert_equal(s.to_tuple(), ('bear', 'eat', 1, 'door'))
    word_list = lexicon.scan('in the door')
    s = parser.parse_sentence(word_list)
    assert_equal(s.to_tuple(), ('player', 'eat', 1, 'door'))
    word_list = lexicon.scan('north eat door')
    assert_equal(parser.ParserError, parser.parse_sentence, word_list)

def test_unknown_words():
    word_list = lexicon.scan('xxx the xxx bear xxx eat xxx 5 xxx door xxx')
    s = parser.parse_sentence(word_list)
    assert_equal(s.to_tuple(), ('bear', 'eat', 1, 'door'))

def test_number():
    word_list = lexicon.scan('xxx the xxx bear xxx eat xxx 5 xxx door xxx')
    s = parser.parse_sentence(word_list)
    assert_equal(s.to_tuple(), ('bear', 'eat', 5, 'door'))

执行错误:

~/Documents/python3/practice/ex49$ nosetests3 tests/
EEEEEEEEEE
======================================================================
ERROR: tests.ex49_test.test_sentence_obj
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/nose/case.py", line 198, in runTest
    self.test(*self.arg)
  File "/home/chungyi/Documents/python3/practice/ex49/tests/ex49_test.py", line 8, in test_sentence_obj
    s = parser.Sentence(('noun', 'bear'), ('verb', 'eat'), ('number', 1), ('noun', 'door'))
AttributeError: 'module' object has no attribute 'Sentence'

======================================================================
ERROR: tests.ex49_test.test_peek
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/nose/case.py", line 198, in runTest
    self.test(*self.arg)
  File "/home/chungyi/Documents/python3/practice/ex49/tests/ex49_test.py", line 17, in test_peek
    assert_equal(parser.peek(word_list), 'noun')
AttributeError: 'module' object has no attribute 'peek'

======================================================================
ERROR: tests.ex49_test.test_match
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/nose/case.py", line 198, in runTest
    self.test(*self.arg)
  File "/home/chungyi/Documents/python3/practice/ex49/tests/ex49_test.py", line 22, in test_match
    assert_equal(parser.match(word_list, 'noun'), ('noun', 'princess'))
AttributeError: 'module' object has no attribute 'match'

======================================================================
ERROR: tests.ex49_test.test_skip
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/nose/case.py", line 198, in runTest
    self.test(*self.arg)
  File "/home/chungyi/Documents/python3/practice/ex49/tests/ex49_test.py", line 29, in test_skip
    parser.skip(word_list, 'noun')
AttributeError: 'module' object has no attribute 'skip'

======================================================================
ERROR: tests.ex49_test.test_parse_verb
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/nose/case.py", line 198, in runTest
    self.test(*self.arg)
  File "/home/chungyi/Documents/python3/practice/ex49/tests/ex49_test.py", line 34, in test_parse_verb
    assert_equal(parser.parse_verb(word_list), ('verb', 'eat'))
AttributeError: 'module' object has no attribute 'parse_verb'

======================================================================
ERROR: tests.ex49_test.test_parse_object
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/nose/case.py", line 198, in runTest
    self.test(*self.arg)
  File "/home/chungyi/Documents/python3/practice/ex49/tests/ex49_test.py", line 40, in test_parse_object
    assert_equal(parser.parse_object(word_list), ('noun', 'door'))
AttributeError: 'module' object has no attribute 'parse_object'

======================================================================
ERROR: tests.ex49_test.test_parse_subject
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/nose/case.py", line 198, in runTest
    self.test(*self.arg)
  File "/home/chungyi/Documents/python3/practice/ex49/tests/ex49_test.py", line 49, in test_parse_subject
    s = parser.parse_subject(word_list, subj)
AttributeError: 'module' object has no attribute 'parse_subject'

======================================================================
ERROR: tests.ex49_test.test_parse_sentence
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/nose/case.py", line 198, in runTest
    self.test(*self.arg)
  File "/home/chungyi/Documents/python3/practice/ex49/tests/ex49_test.py", line 54, in test_parse_sentence
    s = parser.parse_sentence(word_list)
AttributeError: 'module' object has no attribute 'parse_sentence'

======================================================================
ERROR: tests.ex49_test.test_unknown_words
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/nose/case.py", line 198, in runTest
    self.test(*self.arg)
  File "/home/chungyi/Documents/python3/practice/ex49/tests/ex49_test.py", line 64, in test_unknown_words
    s = parser.parse_sentence(word_list)
AttributeError: 'module' object has no attribute 'parse_sentence'

======================================================================
ERROR: tests.ex49_test.test_number
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/nose/case.py", line 198, in runTest
    self.test(*self.arg)
  File "/home/chungyi/Documents/python3/practice/ex49/tests/ex49_test.py", line 69, in test_number
    s = parser.parse_sentence(word_list)
AttributeError: 'module' object has no attribute 'parse_sentence'

----------------------------------------------------------------------
Ran 10 tests in 0.007s

FAILED (errors=10)

你的问题出在parser模块命名上。不幸的是,python 中有一个内置的 parser 模块:尝试在任何文件夹中 运行ning python 和 运行 import parser。现在检查 parser.__file__ 以查看它指的是哪个实际模块:对我来说它指的是:

>>> import parser
>>> parser.__file__
'/usr/lib/python2.7/lib-dynload/parser.x86_64-linux-gnu.so'

所以你的 ex49_test.py 选择了错误的 parser 这就是 python 试图告诉你的: AttributeError: 'module' object has no attribute 'match' 意味着在这个模块中没有 match 方法。将解析器重命名为其他名称,看看生活是否变得更好。