虽然能够在字典的键之间执行集合操作非常有用,但我经常希望我可以对字典本身执行集合操作。
我找到了一些用于区分两本词典的方法,但我发现它们非常冗长,并且觉得必须有更多的 Pythonic 答案。
虽然能够在字典的键之间执行集合操作非常有用,但我经常希望我可以对字典本身执行集合操作。
我找到了一些用于区分两本词典的方法,但我发现它们非常冗长,并且觉得必须有更多的 Pythonic 答案。
tl;dr 配方:{k:d1.get(k, k in d1 or d2[k]) for k in set(d1) | set(d2)}
并且|
可以用任何其他集合运算符替换。
根据@torek 的评论,另一个可能更容易记住的食谱(虽然是完全通用的)是:{k:d1.get(k,d2.get(k)) for k in set(d1) | set(d2)}
.
完整答案如下:
我的第一个答案没有正确处理评估为 False 的值。这是处理 Falsey 值的改进版本:
>>> d1 = {'one':1, 'both':3, 'falsey_one':False, 'falsey_both':None}
>>> d2 = {'two':2, 'both':30, 'falsey_two':None, 'falsey_both':False}
>>>
>>> print "d1 - d2:", {k:d1[k] for k in d1 if k not in d2} # 0
d1 - d2: {'falsey_one': False, 'one': 1}
>>> print "d2 - d1:", {k:d2[k] for k in d2 if k not in d1} # 1
d2 - d1: {'falsey_two': None, 'two': 2}
>>> print "intersection:", {k:d1[k] for k in d1 if k in d2} # 2
intersection: {'both': 3, 'falsey_both': None}
>>> print "union:", {k:d1.get(k, k in d1 or d2[k]) for k in set(d1) | set(d2)} # 3
union: {'falsey_one': False, 'falsey_both': None, 'both': 3, 'two': 2, 'one': 1, 'falsey_two': None}
for 的版本union
是最通用的,可以转成函数:
>>> def dict_ops(d1, d2, setop):
... """Apply set operation `setop` to dictionaries d1 and d2
...
... Note: In cases where values are present in both d1 and d2, the value from
... d1 will be used.
... """
... return {k:d1.get(k,k in d1 or d2[k]) for k in setop(set(d1), set(d2))}
...
>>> print "d1 - d2:", dict_ops(d1, d2, lambda x,y: x-y)
d1 - d2: {'falsey_one': False, 'one': 1}
>>> print "d2 - d1:", dict_ops(d1, d2, lambda x,y: y-x)
d2 - d1: {'falsey_two': None, 'two': 2}
>>> import operator as op
>>> print "intersection:", dict_ops(d1, d2, op.and_)
intersection: {'both': 3, 'falsey_both': None}
>>> print "union:", dict_ops(d1, d2, op.or_)
union: {'falsey_one': False, 'falsey_both': None, 'both': 3, 'two': 2, 'one': 1, 'falsey_two': None}
如果两个字典中都有项目,d1
则将使用 from 的值。当然,我们可以d2
通过更改函数参数的顺序来返回值。
>>> print "union:", dict_ops(d2, d1, op.or_)
union: {'both': 30, 'falsey_two': None, 'falsey_one': False, 'two': 2, 'one': 1, 'falsey_both': False}
编辑:这里的食谱不能正确处理 False 值。我已经提交了另一个改进的答案。
以下是我想出的一些食谱:
>>> d1 = {'one':1, 'both':3}
>>> d2 = {'two':2, 'both':30}
>>>
>>> print "d1 only:", {k:d1.get(k) or d2[k] for k in set(d1) - set(d2)} # 0
d1 only: {'one': 1}
>>> print "d2 only:", {k:d1.get(k) or d2[k] for k in set(d2) - set(d1)} # 1
d2 only: {'two': 2}
>>> print "in both:", {k:d1.get(k) or d2[k] for k in set(d1) & set(d2)} # 2
in both: {'both': 3}
>>> print "in either:", {k:d1.get(k) or d2[k] for k in set(d1) | set(d2)} # 3
in either: {'both': 3, 'two': 2, 'one': 1}
虽然 #0 和 #2 中的表达式可以变得更简单,但我喜欢这个表达式的通用性,它允许我将这个配方复制和粘贴到任何地方,并且只需将最后的 set 操作更改为我需要的。
当然我们可以把它变成一个函数:
>>> def dict_ops(d1, d2, setop):
... return {k:d1.get(k) or d2[k] for k in setop(set(d1), set(d2))}
...
>>> print "d1 only:", dict_ops(d1, d2, lambda x,y: x-y)
d1 only: {'one': 1}
>>> print "d2 only:", dict_ops(d1, d2, lambda x,y: y-x)
d2 only: {'two': 2}
>>> import operator as op
>>> print "in both:", dict_ops(d1, d2, op.and_)
in both: {'both': 3}
>>> print "in either:", dict_ops(d1, d2, op.or_)
in either: {'both': 3, 'two': 2, 'one': 1}
>>> print "in either:", dict_ops(d2, d1, lambda x,y: x|y)
in either: {'both': 30, 'two': 2, 'one': 1}
这里还有一些:
设置加法d1 + d2
{key: value for key, value in d1.items() + d2.items()}
# here values that are present in `d1` are replaced by values in `d2`
或者,
d3 = d1.copy()
d3.update(d2)
设置差异d1 - d2
{key: value for key, value in d1.items() if key not in d2}