在python中合并两个dict,不允许重复

在python中合并两个dict,不允许重复,python,dictionary,merge,Python,Dictionary,Merge,这个问题与类似的字典合并问题不同,因为冲突的重复项应该失败,或者返回False。其他解决方案使用优先规则来决定如何管理一个键何时可能映射到两个不同的变量 如何在python中高效地合并两个dict。例如,考虑: d1 = {'x': 'a', 'y': 'b', 'z': 'c'} d2 = {'z': 'c', 'w': 'r'} d3 = {'z': 'd', 'w': 'r'} 因此,合并字典1和字典2的结果是 {'x': 'a', 'y': 'b', 'z': 'c', 'w': 'r

这个问题与类似的字典合并问题不同,因为冲突的重复项应该失败,或者返回False。其他解决方案使用优先规则来决定如何管理一个键何时可能映射到两个不同的变量

如何在python中高效地合并两个dict。例如,考虑:

d1 = {'x': 'a', 'y': 'b', 'z': 'c'}
d2 = {'z': 'c', 'w': 'r'}
d3 = {'z': 'd', 'w': 'r'}
因此,合并字典1和字典2的结果是

{'x': 'a', 'y': 'b', 'z': 'c', 'w': 'r'}
但是1和3或2和3的合并应该失败,因为
z
有冲突

我的解决办法是:

def merge_dicts(d1,d2):
   k1=d1.keys()
   k2=d2.keys()
   unified_dict=dict()
   for k in k1:
       # look up in second dictionary
      if k in k2:
         pt=d2[k]  #pt stands for 'plain text'
         # if lookup is a contradiction, return empty dictionary
         #  don't even bother with partial results
         if pt!=d1[k]:
             return dict()
         else:
             unified_dict[k]=d1[k]  # safe: key is consistent
      else:
          unified_dict[k]=d1[k] # safe:  no key in k2

# get the rest
# already resolved intersection issues so just get set difference
   for k in d2.keys():
      if k not in d1.keys():
          unified_dict[k]=d2[k]

   return unified_dict
有什么改进吗?

在这里使用;它们允许您将字典键视为集合:

def merge_dicts(d1, d2):
    try:
        # Python 2
        intersection = d1.viewkeys() & d2
    except AttributeError:
        intersection = d1.keys() & d2
       
    if any(d1[shared] != d2[shared] for shared in intersection):
        return {}  # empty result if there are conflicts

    # leave the rest to C code, execute a fast merge using dict()
    return dict(d1, **d2)
上述代码仅测试引用不匹配值的共享密钥;合并本身最好留给
dict()
函数处理

我使函数在Python 2和Python 3上工作;如果只需要支持一个或另一个,请删除
try..except
并用相关表达式替换
intersection
。在Python3中,默认情况下,
dict.keys()
方法返回字典视图。另外,在Python3-only代码中,我将使用
{**d1,**d2}
扩展,它的速度更快、更简洁,并且不限于字符串键

你可以想象这是一个单一的班轮;Python 3版本:

def merge_dicts(d1, d2):
    return (
        {} if any(d1[k] != d2[k] for k in d1.keys() & d2)
        else {**d1, **d2}
    )
如果您只需要支持Python 3.9或更高版本,则可以使用:

演示:


稍微不同的方法(预检查):

为什么不使用set呢

#!/usr/bin/python

d1={'x':'a','y':'b','z':'c'}
d2={'w':'r'}
d3={'z':'d','w':'r'}
d4={'x':'a','y':'b','z':'c'}

def merge_dicts(d1, d2):
    dicts = d1.items() + d2.items()
    if len(dicts) != len(set(dicts)):
        raise ValueError
    else:
        return dict(set(dicts))
print merge_dicts(d1, d2)
print merge_dicts(d1, d3)
try:
    print merge_dicts(d1, d4)
except:
    print "Failed"

$ python foo.py
{'y': 'b', 'x': 'a', 'z': 'c', 'w': 'r'}
{'y': 'b', 'x': 'a', 'z': 'd', 'w': 'r'}
Failed
编辑:

事实上,对于不可散列的值,这将不起作用,这将:

#!/usr/bin/python
# coding: utf-8 

#!/usr/bin/python

d1={'x':'a','y':'b','z':'c'}
d2={'w':'r'}
d3={'z':'d','w':'r'}
d4={'x':'a','y':'b','z':'c'}

def merge_dicts(d1, d2):
    merged= d1.copy()
    for k, v in d2.iteritems():
        if k in merged:
            raise ValueError
        else:
            merged[k] = v 
    return merged

for one, two in [(d1, d2), (d1, d3), (d1, d4)]:
    try:
        print merge_dicts(one, two)
    except:
        print "Merge Failed for %s with %s" %(one, two)

这正是您想要的:

def merge_dicts(d1, d2):
    # Join dicts and get rid of non-conflicting dups
    elems = set(d1.items()) | set(d2.items())

    # Construct join dict
    res = {}
    for k, v in elems:
        if k in res.keys():
            return dict()  # conflicting dup found
        res[k] = v;

    return res

这要求这些值是可散列的。如果词典包含其他词典或列表或集合,例如.Indend,对于第一个,值必须是可散列的。您的第二个版本再次禁止具有相等值的共享键。我之所以不使用set,是因为我不确定set操作是否会重复管理计算时间的其他工作。然而,我更倾向于使用集合运算,因为它会使代码更简洁、更具表现力;这不能用于任何一个词典中的列表、集合或词典。此外,在Python 3中,dict.items()已设置为。在Python2中,您可以使用
dict.viewitems()
,还有一个额外的优点,就是它像一个集合一样运行,而值不必是不可变的。这会改变
d1
,并将值限制为仅可散列的对象。此外,您还可以删除
.keys()
调用,因为这些调用完全是冗余和低效的。此外,字典视图已设置为支持交叉点的对象,无需太多调用,您可以将其修改为更高效、更简洁的版本。感谢Martijn的深刻评论,我已发布了一篇编辑文章。这当然是最快的方法,由于在
KeysView
ItemsView
容器上完全遵从基于C的集合操作@马蒂扬皮特斯的胜利。。。再说一遍。@CecilCurry是的,这就是为什么我在自己的回答中也使用了它们。如果您专门使用Python 3.9或更高版本,也可以使用
|
而不是
dict(d1.*d2)
以获得更高的可读性、更快的速度,并且不受仅字符串键的限制!因为这个问题的目标是找到最具Python风格的方式,所以我很欣赏Python 2和Python 3的答案。我需要深入研究和学习的是dict(d1,**d2)语义。谢谢大家的帮助。关于dict(d1,**d2),请参阅和
dict(d1)
仅创建
d1
的副本
dict(**d2)
将以循环方式创建一份
d2
dict(d1,**d2)
创建
d1
的副本,添加
d2
的键值对。
#!/usr/bin/python

d1={'x':'a','y':'b','z':'c'}
d2={'w':'r'}
d3={'z':'d','w':'r'}
d4={'x':'a','y':'b','z':'c'}

def merge_dicts(d1, d2):
    dicts = d1.items() + d2.items()
    if len(dicts) != len(set(dicts)):
        raise ValueError
    else:
        return dict(set(dicts))
print merge_dicts(d1, d2)
print merge_dicts(d1, d3)
try:
    print merge_dicts(d1, d4)
except:
    print "Failed"

$ python foo.py
{'y': 'b', 'x': 'a', 'z': 'c', 'w': 'r'}
{'y': 'b', 'x': 'a', 'z': 'd', 'w': 'r'}
Failed
#!/usr/bin/python
# coding: utf-8 

#!/usr/bin/python

d1={'x':'a','y':'b','z':'c'}
d2={'w':'r'}
d3={'z':'d','w':'r'}
d4={'x':'a','y':'b','z':'c'}

def merge_dicts(d1, d2):
    merged= d1.copy()
    for k, v in d2.iteritems():
        if k in merged:
            raise ValueError
        else:
            merged[k] = v 
    return merged

for one, two in [(d1, d2), (d1, d3), (d1, d4)]:
    try:
        print merge_dicts(one, two)
    except:
        print "Merge Failed for %s with %s" %(one, two)
d1 = {'x': 'a', 'y': 'b', 'z': 'c'}                                                             
d2 = {'z': 'c', 'w': 'r'}
d3 = {'z': 'd', 'w': 'r'}

def dict_merge(d1, d2):
    """docstring for merge"""
    # doesn't work with python 3.x. Use keys(), items() instead
    if len(d1.viewkeys() & d2) != len(d1.viewitems() & d2.viewitems()):
        return {}
    else:
        result = dict(d1, **d2)
        return result

if __name__ == '__main__':
    print dict_merge(d1, d2)
def merge_dicts(d1, d2):
    # Join dicts and get rid of non-conflicting dups
    elems = set(d1.items()) | set(d2.items())

    # Construct join dict
    res = {}
    for k, v in elems:
        if k in res.keys():
            return dict()  # conflicting dup found
        res[k] = v;

    return res