[Tutor] Dictionary and List Question?

Michael Janssen Janssen at rz.uni-frankfurt.de
Thu Nov 13 10:44:22 EST 2003


On Wed, 12 Nov 2003, John P Speno wrote:

> On Wed, Nov 12, 2003 at 09:32:29PM -0500, Clay Shirky wrote:
> > if entry in list:
> >     list[entry] += 1
> > else:
> >     list[entry] = 1
> >
> > but this seems verbose. Is there a way I can say, in one line "increment the
> > value of list[entry], even if its the first time you've seen entry"?
>
> In python, it is better to ask forgivness than permission, so:
>
> try:
>     list[entry] +=1
> except KeyError:
>     list[entry] = 1
>
> I though that the dict's setdefault method might work here, but after
> testing, I don't think it will. It's nice when you have a dict of lists,
> like this:
>
>     list.setdefault(key, []).append(value)

[don't use builtin names like "list" as variables names. Use aList
instead or something else. Second, I will talk about aDict, because
we're discussioning dicts not lists ;-)]


aDict[entry] = aDict.get(entry, 0) +1

works fine. "aDict.get(key, [default]) is the syntax to retrieve a
dictionary value without raising a KeyError.


By the Way:  aDict.setdefault(key, []).append(value) is rather hard to
read. It takes me every time I see it several minutes to understand what
python does behind the scene to make it work.

Well, since I have spend these minutes right away, I will try to explain
why setdefault can be used to append to lists but not to increment an
integer:

The astonishing part of "aDict.setdefault(key, []).append(value)" is
".append(value)": setdefault sets aDict[key] to [] or leave it untouched
depending on if key is given or not. In both ways it also returns the
value.

".append(value)" works on this returned value. It seems like it doesn't
work on the list inside of aDict. Why then does indeed the change on
the return-value of setdefault reflects in the list within aDict (but
wouldn't reflect on a integer inside aDict?)?

aDict = {}
aDict["k1"] = 1
aDict["k2"] = [1]

aDict.setdefault("k1", 1) +1 # or aDict.setdefault("k1", 1).__add__(1)
ret_val = aDict.setdefault("k2", [1]) # in two steps to make it obvious
ret_val.append(1)

print aDict["k1"] # ---> 1
print aDict["k2"] # ---> [1, 1] ---> why?


The important difference between integers and lists to explain this
behaviour is immutability vs mutability plus the python reference
mechanism: Python doesn't generate true copies of objects when another
reference is created:

aList = []
not_a_true_copy_of_aList = aList

are references to the same object:

aList.append("anEntry")
print not_a_true_copy_of_aList # --> "anEntry"

This is why one can work on the list returned by setdefault and change
the list within aDict, because both lists are references to the same
object. Why isn't this possible for integers? Integers are immutuable.
This means, it's not possible to change them in place: any attempt to
change a immutuable type results in a another object:

a = b = 1

are References to the same object but after any change to a, the
name a refers to a new object. The name b is yet a reference to the old
object and stays untouched from the change of a. This is why the change
of the return-value of setdefault doesn't reflect on the value stored
within aDict.


Michael






More information about the Tutor mailing list