0

I'm very new to Python and am working my way through the "Learn Python The Hard Way" web tutorial. But I've come to a halt as I have an issue with passing a single string. I'm able to pass a list OK...

Exercise 48 is getting us to reverse engineer the code from a unit test. The unit test is:

def test_directions():
    assert_equal(Lexicon.scan("north"), [('direction', 'north')])

My code looks like this:

class Lexicon:

    def __init__(self):
        self.directions = ['north','south','east','west','down','up','left','right','back']
        self.verbs = ['go','stop','kill','eat']
        self.stops = ['the','in','of','from','at','it']
        self.nouns = ['door','bear','princess','cabinet']

    def scan(self, words):

        result = []

        for i in words:
            if i in self.directions:
                result.append( ('direction', i) )
            elif i in self.verbs:
                result.append( ('verb', i) )
            elif i in self.stops:
                result.append( ('stop', i) )
            elif i in self.nouns:
                result.append( ('noun', i) )
            else: 
                 try:
                    result.append( ('number', int(i)) )
                 except ValueError:   
                    result.append( ('error', i) )


        return result

Running the code from the python prompt gives me the following results:

>>> from lexicon import Lexicon
>>> test = Lexicon()
>>> test.directions
['north', 'south', 'east', 'west', 'down', 'up', 'left', 'right', 'back']
>>> words = ['south']
>>> test.scan(words)
[('direction', 'south')]
>>> 
>>> test.scan("north")
[('error', 'n'), ('error', 'o'), ('error', 'r'), ('error', 't'), ('error', 'h')]
>>> 

I'd be very grateful if someone could point out why lists are being treated differently to the single string? And also how I can re-write my code so that both are treated the same way?

Thanks in advance, Nigel.

2
  • In first case, you have passed a list with a single element, while in second case you are passing the string itself, which is not the expected input parameter (hence the undesired behavior). Commented Oct 4, 2014 at 15:09
  • The unittest in that exercise also shows a single string argument of multiple words separated by spaces - you will want to account for that in your method. Commented Oct 4, 2014 at 16:24

3 Answers 3

1

This line is trying to iterate through a list of words

for i in words:

If you just pass a string in, i will actually take on each letter, for example

for i in 'test':
    print i

t
e
s
t

To pass in a single word, pass in a list of length 1.

test.scan(["north"])
Sign up to request clarification or add additional context in comments.

Comments

0
def scan(self, words):

    result = []
    words = words.split()

    for i in words:
        ...

The unittest in the exercise can be seen as blackbox specification for how to call the method. Your method must conform to that spec.

To account for a string argument that may contain multiple words separated by spaces, splitthe string. split returns a list which will work for the rest of the method.

1 Comment

Thank you wwii! That works perfectly: >>> stuff = raw_input('> ') > go north west tues 1 south >>> test.scan(stuff) [('verb', 'go'), ('direction', 'north'), ('direction', 'west'), ('error', 'tues'), ('number', 1), ('direction', 'south')] >>> test.scan("north") [('direction', 'north')] >>>
0

Your function doesn't work for single strings. What happens is that for i in words: iterates over the characters of the string.

It is possible to change the function such that it would work with both single strings and iterables of strings. However, it would be more consistent to always pass iteratables and not bother with single strings.

To pass a single string, just pass a one-element list (["north"]) or tuple (("north",)).

2 Comments

Thank you NPE (and Cyber). So, is it incorrect to write the unit test as described? What would be the way to satisfy the assert_equal test? The input is supposed to come from a raw_input and then split() which would give a list so a single string would never be passed in its native form anyway...
@NDP - The unittest should/will describe the call signature so to ensure the unittest will pass Lexicon.scan needs to accept a single string.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.