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

how can I avoid abusing lists?

P: n/a
I have this code:
type1 = [0]
type2 = [0]
type3 = [0]
map = {0:type1, 1:type1, 2:type3, 3:type1, 4:type2} # the real map is
longer than this

def increment(value):
map[value][0] += 1

increment(1)
increment(1)
increment(0)
increment(4)
#increment will actually be called many times through other functions
print type1[0], type2[0], type3[0]
#should print "3 1 0"

This is exactly what I want to do: every time I encounter this kind of
value in my code, increment the appropriate type by one. Then I'd like
to go back and find out how many of each type there were. This way
I've written seems simple enough and effective, but it's very ugly and
I don't think it's the intended use of lists. Does anyone know a
cleaner way to have the same funtionality?

Thanks,
THN

Jul 7 '06 #1
Share this Question
Share on Google+
16 Replies


P: n/a
Just forget the lists...

counters = {0:0, 1:0, 2:0, 3:0, 4:0}

def increment(value):
counters[value] += 1

increment(1)
increment(1)
increment(3)
increment(4)

print counters[0]
>>0
print counters[1]
>>2
print coutners[2]
>>0
print counters[3]
>>1
print coutners[4]
>>1
The increment function should probably include a try:...except:
statement to catch KeyErrors that would arise if you passed a value
that is not a key in the counters dictionary.

Rob C

Jul 7 '06 #2

P: n/a
Ant

Rob Cowie wrote:
Just forget the lists...
counters = {0:0, 1:0, 2:0, 3:0, 4:0}
Or perhaps just use a list:
>>counters = [0,0,0,0]
def inc(v):
.... counters[v] += 1
....
>>inc(1)
inc(1)
inc(3)
counters
[0, 2, 0, 1]
The increment function should probably include a try:...except:
Likewise for IndexErrors

Jul 7 '06 #3

P: n/a
Thomas Nelson wrote:
I have this code:
type1 = [0]
type2 = [0]
type3 = [0]
map = {0:type1, 1:type1, 2:type3, 3:type1, 4:type2} # the real map is
longer than this

def increment(value):
map[value][0] += 1

increment(1)
increment(1)
increment(0)
increment(4)
#increment will actually be called many times through other functions
print type1[0], type2[0], type3[0]
#should print "3 1 0"

This is exactly what I want to do: every time I encounter this kind of
value in my code, increment the appropriate type by one. Then I'd like
to go back and find out how many of each type there were. This way
I've written seems simple enough and effective, but it's very ugly and
I don't think it's the intended use of lists. Does anyone know a
cleaner way to have the same funtionality?

Thanks,
THN
In this case, lists are unnecessary. Just use ints.

Since your "type codes" are ints, you can create your map like this
(assuming 10 types):

map = dict((n, 0) for n in range(10))
Then your increment function becomes:

def increment(value):
map[value] += 1
And instead of,
print type1[0], type2[0], type3[0]

say,
print map[0], map[1], map[2]
Peace,
~Simon

Jul 7 '06 #4

P: n/a
Thomas Nelson wrote:
This is exactly what I want to do: every time I encounter this kind of
value in my code, increment the appropriate type by one. Then I'd like
to go back and find out how many of each type there were. This way
I've written seems simple enough and effective, but it's very ugly and
I don't think it's the intended use of lists. Does anyone know a
cleaner way to have the same funtionality?

Thanks,
THN
Just assign each type a number (type1 -1, type2 -2) and then count
the values as usual

def count(map, it):
d={}
for x in it:
x = map[x] #only difference from normal count function
#d[x]=d.get(x,0)+1
if x in d:
d[x] +=1
else:
d[x] = 1
return d
>>map = {0:1, 1:1, 2:3, 3:1, 4:2}
count(map, [1,1,0,4])
{1: 3, 2: 1}
>>for x in count(map, [1,1,0,4]).items():
.... print 'type%d: %d' %x
....
type1: 3
type2: 1

Jul 7 '06 #5

P: n/a
Just forget the lists...
>
counters = {0:0, 1:0, 2:0, 3:0, 4:0}
You'll notice that the OP's code had multiple references to the
same counter (0, 1, and 3 all mapped to type1)

The OP's method was about as good as it gets. One might try to
redo it with an accumulator class of some sort:

class Accumulator(object):
def __init__(self, startvalue = 0):
self.counter = startvalue
def __iadd__(self, qty):
self.counter += qty
return self.counter
def add(self, qty = 1):
self.counter += qty
def __int__(self):
return self.counter
def __str__(self):
return str(self.counter)
def __repr__(self):
return '<Accumulator 0x%x (%s)>' % (
id(self), str(self.counter))

type1 = Accumulator()
type2 = Accumulator()
type3 = Accumulator()
d = {0:type1, 1:type1, 2:type3, 3:type1, 4:type2}

print ','.join([str(x) for x in d.values()])
# all zeros
d[0] += 1
print ','.join([str(x) for x in d.values()])
# d[0], d[1], and d[3] have incremented
d[2].add()
d[2].add()
print ','.join([str(x) for x in d.values()])
# d[2] has now incremented twice
d[4].add(5)
print ','.join([str(x) for x in d.values()])
# d[4] has now incremented by 5

Some of the syntactic sugar of the class could likely be left out
if you just want, but it does the same thing as the OP's, with a
diff. spin on the syntax.

-tkc


Jul 7 '06 #6

P: n/a
Thomas Nelson wrote:
I have this code:
type1 = [0]
type2 = [0]
type3 = [0]
map = {0:type1, 1:type1, 2:type3, 3:type1, 4:type2}
Warning : you're shadowing the builtin map() function.
# the real map is
longer than this

def increment(value):
map[value][0] += 1

increment(1)
increment(1)
increment(0)
increment(4)
#increment will actually be called many times through other functions
print type1[0], type2[0], type3[0]
#should print "3 1 0"

This is exactly what I want to do: every time I encounter this kind of
value in my code, increment the appropriate type by one. Then I'd like
to go back and find out how many of each type there were. This way
I've written seems simple enough and effective, but it's very ugly and
I don't think it's the intended use of lists.
Not really.
Does anyone know a
cleaner way to have the same funtionality?

# first replace the list hack
class Counter(object):
def __init__(self, name, *keys):
self.name = name
self.keys = keys
self.count = 0

def inc(self):
self.count += 1

# now wrap the whole thing in a convenient way
class Counters(object):
def __init__(self, *counters):
self._counters = dict()
self._counters_map = dict()
for counter in counters:
assert counter.name not in self._counters
self._counters[counter.name] = counter
for key in counter.keys:
assert key not in self._counters_map
self._counters_map[key] = counter

def __getattr__(self, name):
return self._counters[name].count

def __getitem__(self, key):
return self._counters_map[key].count

def __call__(self, key):
self._counters_map[key].inc()

# and finally, let's use it:
increment = Counters(Counter("type1", 0, 1, 3),
Counter("type2", 4),
Counter("type3", 2)
)

increment(1)
increment(1)
increment(0)
increment(4)

print increment.type1, increment.type2, increment.type3
HTH
--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in 'o****@xiludom.gro'.split('@')])"
Jul 7 '06 #7

P: n/a

Thomas Nelson wrote:
I have this code:
type1 = [0]
type2 = [0]
type3 = [0]
map = {0:type1, 1:type1, 2:type3, 3:type1, 4:type2} # the real map is
longer than this

def increment(value):
map[value][0] += 1

increment(1)
increment(1)
increment(0)
increment(4)
#increment will actually be called many times through other functions
print type1[0], type2[0], type3[0]
#should print "3 1 0"

This is exactly what I want to do: every time I encounter this kind of
value in my code, increment the appropriate type by one. Then I'd like
to go back and find out how many of each type there were. This way
I've written seems simple enough and effective, but it's very ugly and
I don't think it's the intended use of lists. Does anyone know a
cleaner way to have the same funtionality?
I usually do this:
>>histogram = {}
for i in range(1,7):
for j in range(1,7):
s = i + j
if histogram.has_key(s):
histogram[s] += 1
else:
histogram[s] = 1
>>for i in histogram:
print i,histogram[i]

2 1
3 2
4 3
5 4
6 5
7 6
8 5
9 4
10 3
11 2
12 1

Note that only results actually encountered create dictionary
entries, so there is no result for index 0 or 1.
>
Thanks,
THN
Jul 7 '06 #8

P: n/a
me********@aol.com wrote:
if histogram.has_key(s):
histogram[s] += 1
else:
histogram[s] = 1
I wonder if

histogram[s] = histogram.get(s, 0) + 1

would be more efficient...

Cheers,

--
Klaus Alexander Seistrup
Copenhagen, Denmark
http://ipsum.dk/
Jul 7 '06 #9

P: n/a
Thomas Nelson wrote:
I have this code:
type1 = [0]
type2 = [0]
type3 = [0]
map = {0:type1, 1:type1, 2:type3, 3:type1, 4:type2} # the real map is
longer than this

def increment(value):
map[value][0] += 1

increment(1)
increment(1)
increment(0)
increment(4)
#increment will actually be called many times through other functions
print type1[0], type2[0], type3[0]
#should print "3 1 0"

This is exactly what I want to do: every time I encounter this kind of
value in my code, increment the appropriate type by one. Then I'd like
to go back and find out how many of each type there were. This way
I've written seems simple enough and effective, but it's very ugly and
I don't think it's the intended use of lists. Does anyone know a
cleaner way to have the same funtionality?
I don't think your code is ugly. Anyway, here are two more alternatives:
>>types = [0] * 3
dispatch = [0, 0, 2, 0, 1]
for value in [1, 1, 0, 4]:
.... types[dispatch[value]] += 1
....
>>types
[3, 1, 0]

>>inflated = [0] * 5
groups = [[0, 1, 3], [4], [2]]
for value in [1, 1, 0, 4]:
.... inflated[value] += 1
....
>>[sum(inflated[i] for i in group) for group in groups]
[3, 1, 0]

Peter

Jul 7 '06 #10

P: n/a
In article <11*********************@s13g2000cwa.googlegroups. com>, Thomas Nelson wrote:
This is exactly what I want to do: every time I encounter this kind of
value in my code, increment the appropriate type by one. Then I'd like
to go back and find out how many of each type there were. This way
I've written seems simple enough and effective, but it's very ugly and
I don't think it's the intended use of lists. Does anyone know a
cleaner way to have the same funtionality?
How about this:

map = {}
def increment(value):
map[value] = map.get(value, 0) + 1

?
Jul 7 '06 #11

P: n/a

Tim Chase wrote:
>
You'll notice that the OP's code had multiple references to the
same counter (0, 1, and 3 all mapped to type1)

The OP's method was about as good as it gets. One might try to
D'oh! Didn't notice that.

Yeah, Thomas, if you really do want more than "type code" (i.e. key to
your map dict) to map to the same type counter then IMHO your original
method is very apt.

Peace,
~Simon

Jul 7 '06 #12

P: n/a
Thanks to everyone who posted. First, I don't think my question was
clear enough: Rob Cowie, Ant, Simon Forman, me********@aol.com, and Jon
Ribbens offered solutions that don't quite work as-is, because I need
multiple values to map to a single type. Tim Chase and Bruno
Destuilliers both offer very nice OOP solutions, and I think this is
the route I will probably go. However, for the simplest and easiest
solution, I really like this from Peter Otten:
inflated = [0]*5
groups = [[0,1,3],[4],[2]]
for value in [1,1,0,4]:
inflated[value] += 1
print [sum(inflated[i] for i in group) for group in groups]

4 lines (one more to assign the lists to name values, but that's
minor), and intuitive. If I had just thought of this to begin with, I
wouldn't have bothered posting.

Thanks to all for the advice.

THN

Jul 7 '06 #13

P: n/a
No, your question was clear. With hindsght and a more thorough read of
your post I see my error ;^)

Jul 7 '06 #14

P: n/a
Thomas Nelson wrote:
Thanks to everyone who posted. First, I don't think my question was
clear enough: Rob Cowie, Ant, Simon Forman, me********@aol.com, and Jon
Ribbens offered solutions that don't quite work as-is, because I need
multiple values to map to a single type. Tim Chase and Bruno
Destuilliers both offer very nice OOP solutions, and I think this is
the route I will probably go. However, for the simplest and easiest
solution, I really like this from Peter Otten:
inflated = [0]*5
groups = [[0,1,3],[4],[2]]
for value in [1,1,0,4]:
inflated[value] += 1
print [sum(inflated[i] for i in group) for group in groups]

4 lines (one more to assign the lists to name values, but that's
minor), and intuitive. If I had just thought of this to begin with, I
wouldn't have bothered posting.

Thanks to all for the advice.

THN
I want to be sure I understand the above.

inflated is a list of counters, one for each "type code"?

groups is a list of groups of "type codes", one for each actual "type"?

The for loop simulates four calls to an "increment()" function?

The list comprehension sums all the counters that correspond to each
actual type?

so you could write:

type1, type2, type3 = groups = [[0,1,3],[4],[2]]

Is that right?
Wow, very nice.
Peace,
~Simon
It was too easy to read
map = {0:type1, 1:type1, 2:type3, 3:type1, 4:type2}
as
map = {0:type1, 1:type2, 2:type3, 3:type4, 4:type5}

sorry.

Jul 7 '06 #15

P: n/a
Peter Otten a écrit :
(snip)
I don't think your code is ugly. Anyway, here are two more alternatives:

>>>>types = [0] * 3
dispatch = [0, 0, 2, 0, 1]
for value in [1, 1, 0, 4]:
... types[dispatch[value]] += 1
...
>>>>types

[3, 1, 0]
I wonder why I'm still pretending to be a programmer
>
>>>>inflated = [0] * 5
groups = [[0, 1, 3], [4], [2]]
for value in [1, 1, 0, 4]:
... inflated[value] += 1
...
>>>>[sum(inflated[i] for i in group) for group in groups]

[3, 1, 0]
me-change-job-me-go-selling-pizzas :(

Jul 7 '06 #16

P: n/a
In article <11**********************@s16g2000cws.googlegroups .com>,
Rob Cowie <co*******@gmail.comwrote:
>Just forget the lists...

counters = {0:0, 1:0, 2:0, 3:0, 4:0}

def increment(value):
counters[value] += 1

increment(1)
increment(1)
increment(3)
increment(4)

print counters[0]
>>>0
print counters[1]
>>>2
print coutners[2]
>>>0
print counters[3]
>>>1
print coutners[4]
>>>1

The increment function should probably include a try:...except:
statement to catch KeyErrors that would arise if you passed a value
that is not a key in the counters dictionary.

Rob C
counters = {}
def increment(value):
counters[value] = counters.get(value, 0) + 1

increment(1)
increment(1)
increment(3)
increment(4)
increment('a string key')
keyz = counters.keys()
keyz.sort()
for k in keyz:
print k, counters[k]

Takes care of IndexError and ValueError. It does not report keys that
don't get incremented. If that's important, then initalise counters as
in the quoted posting.

For Python 2.4 and later, you can replace the keyz =
counts.keys()/keyz.sourt() for k in keyz: with

for k in sorted(counters.heys()):
print k, counters[k]
--
Jim Segrave (je*@jes-2.demon.nl)

Jul 8 '06 #17

This discussion thread is closed

Replies have been disabled for this discussion.