By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
444,089 Members | 2,159 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 444,089 IT Pros & Developers. It's quick & easy.

trictionary?

P: n/a
i have some code which looks kinda like

bin = {}
for whatever:
for [a, b] in foo:
x = 42 - a
y = 42 - b
if bin.has_key(x):
bin[x] += 1
else:
bin[x] = 1
for i, j in bin.iteritems():
print i, j

now i want to add a second count column, kinda like

bin = {}
for whatever:
for [a, b] in foo:
x = 42 - a
if bin.has_key(x):
bin[x.b] += 1
else:
bin[x.b] = 1
bin[x.not b] = 0
for x, y, z in bin.iteritems():
print x, y, z

should the dict value become a two element list, or is
there a cleaner way to do this?

randy

Aug 28 '05 #1
Share this Question
Share on Google+
15 Replies


P: n/a
Randy Bush wrote:
now i want to add a second count column, kinda like

bin = {}
for whatever:
for [a, b] in foo:
x = 42 - a
if bin.has_key(x):
bin[x.b] += 1
else:
bin[x.b] = 1
bin[x.not b] = 0
for x, y, z in bin.iteritems():
print x, y, z

should the dict value become a two element list, or is
there a cleaner way to do this?


It would probably help if you explained what the real problem is you're
trying to solve. Using a two element list to store a pair of counts has
a bad code smell to me.

That said, you could write your code something like:

bin = {}
for whatever:
# NOTE: brackets are unnecessary
for a, b in foo:
x = 42 - a
# NOTE: 'in' is generally faster than has_key()
if x in bin
bin[x][0] += 1
else:
bin[x] = [1, 0]
# NOTE: extra parens necessary to unpack count list
for x, (y, z) in bin.iteritems():
print x, y, z

STeVe
Aug 28 '05 #2

P: n/a
Steven Bethard wrote:
....
Using a two element list to store a pair of counts has
a bad code smell to me.

....

Why is that? It strikes me as the cleanest way to solve that problem,
as long as it's easy enough to figure out what each element really
represents. You could name each element, but for a block of code eight
lines long, doing anything more descriptive would be too verbose.

Adam

Aug 28 '05 #3

P: n/a
Adam Tomjack wrote:
Steven Bethard wrote:
...
Using a two element list to store a pair of counts has a bad code
smell to me.

...

Why is that?


Note that "code smell"[1] doesn't mean that something is actually wrong,
just that it might be. In Python, pairs are usually handled with
tuples[2], but tuples would be inconvenient in this case, since the
first value must be modified. Declaring a class with two attributes as
you suggested is often a good substitute, but if the OP's code is really
what it looks like, I get another code smell because declaring a class
to be used by only 10 lines of code seems like overkill.

I also get a code smell from a dict holding two-element lists because
I've been writing in Python and answering questions on the Python-list
for a couple of years now, and I've never needed one yet. ;-)

STeVe

[1]http://en.wikipedia.org/wiki/Code_smell
[2]http://www.python.org/doc/faq/general.html#why-are-there-separate-tuple-and-list-data-types
Aug 29 '05 #4

P: n/a
Steven Bethard wrote:
Adam Tomjack wrote:
Steven Bethard wrote:
...
Using a two element list to store a pair of counts has a bad code
smell to me.

...

Why is that?


Note that "code smell"[1] doesn't mean that something is actually wrong,
just that it might be. In Python, pairs are usually handled with
tuples[2], but tuples would be inconvenient in this case, since the
first value must be modified. Declaring a class with two attributes as
you suggested is often a good substitute, but if the OP's code is really
what it looks like, I get another code smell because declaring a class
to be used by only 10 lines of code seems like overkill.

I also get a code smell from a dict holding two-element lists because
I've been writing in Python and answering questions on the Python-list
for a couple of years now, and I've never needed one yet. ;-)

STeVe

[1]http://en.wikipedia.org/wiki/Code_smell
[2]http://www.python.org/doc/faq/general.html#why-are-there-separate-tuple-and-list-data-types


Could you do me a favor and see what this smells like?

I put some data in a csv file (with headers) and this will
bring it in quite simply as a dictionary with [names] as
keys and an attribute dictionary as the value.

py_monsters.csv:

name,hardiness,agility,friend,courage,room,weight, special_def_odds,armor,weapon,odds,dice,side,hits, reaction,desc
PIRATE,5,20,0,10,26,300,0,0,11,60,1,10,0,"not met",You see a man with a
beard and a brass ring in his ear. He is wearing clothes made of silk
and is wielding a very fancily engraved sword.
import csv
temp1 = []
temp2 = []
reader = csv.reader(file(r"py_monsters.csv"))
for rec in reader:
temp1.append(rec)
for i in temp1[1:]:
temp2.append((i[0],dict(zip(temp1[0][1:],i[1:]))))
monsters = dict(temp2)

This gives me what I want

[('PIRATE', {'reaction': 'not met',
'agility': '20',
'room': '26',
'weight': '300',
'armor': '0',
'weapon': '11',
'hits': '0',
'side': '10',
'special_def_odds': '0',
'courage': '10',
'hardiness': '5',
'desc': 'You see a man with a beard and a brass ring in his ear.
He is wearing clothes made of silk and is wielding a very fancily
engraved sword.',
'odds': '60',
'friend': '0',
'dice': '1'})]

so that I can now write code like

if monsters['PIRATE']['reaction']=='not met':
print monsters['PIRATE']['desc']

instead of using stupid index numbers.

But the loader seems to have a kind of perl-like odor to it,
i.e., next week I won't understand what it does.

Aug 29 '05 #5

P: n/a
Steven Bethard <st************@gmail.com> wrote:
In Python, pairs are usually handled with tuples[2], but tuples would be
inconvenient in this case, since the first value must be modified.
Instead of modifying the tuple (which you can't do), you can create a new
one:

a, b = myDict[key]
myDict[key] = (a+1, b)

It's a bit inefficient, but get it working first, with clear, easy to
understand code, then worry about how efficient it is.
Declaring a class with two attributes as
you suggested is often a good substitute, but if the OP's code is really
what it looks like, I get another code smell because declaring a class
to be used by only 10 lines of code seems like overkill.


But, classes are so lightweight in Python. You can get away with nothing
more than:

class Data:
pass

and then you can do things like:

myData = Data
myData.a = a
myData.b = b

More likely, I would want to write:

class Data:
def __init__ (self, a, b):
self.a = a
self.b = b

but I've done the minimilist Data class more than once. It doesn't cost
much, and it's often more self-documenting than just a tuple.
Aug 29 '05 #6

P: n/a
>> bin = {}
for whatever:
for [a, b] in foo:
x = 42 - a
if bin.has_key(x):
bin[x.b] += 1
else:
bin[x.b] = 1
bin[x.not b] = 0
for x, y, z in bin.iteritems():
print x, y, z

should the dict value become a two element list, or is there a
cleaner way to do this? It would probably help if you explained what the real problem is
you're trying to solve.


actually, that code fragment was meant to do that. it's pretty much
what i needed to do at that point, just the variable names made
simple.
Using a two element list to store a pair of counts has a bad code
smell to me.
exactly. which is why i was asking.
That said, you could write your code something like:
bin = {}
for whatever:
# NOTE: brackets are unnecessary
for a, b in foo:
x = 42 - a
# NOTE: 'in' is generally faster than has_key()
if x in bin
bin[x][0] += 1
else:
bin[x] = [1, 0]
# NOTE: extra parens necessary to unpack count list
for x, (y, z) in bin.iteritems():
print x, y, z


so, to do this using the real names, it looks like

for [start, end, AS, full] in heard:
week = int((start-startDate)/aWeek)
if week in bin:
if full:
bin[week][0] += 1
else:
bin[week][1] += 1
else:
if full:
bin[week] = [1, 0]
else:
bin[week] = [0, 1]
...
for i, (j, k) in bin.iteritems():
if j == 0:
print str(i) + ",," + str(k)
elif k == 0:
print str(i) + "," + str(j)
else:
print str(i) + "," + str(j) + "," + str(k)

which is still pretty darned grotty and unexpressive. of course,
i could be a bit more obscure and do

if week in bin:
bin[week][not full] += 1
else:
bin[week] = [ full, not full ]

except i probably have to coerce the types or something. less
code but less obvious.

randy

Aug 29 '05 #7

P: n/a
I'd write it like this:

bin = {}
for start, end, AS, full in heard:
week = int((start-startDate)/aWeek)
counters = bin.setdefault(week, [0, 0])
if full:
counters[0] += 1
else:
counters[1] += 1

for week, (times_full, times_not_full) in bin.iteritems():
print ...

Seriously, use setdefault() as often as you can. It may be a little
awkward at first, but after a little bit, you instantly know what it
means. It takes out a whole if/else statement which will make your code
smaller and more readable at the same time.

The list is actually representing a struct. Regardless of how it
smells, it's tight, small, and elegant. (This is the sort of problem
where Python shines.) I do stuff like this all the time. Lists are so
lightweight that there really isn't a better way to solve this problem.
Anything else either involves more code or is less readable, in my
opinion.

Using an idea you used earlier, you could get smaller code by saying:
for start, end, AS, full in heard:
week = int((start-startDate)/aWeek)
counters = bin.setdefault(week, [0, 0])
counters[not full] += 1

Or
for start, end, AS, full in heard:
week = int((start-startDate)/aWeek)
bin.setdefault(week, [0, 0])[not full] += 1

Or even
for start, end, AS, full in heard:
bin.setdefault(int((start-startDate)/aWeek), [0, 0])[not full] += 1

While smaller, those are all too tricky for my taste. I think they're
harder to read. I'd personally use the if/else. YMMV.
Using lists to represent structs is perfectly fine if the list doesn't
live longer than about one screen of code. If the list-struct gets
returned from a top-level function and gets used elsewhere, then you
should start to worry. It's too easy to forget what each element is
supposed to represent if you have to scroll your editor to see the place
where the list was created. That, in my opinion, would smell fishy. In
that case, you should probably represent your struct with a class or a
dict so that each element has a name. On the flip side, using such a
heavy solution for such a simple problem also smells bad. The solution
should should be as complicated as the problem -- no more, no less.

Adam
Randy Bush wrote:
bin = {}
for whatever:
for [a, b] in foo:
x = 42 - a
if bin.has_key(x):
bin[x.b] += 1
else:
bin[x.b] = 1
bin[x.not b] = 0
for x, y, z in bin.iteritems():
print x, y, z

should the dict value become a two element list, or is there a
cleaner way to do this?


It would probably help if you explained what the real problem is
you're trying to solve.

actually, that code fragment was meant to do that. it's pretty much
what i needed to do at that point, just the variable names made
simple.

Using a two element list to store a pair of counts has a bad code
smell to me.

exactly. which is why i was asking.

That said, you could write your code something like:
bin = {}
for whatever:
# NOTE: brackets are unnecessary
for a, b in foo:
x = 42 - a
# NOTE: 'in' is generally faster than has_key()
if x in bin
bin[x][0] += 1
else:
bin[x] = [1, 0]
# NOTE: extra parens necessary to unpack count list
for x, (y, z) in bin.iteritems():
print x, y, z

so, to do this using the real names, it looks like

for [start, end, AS, full] in heard:
week = int((start-startDate)/aWeek)
if week in bin:
if full:
bin[week][0] += 1
else:
bin[week][1] += 1
else:
if full:
bin[week] = [1, 0]
else:
bin[week] = [0, 1]
...
for i, (j, k) in bin.iteritems():
if j == 0:
print str(i) + ",," + str(k)
elif k == 0:
print str(i) + "," + str(j)
else:
print str(i) + "," + str(j) + "," + str(k)

which is still pretty darned grotty and unexpressive. of course,
i could be a bit more obscure and do

if week in bin:
bin[week][not full] += 1
else:
bin[week] = [ full, not full ]

except i probably have to coerce the types or something. less
code but less obvious.

randy


Aug 29 '05 #8

P: n/a
Randy Bush wrote:
... i could be a bit more obscure and do
if week in bin:
bin[week][not full] += 1
else:
bin[week] = [ full, not full ]


If you cannot take the setdefault advice, at least do:

try:
bin[week][not full] += 1
except KeyError:
bin[week] = [full, not full]

--Scott David Daniels
Sc***********@Acm.Org
Aug 29 '05 #9

P: n/a
Adam Tomjack wrote:
I'd write it like this:

bin = {}
for start, end, AS, full in heard:
week = int((start-startDate)/aWeek)
counters = bin.setdefault(week, [0, 0])
if full:
counters[0] += 1
else:
counters[1] += 1

for week, (times_full, times_not_full) in bin.iteritems():
print ...

Seriously, use setdefault() as often as you can. It may be a little
awkward at first, but after a little bit, you instantly know what it
means. It takes out a whole if/else statement which will make your code
smaller and more readable at the same time.


However, you should be aware that it can make your code slower. If the
default value is expensive to create,

value = dct.setdefault(key, expensive_func())

will often be slower and/or more memory intensive than

try:
value = dct[key]
except KeyError:
value = expensive_func()

or

if key in dct:
value = dct[key]
else:
value = expensive_func()

Personally, I waver back and forth between using setdefault. It's kind
of handy, but it never reads quite right. While I've used it often
enough to grok it pretty quickly, it's still consumes more of my mental
processing than some other approaches.

I also tend to agree with the assessment[1] (I believe due to Raymond
Hettinger) that setdefault is poorly designed, and should instead set a
default value for the entire dictionary, e.g. so that you could do:

counts = {}
counts.setdefault(value=0)
for elem in data:
counts[elem] += 1

Unfortunately, this is pretty badly backwards incompatible, so I can
only really hope for this change in Python 3.0 which is still a number
of years off.

STeVe

[1]http://wiki.python.org/moin/Python3%2e0Suggestions#head-b384410f8b2dc16fd74c9eec764680e428643d73
Aug 29 '05 #10

P: n/a
me********@aol.com wrote:
import csv
temp1 = []
temp2 = []
reader = csv.reader(file(r"py_monsters.csv"))
for rec in reader:
temp1.append(rec)
for i in temp1[1:]:
temp2.append((i[0],dict(zip(temp1[0][1:],i[1:]))))
monsters = dict(temp2)


I would tend to write this as:

import csv
reader = csv.reader(file(r"py_monsters.csv"))
labels = reader.next()[1:]
monsters = dict((row[0], dict(zip(labels, row[1:])))
for row in reader)

which I believe gives equivalent output:

py> csv_text = """\
....
name,hardiness,agility,friend,courage,room,weight, special_def_odds,armor,weapon,odds,dice,side,hits, reaction,desc
.... PIRATE,5,20,0,10,26,300,0,0,11,60,1,10,0,"not met",You see a man
with a beard and a brass ring in his ear. He is wearing clothes made
of silk and is wielding a very fancily engraved sword."""
py> f = StringIO.StringIO(csv_text)
py> reader = csv.reader(f)
py> labels = reader.next()[1:]
py> monsters = dict((row[0], dict(zip(labels, row[1:])))
.... for row in reader)
py> monsters
{'PIRATE': {'reaction': 'not met', 'agility': '20', 'room': '26',
'weight': '300', 'armor': '0', 'weapon': '11', 'hits': '0', 'side':
'10', 'special_def_odds': '0', 'courage': '10', 'hardiness': '5',
'desc': 'You see a man with a beard and a brass ring in his ear. He is
wearing clothes made of silk and is wielding a very fancily engraved
sword.', 'odds': '60', 'friend': '0', 'dice': '1'}}

STeVe
Aug 29 '05 #11

P: n/a
Randy Bush wrote:
Steven Bethard wrote:
It would probably help if you explained what the real problem is
you're trying to solve.
actually, that code fragment was meant to do that. it's pretty much
what i needed to do at that point, just the variable names made
simple.


Yeah, I gathered that. Sometimes though, you may not ever need to get
to "that point" if the surrounding code can be properly reorganized.
But I can't tell if that's possible unless you explain what the goal of
the program is, not just how your current code tries to approach that goal.

So I'm going to try to pump you for a little more information here. Is
your goal to count, for each week, how many times it's "full" and how
many times it's "not full"? What do you use the counts for? What does
"full" mean? Is it always a 0 or 1? What's the importance of the
output formatting?
so, to do this using the real names, it looks like

for [start, end, AS, full] in heard:
week = int((start-startDate)/aWeek)
if week in bin:
if full:
bin[week][0] += 1
else:
bin[week][1] += 1
else:
if full:
bin[week] = [1, 0]
else:
bin[week] = [0, 1]
...
for i, (j, k) in bin.iteritems():
if j == 0:
print str(i) + ",," + str(k)
elif k == 0:
print str(i) + "," + str(j)
else:
print str(i) + "," + str(j) + "," + str(k)


For the current code, I'd probably go with something like:

for start, end, AS, full in heard:
week = int((start-startDate)/aWeek)
if week in bin:
bin[week][not full] += 1
else:
# I'm assuming "full" takes the values 0 or 1
# but if not, you can coerce it with bool()
bin[week] = [full, int(not full)]
...
for i, (j, k) in bin.iteritems():
result = [str(i), j and str(j) or '']
# special-case k because if it's zero, the reference
# code drops a comma
if k:
result.append(str(k))
print ','.join(result)

But if you're just trying to count the number of times a week is full or
not full, I'd probably do something like:

for start, end, AS, full in heard:
week = int((start-startDate)/aWeek)
if week in bin:
bin[week].append(full)
else:
bin[week] = [full]
...
for i, fulls in bin.iteritems():
j = sum(fulls)
k = len(fulls) - j
...
STeVe
Aug 29 '05 #12

P: n/a
> bin = {}
for start, end, AS, full in heard:
week = int((start-startDate)/aWeek)
counters = bin.setdefault(week, [0, 0])
if full:
counters[0] += 1
else:
counters[1] += 1
yes! thanks!
Using an idea you used earlier, you could get smaller code by saying:
for start, end, AS, full in heard:
week = int((start-startDate)/aWeek)
counters = bin.setdefault(week, [0, 0])
counters[not full] += 1
Or
for start, end, AS, full in heard:
week = int((start-startDate)/aWeek)
bin.setdefault(week, [0, 0])[not full] += 1
Or even
for start, end, AS, full in heard:
bin.setdefault(int((start-startDate)/aWeek), [0, 0])[not full] += 1
as you say, too clever.
Using lists to represent structs is perfectly fine if the list doesn't
live longer than about one screen of code.


i can definitely see that. in last weeks installment, i buried a
complex trinary tree in a class.

thanks for the advice!

randy

Aug 29 '05 #13

P: n/a
> So I'm going to try to pump you for a little more information here. Is
your goal to count, for each week, how many times it's "full" and how
many times it's "not full"? What do you use the counts for? What does
"full" mean? Is it always a 0 or 1? What's the importance of the
output formatting?
'full' is boolean. it says whether a particular bgp announcement
was for the entire ip address allocation, or is a longer prefix.
e.g., if an allocation was for 666.42.0.0/16 and we heard a bgp
announcement for 666.42.1.0/24 that is !full, while an announcement
for the prefix 666.42.0.0/16 is full.

you asked :-)
for start, end, AS, full in heard:
week = int((start-startDate)/aWeek)
if week in bin:
bin[week][not full] += 1
else:
# I'm assuming "full" takes the values 0 or 1
# but if not, you can coerce it with bool()
bin[week] = [full, int(not full)]


hmmm. this also reads well.

as an old pascal and modula-2 bondage and discipline type, i gotta
say is it a breath of fresh air to be in a language and community
which care about how code reads more than how clever it is.

randy

Aug 29 '05 #14

P: n/a
On 28 Aug 2005 18:54:40 -0700, "me********@aol.com" <me********@aol.com> wrote:
Steven Bethard wrote:
Adam Tomjack wrote:
> Steven Bethard wrote:
> ...
>> Using a two element list to store a pair of counts has a bad code
>> smell to me.
> ...
>
> Why is that?
Note that "code smell"[1] doesn't mean that something is actually wrong,
just that it might be. In Python, pairs are usually handled with
tuples[2], but tuples would be inconvenient in this case, since the
first value must be modified. Declaring a class with two attributes as
you suggested is often a good substitute, but if the OP's code is really
what it looks like, I get another code smell because declaring a class
to be used by only 10 lines of code seems like overkill.

I also get a code smell from a dict holding two-element lists because
I've been writing in Python and answering questions on the Python-list
for a couple of years now, and I've never needed one yet. ;-)

STeVe

[1]http://en.wikipedia.org/wiki/Code_smell
[2]http://www.python.org/doc/faq/general.html#why-are-there-separate-tuple-and-list-data-types


Could you do me a favor and see what this smells like?

I put some data in a csv file (with headers) and this will
bring it in quite simply as a dictionary with [names] as
keys and an attribute dictionary as the value.

py_monsters.csv:

name,hardiness,agility,friend,courage,room,weight ,special_def_odds,armor,weapon,odds,dice,side,hits ,reaction,desc
PIRATE,5,20,0,10,26,300,0,0,11,60,1,10,0,"not met",You see a man with a
beard and a brass ring in his ear. He is wearing clothes made of silk
and is wielding a very fancily engraved sword.
import csv
temp1 = []
temp2 = []
reader = csv.reader(file(r"py_monsters.csv"))
for rec in reader:
temp1.append(rec)
for i in temp1[1:]:
temp2.append((i[0],dict(zip(temp1[0][1:],i[1:]))))
monsters = dict(temp2)

This gives me what I want

[('PIRATE', {'reaction': 'not met',
'agility': '20',
'room': '26',
'weight': '300',
'armor': '0',
'weapon': '11',
'hits': '0',
'side': '10',
'special_def_odds': '0',
'courage': '10',
'hardiness': '5',
'desc': 'You see a man with a beard and a brass ring in his ear.
He is wearing clothes made of silk and is wielding a very fancily
engraved sword.',
'odds': '60',
'friend': '0',
'dice': '1'})]

so that I can now write code like

if monsters['PIRATE']['reaction']=='not met':
print monsters['PIRATE']['desc']


I think if the field names are legal python names, I would rather write that 'if ...'
without all that line noise ;-) E.g., using simple classes (and effectively using
their attribute instance dicts essentially the way you used raw dicts), you could write

if monsters.PIRATE.reaction == 'not met':
print monsters.PIRATE.desc

Also, if the reaction, agility, etc list is fixed, you could use __slots__ for
better efficiency. Also, your __init__ method for the latter could convert
strings to ints for handier use later (unless csv already does that in the mode
you are using).

From the above code I infer that you can do define the requisite classes, so
I'll leave it to you ;-) Note that attribute access opens the door to having
default values as class variables, and using properties to retrieve dynamically
calculated values by name. If you are not familiar with those aspects of classes,
your progammer.agility value can increase markedly with a little study ;-)

E.g., a name like monsters.random_elf could live right
alongside PIRATE and return one a randomly selected elf from a an internal list
of elves or via random selection from a list of elf names defined at the same
level as PIRATE, etc. etc. You could also dynamically configure these guys
according to distance to food and time since last meal, and if you are
carrying food, etc.

With suitable classes you could put instances in various "spaces" defined
by other classes, that define geometric or social or other interactions.

instead of using stupid index numbers.

But the loader seems to have a kind of perl-like odor to it,
i.e., next week I won't understand what it does.

If you have to do something tricky, choose names wisely and
comment the non-obvious ;-)

Regards,
Bengt Richter
Aug 29 '05 #15

P: n/a

Bengt Richter wrote:
On 28 Aug 2005 18:54:40 -0700, "me********@aol.com" <me********@aol.com> wrote:
Steven Bethard wrote:
Adam Tomjack wrote:
> Steven Bethard wrote:
> ...
>> Using a two element list to store a pair of counts has a bad code
>> smell to me.
> ...
>
> Why is that?

Note that "code smell"[1] doesn't mean that something is actually wrong,
just that it might be. In Python, pairs are usually handled with
tuples[2], but tuples would be inconvenient in this case, since the
first value must be modified. Declaring a class with two attributes as
you suggested is often a good substitute, but if the OP's code is really
what it looks like, I get another code smell because declaring a class
to be used by only 10 lines of code seems like overkill.

I also get a code smell from a dict holding two-element lists because
I've been writing in Python and answering questions on the Python-list
for a couple of years now, and I've never needed one yet. ;-)

STeVe

[1]http://en.wikipedia.org/wiki/Code_smell
[2]http://www.python.org/doc/faq/general.html#why-are-there-separate-tuple-and-list-data-types
Could you do me a favor and see what this smells like?

I put some data in a csv file (with headers) and this will
bring it in quite simply as a dictionary with [names] as
keys and an attribute dictionary as the value.

py_monsters.csv:

name,hardiness,agility,friend,courage,room,weight ,special_def_odds,armor,weapon,odds,dice,side,hits ,reaction,desc
PIRATE,5,20,0,10,26,300,0,0,11,60,1,10,0,"not met",You see a man with a
beard and a brass ring in his ear. He is wearing clothes made of silk
and is wielding a very fancily engraved sword.
import csv
temp1 = []
temp2 = []
reader = csv.reader(file(r"py_monsters.csv"))
for rec in reader:
temp1.append(rec)
for i in temp1[1:]:
temp2.append((i[0],dict(zip(temp1[0][1:],i[1:]))))
monsters = dict(temp2)

This gives me what I want

[('PIRATE', {'reaction': 'not met',
'agility': '20',
'room': '26',
'weight': '300',
'armor': '0',
'weapon': '11',
'hits': '0',
'side': '10',
'special_def_odds': '0',
'courage': '10',
'hardiness': '5',
'desc': 'You see a man with a beard and a brass ring in his ear.
He is wearing clothes made of silk and is wielding a very fancily
engraved sword.',
'odds': '60',
'friend': '0',
'dice': '1'})]

so that I can now write code like

if monsters['PIRATE']['reaction']=='not met':
print monsters['PIRATE']['desc']


I think if the field names are legal python names, I would rather write that 'if ...'
without all that line noise ;-) E.g., using simple classes (and effectively using
their attribute instance dicts essentially the way you used raw dicts), you could write

if monsters.PIRATE.reaction == 'not met':
print monsters.PIRATE.desc

Also, if the reaction, agility, etc list is fixed, you could use __slots__ for
better efficiency. Also, your __init__ method for the latter could convert
strings to ints for handier use later (unless csv already does that in the mode
you are using).


No, it doesn't as I found out shortly after posting that. What was I
thinking?
I've got it fixed for the moment, but that idea of classes sounds
interesting.

From the above code I infer that you can do define the requisite classes, so
I'll leave it to you ;-)
Guess I'll have to actually start reading the manuals.
Note that attribute access opens the door to having
default values as class variables, and using properties to retrieve dynamically
calculated values by name. If you are not familiar with those aspects of classes,
your progammer.agility value can increase markedly with a little study ;-)

E.g., a name like monsters.random_elf could live right
alongside PIRATE and return one a randomly selected elf from a an internal list
of elves or via random selection from a list of elf names defined at the same
level as PIRATE, etc. etc. You could also dynamically configure these guys
according to distance to food and time since last meal, and if you are
carrying food, etc.

With suitable classes you could put instances in various "spaces" defined
by other classes, that define geometric or social or other interactions.
Well, this is a port of The Wonderful World of Eamon, which isn't quite
that ambitious,
but thanks for the idea about classes. It's one of those many things I
just skim past
without ever understanding. Now would be a good time to start learning
it.

instead of using stupid index numbers.

But the loader seems to have a kind of perl-like odor to it,
i.e., next week I won't understand what it does.

If you have to do something tricky, choose names wisely and
comment the non-obvious ;-)

Regards,
Bengt Richter


Aug 30 '05 #16

This discussion thread is closed

Replies have been disabled for this discussion.