字典对象相加
SO上看到一个比较好的问题.大概是:
For example I have two dicts:
Dict A:{'a':1,'b':2,'c':3}
Dict B:{'b':3,'c':4,'d':5}
I need a pythonic way of 'combining' two dicts such that the result is :
{'a':1,'b':5,'c':7,'d':5}
That is to say: if a key appears in both dicts, add their values, if it appears in only one dict, keep its value.
我发现有3类答案,每一个都有值得学习的地方.
(1)首先来一个比较酷的解法:
a = {'a': 'foo', 'b':'bar', 'c': 'baz'} b = {'a': 'spam', 'c':'ham', 'x': 'blah'} r = dict(a.items() + b.items() + [(k, a[k] + b[k]) for k in set(b) & set(a)])
这里面用到了一个难以觉察到的设置,举例说明:
>>> dict([(1,2),(1,2),(1,3),(2,3),(2,4)])
{1: 3, 2: 4}
可以看到,在将列表转换为字典的过程中,如果出现了相同的键,那么Python是默认取最后一个键及其对应的值.
另外,set的用法也显得非常简练.作者后来还给出了更普遍的版本:
def combine_dicts(a, b, op=None): op = op or (lambda x, y: x + y) return dict(a.items() + b.items() + [(k, op(a[k], b[k])) for k in set(b) & set(a)])
我才知道OR运算符不是仅仅返回True或False的...
>>> a= None or 2 >>> a 2 >>> 4 or 2 4 >>> not None or 2 True >>>
这位作者曾经解答过我一个遍历树的问题,我花了一天时间冥思苦想,最后在递归和生成器的理解上进步一大截.
这是他SO主页:http://stackoverflow.com/users/989121/thg435
(2)下面来一个面向对象风格的:
class MyDict(dict): def __add__(self, oth): r = self.copy() for key, val in oth.items(): if key in r: r[key] += val # You can custom it here else: r[key] = val return r a = MyDict({'a':1, 'b':2, 'c':3}) b = MyDict({'b':3, 'c':4, 'd':5}) print a+b # Output {'a':1, 'b': 5, 'c': 7, 'd': 5}
原来__add__方法是这么来的.同时我也觉得,面向对象编程还是挺不错的,继承,撰写特殊方法,很方便的嘛.
另外,字典的copy方法原来就是在这种地方起作用的--很方便地返回一个和原字典内容一样的新的字典对象.毕竟2个字典相加后返回的新字典一般都是要求具有独立性的.
以前我一直想不通这个方法是拿来干嘛的..现在是明白一些了.
题外话:出于对copy一贯的警觉,我对字典的copy方法进行了一些测试,发现还是一贯地"陷阱".并非那么独立..
>>> d={1: [], 2: 4} >>> e=d.copy() >>> e {1: [], 2: 4} >>> e[1].append('trap?') >>> d {1: ['trap?'], 2: 4}
可见,当字典的值是可变对象时(比如list),copy是返回一个引用,并非新对象.总之,如果你的字典值包含可变对象,为了确保不发生"惊喜",还是用copy.deepcopy吧:
>>> import copy >>> e=copy.deepcopy(d) >>> e {1: ['trap?'], 2: 4} >>> e[1].append('no_trap') >>> d {1: ['trap?'], 2: 4} >>>
(3)最后是典型的Python风格--找现成轮子,import搞定之:
>>> from collections import Counter >>> A = Counter({'a':1, 'b':2, 'c':3}) >>> B = Counter({'b':3, 'c':4, 'd':5}) >>> A + B Counter({'c': 7, 'b': 5, 'd': 5, 'a': 1})
目前发现Python有以下一些基础库,提供一些不牵扯具体应用的有用的函数:
collections
functools
operator
itertools