defaultdict 与元组
我想做以下事情:
d = defaultdict((int,float))
for z in range( lots_and_lots):
d['operation one'] += (1,5.67)
...
...
d['operation two'] += (1,4.56)
然后输出每个操作被调用的次数和浮点值的总和。
for k,v in d.items():
print k, 'Called', v[0], 'times, total =', v[1]
但是我不知道怎么实现这一点,因为你不能把元组作为参数传给defaultdict,而且你也不能把一个元组加到另一个元组里去求和,这样只会得到多余的值在你的元组里。也就是说:
>>> x = (1,0)
>>> x+= (2,3)
>>> x
(1, 0, 2, 3)
而不是
>>> x = (1,0)
>>> x+= (2,3)
>>> x
(3,3)
我该怎么才能实现我想要的呢?
6 个回答
22
传给 defaultdict
的参数必须是一个“可调用的”东西,也就是说它需要能返回一个默认值。你可以这样定义你的默认字典:
d = defaultdict(lambda: (0, 0.0))
像 int
和 float
这样的类型可以被调用并返回零,这其实是个方便的特性,但并不是 defaultdict
工作的关键。
使用 +=
会有点麻烦,因为在元组之间进行加法操作实际上是把元组合并在一起,所以你得用比较繁琐的方法来实现:
left, right = d["key"]
d["key"] = (left + 2, right + 3)
编辑: 如果你真的必须使用 +=
,只要你有一个支持所需操作的集合类型,就可以这么做。fileoffset 建议 使用 numpy
数组类型,这可能是个不错的主意,但你也可以通过继承 tuple
并重写你需要的操作符来得到一个接近的效果:以下是一个粗略的示例:
class vector(tuple):
def __add__(self, other):
return type(self)(l+r for l, r in zip(self, other))
def __sub__(self, other):
return type(self)(l-r for l, r in zip(self, other))
def __radd__(self, other):
return type(self)(l+r for l, r in zip(self, other))
def __lsub__(self, other):
return type(self)(r-l for l, r in zip(self, other))
from collections import defaultdict
d = defaultdict(lambda:vector((0, 0.0)))
for k in range(5):
for j in range(5):
d[k] += (j, j+k)
print d
我们其实不需要(也不想)真正重载 +=
操作符(也就是 __iadd__
),因为 tuple
是不可变的。如果你提供了加法,Python 会正确地用新值替换旧值。
28
你可以使用 collections.Counter 来汇总结果:
>>> from collections import Counter, defaultdict
>>> d = defaultdict(Counter)
>>> d['operation_one'].update(ival=1, fval=5.67)
>>> d['operation_two'].update(ival=1, fval=4.56)
5
我猜你有太多的操作,不能简单地把每个条目的值列表存起来吧?
d = defaultdict(list)
for z in range(lots_and_lots):
d['operation one'].append(5.67)
...
...
d['operation two'].append(4.56)
for k,v in d.items():
print k, 'Called', len(v), 'times, total =', sum(v)
你可以做的一个方法是创建一个自定义的增量器:
class Inc(object):
def __init__(self):
self.i = 0
self.t = 0.0
def __iadd__(self, f):
self.i += 1
self.t += f
return self
然后
d = defaultdict(Inc)
for z in range(lots_and_lots):
d['operation one'] += 5.67
...
...
d['operation two'] += 4.56
for k,v in d.items():
print k, 'Called', v.i, 'times, total =', v.t