测试字典是否包含在字典中。

44

测试Python字典相等性的方法如下:

first  = {"one":"un", "two":"deux", "three":"trois"}
second = {"one":"un", "two":"deux", "three":"trois"}

print(first == second) # Result: True

但现在我的第二个字典包含一些我想要忽略的额外键:

first  = {"one":"un", "two":"deux", "three":"trois"}
second = {"one":"un", "two":"deux", "three":"trois", "foo":"bar"}

有没有一种简单的方法来测试第一个字典是否是第二个字典的一部分,包括它的所有键和值?

编辑1:

这个问题被怀疑是如何测试字典是否包含特定的的重复,但我想测试键和它们的值。仅包含相同的键并不意味着两个字典相等。

编辑2:

好的,现在我已经得到了四种不同方法的答案,并证明了它们都可以工作。因为我需要一个快速的过程,所以我测试了每个执行时间。我创建了三个具有1000个项的相同字典,键和值是长度为10的随机字符串。第二个和第三个字典添加了一些额外的键值对,并且第三个字典的最后一个非额外键添加了一个新值。所以,第一个字典是第二个字典的子集,但不是第三个字典的子集。使用10000次重复的timeit模块,我得到:

Method                                                      Time [s]   
first.viewitems() <=second.viewitems()                           0.9 
set(first.items()).issubset(second.items())                      7.3
len(set(first.items()) & set(second.items())) == len(first)      8.5
all(first[key] == second.get(key, sentinel) for key in first)    6.0

我猜最后一种方法是最慢的,但它排名第二。 但方法1胜过所有其他方法。

感谢您的回答!


2
可能是重复的问题:如何测试字典是否包含特定键 - tjati
1
可能是重复的问题:Python:检查一个字典是否是另一个较大字典的子集 - Maor Refaeli
4个回答

87
您可以使用 字典视图
# Python 2
if first.viewitems() <= second.viewitems():
    # true only if `first` is a subset of `second`

# Python 3
if first.items() <= second.items():
    # true only if `first` is a subset of `second`

字典视图是Python 3中的标准, 在Python 2中,您需要在标准方法前加上前缀view。它们的行为类似于集合,<=测试其中一个是否是另一个的子集(或相等)。
Python 3中的演示:
>>> first  = {"one":"un", "two":"deux", "three":"trois"}
>>> second = {"one":"un", "two":"deux", "three":"trois", "foo":"bar"}
>>> first.items() <= second.items()
True
>>> first['four'] =  'quatre'
>>> first.items() <= second.items()
False

这也适用于非可哈希值,因为键已经使键值对唯一。文档在这一点上有点混淆,但即使使用可变值(比如列表),这也是有效的:
>>> first_mutable = {'one': ['un', 'een', 'einz'], 'two': ['deux', 'twee', 'zwei']}
>>> second_mutable = {'one': ['un', 'een', 'einz'], 'two': ['deux', 'twee', 'zwei'], 'three': ['trois', 'drie', 'drei']}
>>> first_mutable.items() <= second_mutable.items()
True
>>> first_mutable['one'].append('ichi')
>>> first_mutable.items() <= second_mutable.items()
False

你可以使用all()函数和生成器表达式,使用object()作为哨兵来简洁地检测缺失值:
sentinel = object()
if all(first[key] == second.get(key, sentinel) for key in first):
    # true only if `first` is a subset of `second`

但是这种方式不如使用字典视图来阅读和表达清晰明了。

7
all(k in second and second[k] == v for k, v in first.items())

如果您知道这些值中没有一个可以是None,那么简化后的代码如下:

all(second.get(k, None) == v for k, v in first.items())

你为什么删除了之前发布的备选版本?not (set(first.items()) - set(second.items())) - Iskren
@Iskren,因为如果值是不可哈希的,例如'foo':[1, 2, 3]是其中一个项,它将无法正常工作。 - behzad.nouri
1
你的第二个解决方案非常优雅,尽管如果值被深度嵌套可能会变慢。不过它高度多态且简洁。+1 - Eli Korvigo

4
所以,您基本上想检查一个字典是否是另一个字典的子集。
first  = {"one":"un", "two":"deux", "three":"trois"}
second = {"one":"un", "two":"deux", "three":"trois", "foo":"bar"}

def subset_dic(subset, superset):
    return len(set(subset.items()) & set(superset.items())) == len(subset)


print(subset_dic(first, second))

输出:

True

如果您想抽象出子集/超集部分:

def subset_dic(dict1, dict2):
    return len(set(dict1.items()) & set(dict2.items())) == len(min((dict1, dict2), key=len))

注意: 如果任何值是可变对象,则此方法将无法正常工作。因此,在函数中可添加一个额外的步骤(将可变对象转换为不可变模拟)以克服此限制。


创建一组完整的字典似乎有点昂贵。 - poke
在没有哈希的情况下比较两组元组的复杂度甚至更高。 - Eli Korvigo
是的,但不需要比较元组;字典已经具有O(1)项访问,因此您只需遍历一个字典并在另一个字典上进行成员检查即可。 - poke
已经有一个回答可以做到这一点,SO规则建议仅发布原创答案 :) - Eli Korvigo
当然,我的评论并不是要表达“这样做很糟糕,你应该换成X”,只是对这个解决方案性能的一般注释 :) - poke

4

# 更新答案:

方法一:使用字典视图:

正如Martijn所建议的那样,我们可以使用字典视图来检查。 dict.viewitems() 充当一个集合。我们可以在其中执行各种集合操作,如交集、并集等(请查看此link)。

first.viewitems() <= second.viewitems()
True

我们检查first是否小于等于second。如果评估为True,则意味着firstsecond的子集。
方法2 使用集合的issubset()操作:
(免责声明:此方法存在一些冗余,并且需要所有值都是可哈希的。建议遵循方法1以处理所有情况。感谢Martijn的建议。)
使用字典的.items()属性获取(键,值)元组列表,然后使用集合的issubset()操作。
这将检查键和相等性。
>>> first  = {"one":"un", "two":"deux", "three":"trois"}
>>> second = {"one":"un", "two":"deux", "three":"trois", "foo":"bar"}

>>> set(first.items()).issubset(second.items())
True

1
дёәд»Җд№Ҳе…ҲдҪҝз”Ёlist()пјҢиҖҢдёҚжҳҜзӣҙжҺҘдҪҝз”Ёset(first.items()).issubset(second.items())пјҹеңЁPython 3дёӯпјҢdict.items()зӣҙжҺҘж”ҜжҢҒдҪҝз”ЁеңәжҷҜ;l1.items() < l2.items()д№ҹеҸҜд»ҘжӯЈеёёе·ҘдҪңгҖӮйңҖиҰҒжіЁж„Ҹзҡ„жҳҜпјҢиҝҷиҰҒжұӮжүҖжңүеҖјйғҪжҳҜеҸҜе“ҲеёҢзҡ„пјҲдҪҝз”Ёset()еҜ№иұЎжҲ–еӯ—е…ёи§ҶеӣҫпјүгҖӮ - Martijn Pieters
谢谢Martijn。那一步是不必要的。已更新答案! - Rahul Gupta
这里仍然存在冗余;在Python 3中,dict.items()已经作为set来使用。在Python 2中,您可以通过使用dict.viewitems()来获得相同的行为。而且,您的方法仍然要求值是可哈希的,而字典视图不需要。 - Martijn Pieters
耶,已经更新了!感谢 Martijn 的建议.. :) - Rahul Gupta

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接