I am working on the exercise 48 of Learn Python The Hard Way and writing the lexicon
dictionary and the scan
module to run the following test on:
from nose.tools import *
from ex47 import lexicon
def test_directions():
assert_equal(lexicon.scan("north"), [('direction', 'north')])
result = lexicon.scan("north south east")
assert_equal(result, [('direction', 'north'),
('direction', 'south'),
('direction', 'east')])
def test_verbs():
assert_equal(lexicon.scan("go"), [('verb', 'go')])
result = lexicon.scan("go kill eat")
assert_equal(result, [('verb', 'go'),
('verb', 'kill'),
('verb', 'eat')])
def test_stops():
assert_equal(lexicon.scan("the"), [('stop', 'the')])
result = lexicon.scan("the in of")
assert_equal(result, [('stop', 'the'),
('stop', 'in'),
('stop', 'of')])
def test_nouns():
assert_equal(lexicon.scan("bear"), [('noun', 'bear')])
result = lexicon.scan("bear princess")
assert_equal(result, [('noun', 'bear'),
('noun', 'princess')])
def test_numbers():
assert_equal(lexicon.scan("1234"), [('number', 1234)])
result = lexicon.scan("3 91234")
assert_equal(result, [('number', 3),
('number', 91234)])
def test_errors():
assert_equal(lexicon.scan("ASDFADFASDF"), [('error', 'ASDFADFASDF')])
result = lexicon.scan("bear IAS princess")
assert_equal(result, [('noun', 'bear'),
('error', 'IAS'),
('noun', 'princess')])
I have most of this figured out, except the numbers and errors tests. When the user input is a word not defined in the dictionary, it needs to be tagged with the error
value name, and if it's a number - the number
value name. Obviously, I can preemptively add all the inputs that will be tested into the dictionary, but that's cheating.
my dictionary looks like this:
lexicon = {
'north': 'direction',
'princess': 'noun',
# etc...
}
Is there a way to work, uh, "open-ended" definitions for numbers and undefined words into this?
UPDATE. Here is a working solution :
def scan(sentence):
words = sentence.split()
pairs = []
for word in words:
try:
number = int(word)
tupes = ('number', number)
pairs.append(tupes)
except ValueError:
try:
word_type = lexicon[word]
tupes = (word_type, word)
pairs.append(tupes)
except KeyError:
tupes = ('error', word)
pairs.append(tupes)
return pairs
I think you'll find this "open-endedness" by checking whether your item is in the dictionary. Something like this will work:
my_item in lexicon
This returns True if the dictionary lexicon
contains a key of my_item
, and False otherwise.
But there is a better way to do it.
This is a good opportunity to practice Python's concept of "It's Better To Ask Forgiveness Than Permission". How about something like:
try:
value = lexicon[my_item]
except KeyError:
# KeyError is thrown when you index into a dictionary with a nonexistent key
value = 'error'
Coincidentally, you can do the same sort of thing to check whether a string can be converted to an integer.
try:
value = int(maybe_a_number)
except ValueError:
# maybe_a_number wasn't a number!
This is a common pattern in Python -- try to use a variable in a certain way, and handle the failure case.
For more material on dictionaries, check out: http://docs.python.org/2/library/stdtypes.html#mapping-types-dict