[Tutor] How to accomplish Dict setdefault(key, (append to existing value list) or (start new list) )?

Danny Yoo dyoo@hkn.eecs.berkeley.edu
Thu May 15 17:21:01 2003


> I used two lines above for the sake of clarity, so that it's a little
> easier to see what's happening.  The dict method get() will return the
> value associated with the first argument (OrderDate) if there is such a
> value, and will return the second argument if there is no such value.
>  So my first line will give either the previous list of orders for that
> date, or an empty list.  Either way, I append the current order ID to
> the list and put it back in the dictionary.  This can all be done in a
> single line, as well --
>
> for OrderDate, OrderID in Orders:
>     OrderDates[OrderDate] = OrderDates.get(OrderDate, []).append(OrderID)


Hi everyone,


Ah!  There's some confusion here.  There are two methods in dictionaries
that look like they do similar things:

    get()
    setdefault()


But the code above is incorrect; Jeff meant to use setdefault():

###
for OrderDate, OrderID in Orders:
    OrderDates.setdefault(OrderDate, []).append(OrderID)
###


The statements above can be rewritten as:

###
for date, OrderID in Orders:
    if date not in OrderDates:
        OrderDates[date] = []
    OrderDates[date].append(OrderId)
###


That is, setdefault() will first check to see if the value in the
dictionary needs to be initialized, and will set it up to some default if
it hasn't been set yet.

###
>>> x = {}
>>> x.setdefault('one', 1)
1
>>> x
{'one': 1}
###

And as a shortcut, setdefault() returns that value back to us --- that's
where it sorta looks like get().




When we see something like:

    OrderDates.get(date, [])

we can sorta decompose it to:

    if date in OrderDate:
        return OrderDate[date]
    else:
        return []


(This translation is not quite correct; it should really be an expression,
not a set of statements.  A closer translation might be:

    (date in OrderDate and OrderDate[date] or [])

except that this is not correct either!  Can anyone see why?  This is very
pertinent to the arguments for the "ternary" conditional expression.)

Anyway, the main thing to see is that there is a difference between
setdefault() and get()!  The setdefault() does make changes to the
dictionary, but the get(), by itself, doesn't do so.



Going back to the original code:

###
for OrderDate, OrderID in Orders:
    OrderDates[OrderDate] = OrderDates.get(OrderDate, []).append(OrderID)
###


The key to the bug is that:

    OrderDates.get(OrderDate, []).append(OrderID)


is guaranteed to be 'None', since that's what the append() call will
return.  That is, it's doing something similar to:

###
for OrderDate, OrderID in Orders:
    temp_value = OrderDates.get(OrderDate, [])
    OrderDates[OrderDate] = temp_value.append(OrderID)
###


And that's why we're getting None in all of the OrderDates.




If we really wanted to use get(), we can correct the code like this:


###
for OrderDate, OrderID in Orders:
    OrderDates[OrderDate] = OrderDates.get(OrderDate, [])
    OrderDates[OrderDate].append(OrderID)
###



If you have more questions about this, please feel free to ask!