[Tutor] Multi-Dimensional Dictionary that contains a 12 element list.
Kent Johnson
kent37 at tds.net
Sat Dec 31 15:33:52 CET 2005
Paul Kraus wrote:
> So now for all my reports all I have to write are little 5 or 6 line scripts
> that take a text file split the fields and format them before basing them off
> into my custom object. Very slick and this is my first python project. Its
> cluttered and messy but for about 1 hours worth of work on a brand new
> language I am impressed with the usability of this language.
I'm impressed with how fast you have learned it!
>
> Current Script - Critique away! :)
A number of comments below...
> =-=-=-=-=--=-=
> #!/usr/bin/python
> import string
> import re
>
> class Tbred_24Months:
> def __init__(self,currentyear,currentmonth): ### Takes Ending Year and
> Ending Month Inits List
> guide = []
> self.results = {}
> self.guide = {}
> self.end_month = currentmonth
> self.end_year = currentyear
You never use these two attributes ^^
> self.start_month = currentmonth
> self.start_year = currentyear
start_month and start_year can be local variables, you don't use them
outside this method.
> for count in range(24):
> guide.append((self.start_month,self.start_year))
> self.start_month -= 1
> if self.start_month < 1:
> self.start_month = 12
> self.start_year -= 1
> guide.reverse()
> count = 0
> for key in guide:
> self.guide[key[1],key[0]]=count
> count += 1
You can unpack key in the for statement, giving names to the elements:
for start_month, start_year in guide:
self.guide[start_year, start_month] = count
You can generate count automatically with enumerate():
for count, (start_month, start_year) in enumerate(guide):
self.guide[start_year, start_month] = count
If you built guide with tuples in the order you actually use them (why
not?) you could build self.guide as easily as
self.guide = dict( (key, count) for count, key in enumerate(guide) )
For that matter, why not just build self.guide instead of guide, in the
first loop?
for index in range(23, -1, -1):
self.guide[start_month,start_year] = index
start_month -= 1
if start_month < 1:
start_month = 12
start_year -= 1
I found the use of both guide and self.guide confusing but it seems you
don't really need guide at all.
> self.sortedkeys = self.guide.keys()
> self.sortedkeys.sort()
You don't use sortedkeys
>
> def insert(self,key,year,month,number):
> if self.guide.has_key((year,month)):
> if self.results.has_key(key):
> seq = self.guide[(year,month)]
> self.results[key][seq] += number
> else:
> self.results[key] = []
> for x in range(24):self.results[key].append(0)
Could be
self.results[key] = [0*24]
Is there a bug here? You don't add in number when the key is not there.
I like to use dict.setdefault() in cases like this:
if self.guide.has_key((year,month)):
seq = self.guide[(year,month)]
self.results.setdefault(key, [0*24])[seq] += number
though if performance is a critical issue your way might be better - it
doesn't have to build the default list every time. (Don't try to build
the default list just once, you will end up with every entry aliased to
the same list!) (I hesitate to include this note at all - you will only
notice a difference in performance if you are doing this operation many
many times!)
>
> def splitfields(record):
> fields = []
> datestring=''
> ### Regular Expr.
> re_negnum = re.compile('(\d?)\.(\d+)(-)')
> re_date = re.compile('(\d\d)/(\d\d)/(\d\d)')
> for element in record.split('|'):
> element=element.strip() # remove leading/trailing whitespace
> ### Move Neg Sign from right to left of number
> negnum_match = re_negnum.search( element )
> if negnum_match:
> if negnum_match.group(1):element = "%s%d.%02d"
> %(negnum_match.group(3),int(negnum_match.group(1)),int(negnum_match.group(2)))
> else:element = "%s0.%02d"
> %(negnum_match.group(3),int(negnum_match.group(2)))
This is a lot of work just to move a - sign. How about
if element.endswith('-'):
element = '-' + element[:-1]
> ### Format Date
> date_match = re_date.search(element)
> if date_match:
> (month,day,year) =
> (date_match.group(1),date_match.group(2),date_match.group(3))
> ### Convert 2 year date to 4 year
> if int(year) > 80:year = "19%02d" %int(year)
> else:year = "20%02d" %int(year)
> element = (year,month,day)
You go to a lot of trouble to turn your year into a string, then you
convert it back to an int later. Why not just keep it as an int?
Do you know that Python data structures (lists, dicts, tuples) are
heterogeneous? You can have a list that contains strings, ints, tuples, etc.
> if element == '.000': element = 0.00
> fields.append( element )
> return fields
This whole splitfields() function is kind of strange. It seems you know
what order the fields are in since the caller assumes an order. So why
not something like
elements = record.split('|')
if len(elements) != 7:
return None
vendor,otype,oreturn,discountable,discperc,amount,date
discperc = processNumber(discperc)
amount = processNumber(amount)
date = processDate(date)
return vendor,otype,oreturn,discountable,discperc,amount,date
then all the ugly regex stuff goes into processNumber() and
processDate() and the intent of the code is much clearer.
processNumber() can return a float and processDate() can return the
(year, month, day) tuple you need.
>
>
> ### Build Vendor Sales
> sales = Tbred_24Months(2005,11)
> vendorsales = open('vendorsales.txt','r')
> for line in vendorsales:
> fields = splitfields( line )
> if len(fields) == 7:
> (vendor,otype,oreturn,discountable,discperc,amount,date) = fields
> amount = float(amount);discperc = float(discperc)
> #if discperc and discountable == 'Y': amount = amount - ( amount *
> (discperc/100) )
> if otype == 'C' or oreturn == 'Y':amount = amount * -1
> sales.insert(vendor,int(date[0]),int(date[1]),amount)
> result = ''
>
> for key in sales.results:
> sum = float(0)
could be sum = 0.0
> result = str(key)
> for amount in sales.results[key]:
> sum += amount
> result += "|" + str(amount)
You can iterate keys and values together with
for key, amounts in sales.results.iteritems():
then it would be
for amount in amounts:
> print str(key),sum
> #print result,sum
HTH
Kent
More information about the Tutor
mailing list