Tomasz Kapłoński
Tomasz Kapłoński

Reputation: 1378

Strange behaviour while replacing string in python

I have a date given in format "October 28, 2010" (or simillar). Now I want to change the full name of month to shorter version (in this case Oct). For this I prepared a dictionary:

_mapping = {'January': 'Jan', 
            'February': 'Feb', 
            'March': 'Mar', 
            'April': 'Apr', 
            'May': 'May', 
            'June': 'Jun', 
            'July': 'Jul', 
            'August': 'Aug', 
            'September': 'Sep', 
            'October': 'Oct', 
            'November': 'Nov', 
            'December': 'Dec'}

and in the method where the substition goes I wrote following:

def fetch(self, pno):
    ...
    date = #get data (working fine)
    for l, s in self._mapping.iteritems():
        pubdate = date.replace(l, s)
        print l + " -> " + pubdate #this is only for debug
    (pubd_month, self.pubd_day, self.pubd_year) = pubdate.split(' ')
    print pubd_month, self.pubd_day, self.pubd_year
    print pubdate

The result of execution is:

February -> October 28, 2008
October -> Oct 28, 2008
January -> October 28, 2008
April -> October 28, 2008
November -> October 28, 2008
March -> October 28, 2008
August -> October 28, 2008
May -> October 28, 2008
December -> October 28, 2008
June -> October 28, 2008
September -> October 28, 2008
July -> October 28, 2008
October
October 28, 2008

As you can see it seems that the replacement goes ok when it finds October but outside the loop I get again full month name. What do I do wrong?

Another question: is there shorter way to do this?

Upvotes: 1

Views: 141

Answers (2)

unutbu
unutbu

Reputation: 880937

There is no need for _mapping. If you use datetime.datetime objects, the strftime method can return the abbreviated month for you:

import datetime as DT

def short_date(date):
    date = DT.datetime.strptime(date, '%B %d, %Y')
    return date.strftime('%b %d, %Y')

print(short_date('October 28, 2010'))

prints

Oct 28, 2010

If your date strings come in a variety of formats then instead of parsing them with strptime(date, '%B %d, %Y') you could use dateutil's parser.parse method. For greatest flexibility, you'll probably be best served parsing the date strings into datetime.datetime objects as early as possible, and using strftime to format them as late as possible.

Upvotes: 3

Martijn Pieters
Martijn Pieters

Reputation: 1125078

Because you are always replacing pubdate with a replacement on date, you overwrite any successful matching.

Either test first if the month is in the string before replacing, or replace pubdate with pubdate.replace() results:

pubdate = #get data (working fine)
for l, s in self._mapping.iteritems():
    pubdate = pubdate.replace(l, s)

or, using testing:

date = #get data (working fine)
for l, s in self._mapping.iteritems():
    if l in date:
        pubdate = date.replace(l, s)

The latter still will result in pubdate being overwritten if more than one month name is found. Alternatively, if there is only ever going to be one month match, use break to end the loop early:

date = #get data (working fine)
for l, s in self._mapping.iteritems():
    if l in date:
        pubdate = date.replace(l, s)
        break

Caveat: if no match is ever found, pubdate will never have been assigned to and you'll get a UnboundLocal exception.

Upvotes: 3

Related Questions