简体   繁体   中英

Errors when modifying a dictionary in python

Suppose I want to create a dictionary that maps digits to numbers less than 100 ending in those digits as follows:

d = {}
for i in range(100):
 r = i % 10
 if r in d:
  d[r] = d[r].append(i)
 else:
  d[r] = [i]
print d

First of all, when i is 20, d[r] is apparently a NoneType when I try to append to it, throwing an error. Why would this be? Secondly, I feel like my approach is inefficient, as the work in checking if r in d isn't propagated. Something like this would be better, I feel:

case(d[r]) of 
   SOME(L) => d[r] = L.append(i)
 | NONE => d[r] = [i]

Is there a way to have that logic in python?

First of all, when i is 20, d[r] is apparently a NoneType when I try to append to it, throwing an error. Why would this be?

This is because the following code is wrong:

d[r] = d[r].append(i)

.append modifies the list as a side effect , and returns None . So after the list is appended to, it gets thrown away and replaced with the None value now being re-assigned into d[r] .

Is there a way to have that logic in python?

There are a variety of hacks that can be used, but none of them are appropriate here.

Instead, solve the specific problem: "modify a dictionary value if present, or create a new value otherwise". This can be refined into "create an empty default value if absent, and then modify the value now guaranteed to be present".

You can do that using .setdefault , or more elegantly, you can replace the dictionary with a collections.defaultdict :

from collections import defaultdict

d = defaultdict(list)
for i in range(100):
    r = i % 10
    d[r].append(i)

Or you can solve the even more specific problem: "create a dictionary with a given pattern", ie from applying a rule or formula to an input sequence (in this case, the input is range(100) :

from itertools import groupby

def last_digit(i): return i % 10
d = {k: list(v) for k, v in groupby(sorted(range(100), key=last_digit), last_digit)}

Or you can solve the even more specific problem, by taking advantage of the fact that range takes another argument to specify a step size:

d = {i: range(i, 100, 10) for i in range(10)}

With Andrew's suggestion to use d[r].append(i) , you get the desired answer:

In [3]: d
Out[3]: 
{0: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90],
 1: [1, 11, 21, 31, 41, 51, 61, 71, 81, 91],
 2: [2, 12, 22, 32, 42, 52, 62, 72, 82, 92],
 3: [3, 13, 23, 33, 43, 53, 63, 73, 83, 93],
 4: [4, 14, 24, 34, 44, 54, 64, 74, 84, 94],
 5: [5, 15, 25, 35, 45, 55, 65, 75, 85, 95],
 6: [6, 16, 26, 36, 46, 56, 66, 76, 86, 96],
 7: [7, 17, 27, 37, 47, 57, 67, 77, 87, 97],
 8: [8, 18, 28, 38, 48, 58, 68, 78, 88, 98],
 9: [9, 19, 29, 39, 49, 59, 69, 79, 89, 99]}

You could do this:

In [7]: for onesdigit in range(10):
   ...:     d[onesdigit] = range(onesdigit, 100, 10)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM