Learn python the hard way Ex48 AsserionError: Lists differ: [(None, '1234')] != [('number', 1234)]

Hi, I have watched Ex48 video but I still got below error.

assert_equal(lexicon.scan("1234"), [('number', 1234)])

AssertionError: Lists differ: [(None, '1234')] != [('number', 1234)]

First differing element 0:

(None, '1234')

('number', 1234)

- [(None, '1234')]

+ [('number', 1234)] 

My codes in lexicon.py as below,
I also tried " int(1234)" but got the same error…

lexicon = {
    "north": 'direction',
    "south": 'direction',
    "east": 'direction',
    "go": 'verb',
    "kill": 'verb',
    "eat": 'verb',
    "the": 'stop',
    "in": 'stop',
    "of": 'stop',
    "bear": 'noun',
    "princess": 'noun',
    1234: 'number',
    3: 'number',
    91234: 'number',
    "ASDFADFASDF": 'error',
    "bear": 'noun',
    "IAS": 'error',
    "princess": 'noun'
}

def scan(sentence):
    results = []
    words = sentence.split()
    for word in words:
        word_type = lexicon.get(word)
        results.append((word_type, word))
    return results

Any help would be appreciated!!!

There’s a difference between 1234 and “1234”. The first one is an integer, and that’s what the dictionary contains, but you are scanning the second one, a string. You need to convert the string first.

You were on the right track with “int(1234)” but you got the quotes wrong. Try int(“1234”).

Hi Florian,
Thanks, I tried int(“1234”) but somehow still got the same error.

OK, at what point in the code did you use int("1234")?

You need to somehow handle the case that you get a number string within your scan function.

def scan(sentence):
    ...
    for word in words:
        if # word is a number string:
            # handle that case
        word_type = lecicon.get(word, 'error')
    ...
    return results

In order to make the function more generic you could also omit the numbers from the dictionary and create the result ("number", word) on the fly when the number test succeeds.

Maybe reread the exercise in the book, I think Zed shows you how to do the conversion.

I replaced 1234: ‘number’ with int(“1234”).
Ok, I will reread the exercise and try again to see if it will work.
thank you, again!

Yes, so the issue is a little more subtle than @florian is explaining. @florian is correct that you are trying to search for 1234 when the user has typed in “1234”. BUT, that’s not the actual problem with the code.

First, imagine you are not handling numbers. Just do words. In that case this would work, and you’d pass those tests. Then, when you type in a word that does not exist what happens? You get something like this on the return:

(None, “thisworddoesntwork”)

Now, if someone types in a number–remembering that we’re talking about your code but in a form that does NOT handle numbers yet–you would get the same error:

(None, “1234”)

You of course then say, “Aha, I just have to handle that number in the test.” You then write into the dictionary this pair:

1234: “number”

But, that’s now how the number comes in from the users. When someone types on the keyboard python doesn’t do anything with it. If they type “1234” then it comes as a string, “1234”. You have to manually convert it to a number using int() then work with that (as @florian said).

HOWERVER, changing your dictionary to this:

“1234”: “number”

Still doesn’t actually work. It might get the test to run, but what if the user types 9837987345875. That’s still a number, but your tiny lexicon only knows “1234”. Are you going to put every number possible into the dictionary? You could. Or, you could use the above information about int() to come up with a permanent future proof solution. Try this:

Python 3.6.8 (default, Aug 20 2019, 17:12:48)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> int("1234")
1234
>>> int("adfasdfasdfasdf")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'adfasdfasdfasdf'
>>> try:
...     int("asdfadfasdf")
... except:
...     print("not a number")
...
not a number
>>>

You can see that if you run int() it will convert strings full of numbers into Python integers. BUT, if you feed int() something that’s not a number it explodes with an error. If you wrap that with a try/except then you can handle the case where it’s not a number. That means you can use this logic:

  1. Attempt to convert every input to an integer using int() but inside a try: and then if that works you can return it as the (1234, “number”).
  2. If that explodes, then the except: will run, that’s where you put your current ```lexicon.get(word, ‘error’) code.
  3. Now if someone types any number you get a number, but if that fails then you look it up in the lexicon.

There’s other ways to do this, like using isdigit https://docs.python.org/3/library/stdtypes.html#str.isdigit so try them out.

2 Likes

Hi Zed, thank you for the detailed explanation!! I tried to code which is similar to the concept of isdigit last night and it worked. Below is the updated codes. I will try the other way you mentioned (try, except) to run nosetests. Thank you.

Ok, now to confirm, remove the numbers from your lexicon dictionary.