Doctest failing

T

Tigerstyle

Hi guys.

I'm strugglin with some homework stuff and am hoping you can help me
out here.

This is the code:

small_words = ('into', 'the', 'a', 'of', 'at', 'in', 'for', 'on')

def book_title(title):
""" Takes a string and returns a title-case string.
All words EXCEPT for small words are made title case
unless the string starts with a preposition, in which
case the word is correctly capitalized.'The Works of Alexander Dumas'
"""
new_title = []
title_split = title.strip().lower().split()
for word in title_split:
if title_split[0] in small_words:
new_title.append(word.title())
elif word in small_words:
new_title.append(word.lower())
else:
new_title.append(word.title())
return(' '.join(new_title))

def _test():
import doctest, refactory
return doctest.testmod(refactory)
if __name__ == "__main__":
_test()

All tests are failing even though I am getting the correct output on
the first two tests. And the last test still gives me "Of" instead of
"of"

Any help is appreciated.

Rgds

T
 
M

Mel

Tigerstyle said:
Hi guys.

I'm strugglin with some homework stuff and am hoping you can help me
out here.

This is the code:

small_words = ('into', 'the', 'a', 'of', 'at', 'in', 'for', 'on')

def book_title(title):
""" Takes a string and returns a title-case string.
All words EXCEPT for small words are made title case
unless the string starts with a preposition, in which
case the word is correctly capitalized.'The Works of Alexander Dumas'
"""
new_title = []
title_split = title.strip().lower().split()
for word in title_split:
if title_split[0] in small_words:
new_title.append(word.title())
elif word in small_words:
new_title.append(word.lower())
else:
new_title.append(word.title())
return(' '.join(new_title))

def _test():
import doctest, refactory
return doctest.testmod(refactory)
if __name__ == "__main__":
_test()

All tests are failing even though I am getting the correct output on
the first two tests. And the last test still gives me "Of" instead of
"of"

Any help is appreciated.

I don't know about doctest -- I suspect it wants a structured docstring to
specify the tests -- but this

if title_split[0] in small_words:
new_title.append(word.title())

can't be what you want.

Mel.
 
P

Peter Otten

Tigerstyle said:
I'm strugglin with some homework stuff and am hoping you can help me
out here.

This is the code:

small_words = ('into', 'the', 'a', 'of', 'at', 'in', 'for', 'on')
new_title = []
title_split = title.strip().lower().split()
for word in title_split:
if title_split[0] in small_words:
new_title.append(word.title())
elif word in small_words:
new_title.append(word.lower())
else:
new_title.append(word.title())

The logic of the for-loop is flawed; the expression

title_split[0] in small_words

will always evaluate to True if the first word is a "small word", even when
the loop is already past the first word. You can work around that with a
flag along these lines

first = True
for word in title_split:
if first:
# special treatment for the first word
first = False
else:
# put checks for all words but the first here
new_title.append(fixed_word) # assuming you have stored the titlecased
# or lowercased word in the fixed_word
# variable
 
T

Thomas Jollans

Hi guys.

I'm strugglin with some homework stuff and am hoping you can help me
out here.

All tests are failing even though I am getting the correct output on
the first two tests. And the last test still gives me "Of" instead of
"of"

Cannot reproduce. I only get the one, expected, failure.

% python -m doctest books.py
**********************************************************************
File "books.py", line 12, in books.book_title
Failed example:
book_title('the WORKS OF AleXANDer dumas')
Expected:
'The Works of Alexander Dumas'
Got:
'The Works Of Alexander Dumas'
**********************************************************************
1 items had failures:
1 of 3 in books.book_title
***Test Failed*** 1 failures.
def _test():
import doctest, refactory
return doctest.testmod(refactory)
if __name__ == "__main__":
_test()

What is this "refactory"? Are you testing the right code? What is the
output of your test - does it make sense for the module?

Thomas
 
A

Alister Ware

Hi guys.

I'm strugglin with some homework stuff and am hoping you can help me out
here.

This is the code:

small_words = ('into', 'the', 'a', 'of', 'at', 'in', 'for', 'on')

def book_title(title):
""" Takes a string and returns a title-case string. All words EXCEPT
for small words are made title case unless the string starts with a
preposition, in which case the word is correctly capitalized.'The Works of Alexander Dumas'
"""
new_title = []
title_split = title.strip().lower().split()
for word in title_split:
if title_split[0] in small_words:
new_title.append(word.title())
elif word in small_words:
new_title.append(word.lower())
else:
new_title.append(word.title())
return(' '.join(new_title))

def _test():
import doctest, refactory return doctest.testmod(refactory)
if __name__ == "__main__":
_test()

All tests are failing even though I am getting the correct output on the
first two tests. And the last test still gives me "Of" instead of "of"

Any help is appreciated.

Rgds

T

Ignoring the docttests my process would be to process each word & then
manually capitalize he 1st word, .I would als0 use a comprehension as
makes for cleaner code:-

small_words=('into','the','a','of','at','in','for','on')

def capitalize(word):
if word in small_words:
return word
else:
return word.title()

def set_title(phrase):
result=[ capitalize(x.lower()) for x in phrase.split(' ') ]
result[0]=result[0].title()
return " ".join(result)


print set_title('the works of alexander dumas')
 
C

Chris Angelico

Ignoring the docttests my process would be to process each word & then
manually capitalize he 1st word, .I would als0 use a comprehension as
makes for cleaner code:-

def capitalize(word):
   if word in small_words:
       return word
   else:
       return word.title()

And I'd do this with a lambda, but that's just me. Of course, if your
logic is more complicated, it makes more sense to keep it in a named
function, but a single conditional call can fit nicely into a lambda.

ChrisA
 
T

Terry Reedy

Hi guys.

I'm strugglin with some homework stuff and am hoping you can help me
out here.

We appreciate you saying so instead of hiding that this is homework.
small_words = ('into', 'the', 'a', 'of', 'at', 'in', 'for', 'on')

def book_title(title):
""" Takes a string and returns a title-case string.
All words EXCEPT for small words are made title case
unless the string starts with a preposition, in which
case the word is correctly capitalized.'The Works of Alexander Dumas'
"""
new_title = []
title_split = title.strip().lower().split()
for word in title_split:
if title_split[0] in small_words:
new_title.append(word.title())
elif word in small_words:
new_title.append(word.lower())
else:
new_title.append(word.title())

The key issue is that you want to treat the first word one way (.title
it) and the rest differently (conditionally .title or not) . So
immediately separate the first from the rest and then process each.
There are at least three ways to do the split. Perhaps I should stop
with this hint, and certainly you should think a bit before reading
further, but here is what I consider to be the most elegant 3.2 code.
..
,
,
,
..
..
..
first, *rest = title.strip().lower().split()
new_title = [first.title()]
for word in rest:
if word not in small_words:
word = word.title()
new_title.append(word)
return(' '.join(new_title))

doctest.testmod() now passes (there is no 'refactory' here)
 
T

Terry Reedy

You can work around that with a
flag along these lines

first = True
for word in title_split:
if first:
# special treatment for the first word
first = False
else:
# put checks for all words but the first here
new_title.append(fixed_word) # assuming you have stored the titlecased
# or lowercased word in the fixed_word
# variable

An alternative to a flag and testing every item is to remove and process
the first item *before* the loop. See my response on this thread or my
new thread
Idioms combining 'next(items)' and 'for item in items:'
 
P

Peter Otten

Terry said:
An alternative to a flag and testing every item is to remove and process
the first item *before* the loop. See my response on this thread or my
new thread
Idioms combining 'next(items)' and 'for item in items:'

I reckoned the approach with the flag the most beginner-friendly because you
don't have to think too hard about the corner-cases, namely
''

When I use the "process first item before the loop" approach I usually end
up with a helper generator

def _words(words, small_words={w.title(): w for w in small_words}):
yield next(words)
for word in words:
yield small_words[word] if word in small_words else word

def book_title(s):
return " ".join(_words(iter(s.title().split())))

and the nagging thought that I'm making it more complex than need be.
 
D

Dennis Lee Bieber

title_split = title.strip().lower().split()
for word in title_split:
if title_split[0] in small_words:
new_title.append(word.title())

title_split[0] will never change, so if it is one of the small
words, then all subsequent words will also be treated to this branch.
elif word in small_words:
new_title.append(word.lower())

You already lowercased all the words before splitting, so this is
applying lowercase to a known lowercase word
else:
new_title.append(word.title())
return(' '.join(new_title))
I'd suggest looking up the definition of

enumerate()

in the language documentation... It will give you a simple way to know
if you are looking at the first word. Basically, you want to title-case
the word IF it is the first word OR the word is NOT in the list of
lowercase words; anything else goes through as lower case...
 
T

ting

Tigerstyle said:
I'm strugglin with some homework stuff and am hoping you can help me
out here.
This is the code:
small_words = ('into', 'the', 'a', 'of', 'at', 'in', 'for', 'on')
    new_title = []
    title_split = title.strip().lower().split()
    for word in title_split:
        if title_split[0] in small_words:
            new_title.append(word.title())
        elif word in small_words:
            new_title.append(word.lower())
        else:
            new_title.append(word.title())

The logic of the for-loop is flawed; the expression

title_split[0] in small_words

will always evaluate to True if the first word is a "small word", even when
the loop is already past the first word. You can work around that with a
flag along these lines

first = True
for word in title_split:
    if first:
        # special treatment for the first word
        first = False
    else:
        # put checks for all words but the first here
    new_title.append(fixed_word) # assuming you have stored the titlecased
                                 # or lowercased word in the fixed_word
                                 # variable

Another way to tackle this is to just capitalize the entire sentence
before returning it.

new_title = []
title_split = title.strip().lower().split()
for word in title_split:
if word in small_words:
new_title.append(word.lower())
else:
new_title.append(word.title())
return(''.join(new_title).capitalize())

However, I'm only helping with your homework, because I want to point
out that doctest comments should be *readable*. Don't just throw them
in, take the extra 10-15 seconds to make them easier on the eyes. In
the workplace, months will pass before you review this code again, so
you want to make it easier for an older version of yourself to
remember it.

And it takes very little effort to make it readable. Here's your
doctest string, reformatted with just a tiny bit more whitespace. I
even cut out a sentence.

def book_title(title):
"""
All words EXCEPT for small words are made title case
unless the string starts with a preposition, in which
case the word is correctly capitalized.
'The Works of Alexander Dumas'
"""
 
D

Dennis Lee Bieber

in the language documentation... It will give you a simple way to know
if you are looking at the first word. Basically, you want to title-case
the word IF it is the first word OR the word is NOT in the list of
lowercase words; anything else goes through as lower case...

Of course, most of this can be done in a single line (including
taking into account that some words may have a punctuation mark which
would confuse the original).
smalls = ['into', 'the', 'a', 'of', 'at', 'in', 'for', 'on' ]
punct = ".,;:?!;'\"(){}[]"
def recase(str = "physicist odd-affection, or how i was taught to stop fretting and adore the weapon of mass destruction"):
.... return " ".join( w.title() if i == 0 or w.strip(punct) not in
smalls else w
.... for i,w in enumerate(str.lower().strip().split()) )
....'Physicist Odd-Affection, Or How I Was Taught To Stop Fretting And Adore
the Weapon of Mass Destruction'
recase("what? me worry?") 'What? Me Worry?'
recase("the end of the matter is, to be blunt, a confusion") 'The End of the Matter Is, To Be Blunt, a Confusion'
recase("the end of the matter is in, to be blunt, a confusion") 'The End of the Matter Is in, To Be Blunt, a Confusion'
smalls = ['into', 'the', 'a', 'of', 'at', 'in', 'for', 'on', "and", "is", "to" ]
recase()
'Physicist Odd-Affection, Or How I Was Taught To Stop Fretting and Adore
the Weapon of Mass Destruction'
Of course, explaining what this construct is doing is the trick to
justifying it for a homework assignment.
 
T

Tigerstyle

I'm strugglin with some homework stuff and am hoping you can help me
out here.

We appreciate you saying so instead of hiding that this is homework.








small_words = ('into', 'the', 'a', 'of', 'at', 'in', 'for', 'on')
def book_title(title):
     """ Takes a string and returns a title-case string.
     All words EXCEPT for small words are made title case
     unless the string starts with a preposition, in which
     case the word is correctly capitalized.
     >>>  book_title('DIVE Into python')
     'Dive into Python'
     >>>  book_title('the great gatsby')
     'The Great Gatsby'
     >>>  book_title('the WORKS OF AleXANDer dumas')
     'The Works of Alexander Dumas'
     """
     new_title = []
     title_split = title.strip().lower().split()
     for word in title_split:
         if title_split[0] in small_words:
             new_title.append(word.title())
         elif word in small_words:
             new_title.append(word.lower())
         else:
             new_title.append(word.title())

The key issue is that you want to treat the first word one way (.title
it) and the rest differently (conditionally .title or not) . So
immediately separate the first from the rest and then process each.
There are at least three ways to do the split. Perhaps I should stop
with this hint, and certainly you should think a bit before reading
further, but here is what I consider to be the most elegant 3.2 code.
.
,
,
,
.
.
.
     first, *rest = title.strip().lower().split()
     new_title = [first.title()]
     for word in rest:
         if word not in small_words:
             word = word.title()
         new_title.append(word)
     return(' '.join(new_title))

doctest.testmod() now passes (there is no 'refactory' here)
def _test():
     import doctest, refactory
     return doctest.testmod(refactory)
if __name__ == "__main__":
     _test()

Thank you Terry,

I went for this solution as it was the easiest for me to understand
and comment myself keeping in mind what level I am at right now.
Thanks a ton to everyone for sharing so much information and making it
easy to read and understand your thoughts. This was surely very very
educating read the replies from so many talented people.

Thanks and looking forward to hanging around here more :)

T
 
T

Tigerstyle

Tigerstyle said:
I'm strugglin with some homework stuff and am hoping you can help me
out here.
This is the code:
small_words = ('into', 'the', 'a', 'of', 'at', 'in', 'for', 'on')
def book_title(title):
    """ Takes a string and returns a title-case string.
    All words EXCEPT for small words are made title case
    unless the string starts with a preposition, in which
    case the word is correctly capitalized.
    >>> book_title('DIVE Into python')
    'Dive into Python'
    >>> book_title('the great gatsby')
    'The Great Gatsby'
    >>> book_title('the WORKS OF AleXANDer dumas')
    'The Works of Alexander Dumas'
    """
    new_title = []
    title_split = title.strip().lower().split()
    for word in title_split:
        if title_split[0] in small_words:
            new_title.append(word.title())
        elif word in small_words:
            new_title.append(word.lower())
        else:
            new_title.append(word.title())
    return(' '.join(new_title))
def _test():
    import doctest, refactory
    return doctest.testmod(refactory)
if __name__ == "__main__":
    _test()
All tests are failing even though I am getting the correct output on
the first two tests. And the last test still gives me "Of" instead of
"of"
Any help is appreciated.

I don't know about doctest -- I suspect it wants a structured docstring to
specify the tests -- but this

        if title_split[0] in small_words:
            new_title.append(word.title())

can't be what you want.

        Mel.

Agreed. Not what I need.
 
T

Tigerstyle

Cannot reproduce. I only get the one, expected, failure.

% python -m doctest books.py
**********************************************************************
File "books.py", line 12, in books.book_title
Failed example:
    book_title('the WORKS OF AleXANDer dumas')
Expected:
    'The Works of Alexander Dumas'
Got:
    'The Works Of Alexander Dumas'
**********************************************************************
1 items had failures:
   1 of   3 in books.book_title
***Test Failed*** 1 failures.




What is this "refactory"? Are you testing the right code? What is the
output of your test - does it make sense for the module?

Thomas

Still struggling with my test failing. All 3 tests fail. I'm using
Ecplipse and I think Eclipse is what causing this.
 
T

Tigerstyle

And I'd do this with a lambda, but that's just me. Of course, if your
logic is more complicated, it makes more sense to keep it in a named
function, but a single conditional call can fit nicely into a lambda.

ChrisA

Lambda is too complicated for me to understand yet. Will get there
after a little while. Where would you put this piece of code?
 
T

Tigerstyle

Tigerstyle said:
I'm strugglin with some homework stuff and am hoping you can help me
out here.
This is the code:
small_words = ('into', 'the', 'a', 'of', 'at', 'in', 'for', 'on')
    new_title = []
    title_split = title.strip().lower().split()
    for word in title_split:
        if title_split[0] in small_words:
            new_title.append(word.title())
        elif word in small_words:
            new_title.append(word.lower())
        else:
            new_title.append(word.title())
The logic of the for-loop is flawed; the expression
title_split[0] in small_words
will always evaluate to True if the first word is a "small word", even when
the loop is already past the first word. You can work around that with a
flag along these lines
first = True
for word in title_split:
    if first:
        # special treatment for the first word
        first = False
    else:
        # put checks for all words but the first here
    new_title.append(fixed_word) # assuming you have stored the titlecased
                                 # orlowercased word in the fixed_word
                                 # variable

Another way to tackle this is to just capitalize the entire sentence
before returning it.

new_title = []
title_split = title.strip().lower().split()
for word in title_split:
  if word in small_words:
    new_title.append(word.lower())
  else:
    new_title.append(word.title())
return(''.join(new_title).capitalize())

However, I'm only helping with your homework, because I want to point
out that doctest comments should be *readable*. Don't just throw them
in, take the extra 10-15 seconds to make them easier on the eyes. In
the workplace, months will pass before you review this code again, so
you want to make it easier for an older version of yourself to
remember it.

And it takes very little effort to make it readable. Here's your
doctest string, reformatted with just a tiny bit more whitespace. I
even cut out a sentence.

def book_title(title):
  """
  All words EXCEPT for small words are made title case
  unless the string starts with a preposition, in which
  case the word is correctly capitalized.

  >>> book_title('DIVE Into python')
  'Dive into Python'
  >>> book_title('the great gatsby')
  'The Great Gatsby'
  >>> book_title('the WORKS OF AleXANDer dumas')
  'The Works of Alexander Dumas'
  """

It capitalises the phrase, but still the rest of the phrase is in
lower case.
 
T

Tigerstyle

in the language documentation... It will give you a simple way to know
if you are looking at the first word. Basically, you want to title-case
the word IF it is the first word OR the word is NOT in the list of
lowercase words; anything else goes through as lower case...

        Of course, most of this can be done in a single line (including
taking into account that some words may have a punctuation mark which
would confuse the original).
smalls = ['into', 'the', 'a', 'of', 'at', 'in', 'for', 'on' ]
punct = ".,;:?!;'\"(){}[]"
def recase(str = "physicist odd-affection, or how i was taught to stop fretting and adore the weapon of mass destruction"):

...     return " ".join( w.title() if i == 0 or w.strip(punct) not in
smalls else w
...                     for i,w in enumerate(str.lower().strip().split()) )
...>>> recase()

'Physicist Odd-Affection, Or How I Was Taught To Stop Fretting And Adore
the Weapon of Mass Destruction'>>> recase("what? me worry?")
'What? Me Worry?'
'The End of the Matter Is, To Be Blunt, a Confusion'>>> recase("the end of the matter is in, to be blunt, a confusion")

'The End of the Matter Is in, To Be Blunt, a Confusion'>>> smalls = ['into', 'the', 'a', 'of', 'at', 'in', 'for', 'on', "and", "is", "to" ]
'Physicist Odd-Affection, Or How I Was Taught To Stop Fretting and Adore
the Weapon of Mass Destruction'>>> recase("the end of the matter is in, to be blunt, a confusion")

'The End of the Matter is in, to Be Blunt, a Confusion'



        Of course, explaining what this construct is doing is thetrick to
justifying it for a homework assignment.

Too much destruction in this post man, and yeah I would not be able to
explain the code for my homework.
 
T

Terry Reedy

Thank you Terry,
I went for this solution as it was the easiest for me to understand
and comment myself keeping in mind what level I am at right now.
Thanks a ton to everyone for sharing so much information and making it
easy to read and understand your thoughts. This was surely very very
educating read the replies from so many talented people.

You are welcome. For more, see my thread "Idioms combining 'next(items)'
and 'for item in items:'" and the responses.
 
E

Ethan Furman

Chris said:
And I'd do this with a lambda, but that's just me. Of course, if your
logic is more complicated, it makes more sense to keep it in a named
function, but a single conditional call can fit nicely into a lambda.

Lambdas are great when needed, but if don't *need* it, and you have more
than a few, debugging can be a nightmare... "Okay, so this is function
<lambda>... and that is function <lambda>... and over here we also have
function <lambda>... ARGH!"

~Ethan~
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,985
Messages
2,570,199
Members
46,766
Latest member
rignpype

Latest Threads

Top