我有以下代码:
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 :(得分:2)
如果默认值很高,请测试密钥:
item = stats.get(field) or very_expensive_initializer(field)
item.add(value)
或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([])
答案 1 :(得分:1)
这里不会使用例外
if field not in stats:
stats[field] = very_expensive_initializer(field)
s = stats[field]
答案 2 :(得分: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')