2

Okay, I am looking for a way to reverse sentences in a string in a fast and concise manner.

For example, if I wanted to convert "“One two three. Four five six.” to “Three two one. Six five four.”

Here's my naive approach:

def reverse_sentences(str):
 newlist=str.split(".")   
 newstr=''
 for s in newlist[0:-1]:
   words=s.split()
   words=words[::-1]
   words[0]=words[0][0].upper()+words[0][1:]
   words[-1]=words[-1][0].lower()+words[-1][1:]
   for e in range(len(words)):
      words[e].strip()
      if words[e] != words[-1]:
           newstr+=words[e]+" "
      else:
           newstr+=words[e]
   newstr+=". "
 return newstr

Any faster and more Pythonic way to do this, e.g. list comprehensions and stuff.

garlfd
  • 149
  • 11

2 Answers2

4

Here is a long one-liner using list comprehension and slicing and str.join:

In [79]: t="One two three. Four five six."

In [80]: ' '.join(' '.join(sentence.lower().split()[::-1]).capitalize()+'.'
    ...:                              for sentence in t.split('.')[:-1])
Out[80]: 'Three two one. Six five four.'
zhangxaochen
  • 32,744
  • 15
  • 77
  • 108
  • 1
    This is certainly "more concise", which is what was asked for. It doesn't appear to be faster (see my answer) or more Pythonic (see [PEP 20](http://legacy.python.org/dev/peps/pep-0020/)), but I do like your functional approach. – johnsyweb Mar 01 '14 at 05:04
  • 1
    @Johnsyweb yes it's not faster, just for fun ;) – zhangxaochen Mar 01 '14 at 06:20
2

I would probably write something like this using generator expressions (for lazy evaluation):

def reverse_sentences(text):
    sentences = text.split('.')
    reversed_words = (reversed(words.split()) for words in sentences)
    rejoined_words = (' '.join(words) for words in reversed_words)
    capitalized_sentences = (sentence.capitalize() for sentence in rejoined_words)
    return '. '.join(capitalized_sentences)

reverse_sentences("One two three. Four five six.")

I prefer this function over a "one-liner", since it's easier to maintain (for instance, should you wish to add handling for punctuation, or strip-ping whitespace).

Breaking this down into its component parts (using list comprehensions for illustration):

In [1]: text = "One two three. Four five six."

In [2]: text
Out[2]: 'One two three. Four five six.'

In [3]: sentences = text.split('.')

In [4]: sentences
Out[4]: ['One two three', ' Four five six', '']

In [5]: reversed_words = [words.split()[::-1] for words in sentences]

In [6]: reversed_words
Out[6]: [['three', 'two', 'One'], ['six', 'five', 'Four'], []]

In [7]: rejoined_words= [' '.join(words) for words in reversed_words]

In [8]: rejoined_words
Out[8]: ['three two One', 'six five Four', '']

In [9]: capitalized_sentences = [sentence.capitalize() for sentence in rejoined_words]

In [10]: capitalized_sentences
Out[10]: ['Three two one', 'Six five four', '']

In [11]: '. '.join(capitalized_sentences)
Out[11]: 'Three two one. Six five four. '

Note:

Speed

You probably want to know how fast this runs...

timeit is handy here.

Your example

> python -m timeit <<EOF
def reverse_sentences(str):
 newlist=str.split(".")
 newstr=''
 for s in newlist[0:-1]:
   words=s.split()
   words=words[::-1]
   words[0]=words[0][0].upper()+words[0][1:]
   words[-1]=words[-1][0].lower()+words[-1][1:]
   for e in range(len(words)):
      words[e].strip()
      if words[e] != words[-1]:
           newstr+=words[e]+" "
      else:
           newstr+=words[e]
   newstr+=". "
 return newstr

reverse_sentences("One two three. Four five six.")
EOF
100000000 loops, best of 3: 0.012 usec per loop

My example

> python -m timeit <<EOF
def reverse_sentences(text):     
    sentences = text.split('.')                                                                      
    reversed_words = (reversed(words.split()) for words in sentences)
    rejoined_words = (' '.join(words) for words in reversed_words)
    capitalized_sentences = (sentence.capitalize() for sentence in rejoined_words)
    return '. '.join(capitalized_sentences)

reverse_sentences("One two three. Four five six.")
EOF
100000000 loops, best of 3: 0.012 usec per loop

zhangxaochen's example

> python -m timeit <<EOF
t="One two three. Four five six."
' '.join(' '.join(sentence.lower().split()[::-1]).capitalize()+'.' for sentence in t.split('.')[:-1])
EOF                                                                  
100000000 loops, best of 3: 0.012 usec per loop

Conclusion

Optimise for readability and maintainability by adhering to The Zen of Python and your code will be "more pythonic".

Community
  • 1
  • 1
johnsyweb
  • 136,902
  • 23
  • 188
  • 247