2013-12-04 2 views
2

У меня есть следующий код:Как инициализировать значения в словаре?

import json 
stats = dict() 
for line in input : # many many lines  
    for (field,value) in json.loads(line).iteritems() : 
     stats.get(field,very_expensive_initializer(field)).add(value) 

проблема является то, что Python не лень, поэтому very_expensive_initializer вызывается один раз для каждого поля в каждой строке, а не раз для каждого поля. Правильно?

Каков правильный способ избежать этого?

Например, достаточно ли идиоматично?

try : s = stats[field] 
except KeyError : s = stats[field] = very_expensive_initializer(field) 
s.add(value) 
+0

не должны 'статистике = DICT()' 'быть статистика = dict' –

+0

@KDawG Нет, это не должно быть –

+0

@DavidHeffernan, но не вызывает ли объект 'TypeError: 'dict' не вызываемый' –

ответ

2

Если по умолчанию стоит дорого, тест на ключ:

или если stats[field] может быть пустым:

item = stats[field] if field in stats else very_expensive_initializer(field) 
item.add(value) 

Оба or и условное выражение оцениваются лениво.

Вы все еще можете сделать это одним лайнером, но я не уверен, что вам следует. Вышеупомянутое соответствует вашему исходному коду.

Отметьте, что это не Добавить very_expensive_initializer(field) в stats! Вы можете сделать это тоже:

if field not in stats: 
    stats[field] = very_expensive_initializer(field) 

или

try: 
    item = stats[field] 
except KeyError: 
    item = stats[field] = very_expensive_initializer(field) 

где вы выбираете первый, если field является обычно не найден в stats, последний, если field только иногда не найден в stats.

Вашего следующий вариант подкласс dict и добавить __missing__ метод:

class subclassed_dict(dict): 
    def __missing__(self, key): 
     item = self[key] = very_expensive_initializer(key) 
     return item 

затем использовать это в качестве stats:

stats = subclassed_dict() 

Python будет вызывать __missing__ всякий раз, когда вы пытаетесь получить доступ к ключу, который не но там.

Демонстрация:

>>> def very_expensive_initializer(field): 
...  print 'Doing loads of work' 
...  return set() 
... 
>>> class subclassed_dict(dict): 
...  def __missing__(self, key): 
...   item = self[key] = very_expensive_initializer(key) 
...   return item 
... 
>>> stats = subclassed_dict() 
>>> stats['foo'].add(2) 
Doing loads of work 
>>> stats['foo'].add(3) 
>>> stats['foo'].add(4) 
>>> stats['bar'] 
Doing loads of work 
set([]) 
>>> stats['bar'] 
set([]) 
+0

ни одно решение не помещает вновь созданное значение в 'stats' dict – sds

1

бы не использовать исключения здесь

if field not in stats: 
    stats[field] = very_expensive_initializer(field) 

s = stats[field] 
+0

Исключения являются отличным выбором, если поле * обычно * присутствует в словаре. См. [Прощение Python vs. Permission and Duck Typing] (http://programmers.stackexchange.com/a/175663) –

0

Это лучше подходит кажется для десериализатор

http://docs.python.org/2/library/json.html#encoders-and-decoders

>>> import json 
>>> def as_complex(dct): 
...  if '__complex__' in dct: 
...   return complex(dct['real'], dct['imag']) 
...  return dct 
... 
>>> json.loads('{"__complex__": true, "real": 1, "imag": 2}', 
...  object_hook=as_complex) 
(1+2j) 
>>> import decimal 
>>> json.loads('1.1', parse_float=decimal.Decimal) 
Decimal('1.1') 
Смежные вопросы