2012-05-18 5 views
0

Предположим, я хочу создать словарь, который отображает цифры для чисел меньше, чем 100, заканчивающихся в этих цифр следующим образом:Ошибки при изменении словаря в Python

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 

Прежде всего, когда я в 20, d [ r], по-видимому, является NoneType, когда я пытаюсь добавить его, выбросив ошибку. Почему это должно быть? Во-вторых, я чувствую, что мой подход неэффективен, так как работа по проверке, не распространяется ли r в d. Что-то вроде этого было бы лучше, я чувствую:

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

Есть ли способ иметь эту логику в python?

+3

'd [r] = d [r] .append (i) 'должно быть' d [r] .append (i) '. 'list.append' изменяет список и не возвращает новый список. –

ответ

1

Прежде всего, когда i равно 20, d [r], по-видимому, является NoneType, когда я пытаюсь добавить к нему, выбросив ошибку. Почему это должно быть?

Это происходит потому, что следующий код является неправильным:

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

.append изменяет список в качестве побочного эффекта и возвращает None. Поэтому после добавления списка он отбрасывается и заменяется значением None, которое теперь переустанавливается в d[r].

Есть ли способ иметь эту логику в python?

Существует множество хаков, которые могут быть использованы, но ни один из них здесь не подходит.

Вместо этого устраните конкретную проблему: «измените значение словаря, если оно есть, или создайте новое значение в противном случае». Это можно уточнить, чтобы «создать пустое значение по умолчанию, если оно отсутствует, а затем изменить значение, которое теперь гарантировано для присутствия».

Вы можете сделать это с помощью .setdefault или более элегантно, вы можете заменить словарь с collections.defaultdict:

from collections import defaultdict 

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

Или вы можете решить еще более конкретные задачи: «создать словарь с заданным шаблоном» , т.е. от применения правила или формулы к входной последовательности (в данном случае, вход 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)} 

или вы можете решить еще более конкретные проблемы, воспользовавшись тем, что range принимает другой аргумент, чтобы определить размер шага:

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

Словарь понимает только в Python 3, это правильно? –

+3

Python 2.7 и выше. – Blender

0

С предложением Эндрю использовать d[r].append(i), вы получите желаемый ответ:

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]} 

Вы могли бы сделать это:

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