Python:检查一个字典是否是另一个较大字典的子集

Jam*_*mey 91 python dictionary subset filter

我正在尝试编写一个自定义过滤器方法,它接受任意数量的kwargs并返回一个列表,其中包含类似数据库的列表,其中包含那些kwargs.

例如,假设d1 = {'a':'2', 'b':'3'}d2=同样的事情.d1 == d2结果是真的.但是假设d2=同样的事情加上一堆其他的东西.我的方法需要能够判断d2中是否有d1,但Python不能用字典来表示.

语境:

我有一个字类,并且每个对象都有类似的属性word,definition,part_of_speech,等等.我希望能够在这些单词的主列表上调用过滤方法,例如Word.objects.filter(word='jump', part_of_speech='verb-intransitive').我无法弄清楚如何同时管理这些键和值.但是,对于其他人来说,这可能会在此背景下具有更大的功

Ign*_*ams 95

转换为项目对并检查包含.

all(item in superset.items() for item in subset.items())
Run Code Online (Sandbox Code Playgroud)

优化留给读者练习.

  • 我不认为优化应该留给读者 - 我担心人们会实际使用它而不会意识到它将构建一个superset.items()的副本并为子集中的每个项目迭代它. (25认同)
  • 如果dict值是可以使用的,那么使用viewitems()是我能想到的最优化的方法:`d1.viewitems()<= d2.viewitems()`.Timeit运行表现出超过3倍的性能提升.如果不是可以使用,即使使用`iteritems()`而不是`items()`也会导致大约1.2倍的改进.这是使用Python 2.7完成的. (16认同)
  • 这真可笑。我将把幽默的主题细化留给读者。 (11认同)
  • 使用Python 3时,“ items()”将返回轻量级视图,而不是副本。无需进一步优化。 (3认同)
  • 嵌套目录怎么样? (3认同)
  • @AndreasProfous对于嵌套字典,您需要递归地检查字典的所有元素,请参阅我的答案:/sf/answers/4037266201/(它不仅支持递归字典,但您可以离开如果您不想要或不需要它,请删除它。) (2认同)

aug*_*rar 75

在Python 3中,您可以使用dict.items()获取dict项的类似集合的视图.然后,您可以使用<=运算符来测试一个视图是否是另一个视图的"子集":

d1.items() <= d2.items()
Run Code Online (Sandbox Code Playgroud)

在Python 2.7中,使用dict.viewitems()相同的方法:

d1.viewitems() <= d2.viewitems()
Run Code Online (Sandbox Code Playgroud)

在Python 2.6及更低版本中,您将需要一个不同的解决方案,例如使用all():

all(key in d2 and d2[key] == d1[key] for key in d1)
Run Code Online (Sandbox Code Playgroud)

  • @RodrigoMartins如果您担心未来的维护者,请将操作包装在一个明确命名的函数中或添加代码注释。将代码标准降低到无能的开发人员的水平是一个糟糕的主意。 (4认同)
  • @RodrigoMartins 它记录在 [here](https://docs.python.org/3/library/stdtypes.html#dict-views):“对于类似集合的视图,为抽象基类定义的所有操作 [` collections.abc.Set`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Set) 可用" (3认同)
  • `d1.items() &lt;= d2.items()` 是未定义的行为。它没有记录在官方文档中,最重要的是,它没有经过测试:https://github.com/python/cpython/blob/2e576f5aec1f8f23f07001e2eb3db9276851a4fc/Lib/test/test_dictviews.py#L146 所以这取决于实现。 (2认同)

git*_*rik 34

需要注意的是单元测试需要的人:assertDictContainsSubset()Python的TestCase类中也有一个方法.

http://docs.python.org/2/library/unittest.html?highlight=assertdictcontainssubset#unittest.TestCase.assertDictContainsSubset

然而它在3.2中被弃用,不知道为什么,也许它有一个替代品.

  • 好奇,发现这在[3.2中的新内容](http://docs.python.org/3/whatsnew/3.2.html):*assertDictContainsSubset()方法已被弃用,因为它错误地实现了错误中的参数订购.这会产生难以调试的视错觉,其中像TestCase().assertDictContainsSubset({'a':1,'b':2},{'a':1})这样的测试会失败.**(由Raymond Hettinger供稿) .)* (28认同)
  • 等等,左边是预期的,右边是实际的......这不应该失败吗?该功能唯一的错误是哪个位置哪个位置令人困惑? (2认同)

小智 20

对于键和值检查使用: set(d1.items()).issubset(set(d2.items()))

如果你只需要检查键: set(d1).issubset(set(d2))

  • 如果任一字典中的任何值不可清除,则第一个表达式将不起作用. (11认同)
  • 通过删除集合(d2)可以略微缩短第二个示例,因为"issubset接受任何可迭代".http://docs.python.org/2/library/stdtypes.html#set (5认同)
  • @FrancescoPasa 第二个片段明确表示:“如果您只需要检查键”。`{'a', 'b'}` 实际上是 `{'a', 'b'}` 的子集;) (3认同)

blu*_*lub 18

为完整起见,您还可以这样做:

def is_subdict(small, big):
    return dict(big, **small) == big
Run Code Online (Sandbox Code Playgroud)

但是,我没有对速度(或缺乏速度)或可读性(或缺乏速度)提出任何要求.

  • `{**big, **small} == big` 也会起作用,不需要等到 3.9 ! (3认同)

rob*_*ing 10

>>> d1 = {'a':'2', 'b':'3'}
>>> d2 = {'a':'2', 'b':'3','c':'4'}
>>> all((k in d2 and d2[k]==v) for k,v in d1.iteritems())
True
Run Code Online (Sandbox Code Playgroud)

背景:

>>> d1 = {'a':'2', 'b':'3'}
>>> d2 = {'a':'2', 'b':'3','c':'4'}
>>> list(d1.iteritems())
[('a', '2'), ('b', '3')]
>>> [(k,v) for k,v in d1.iteritems()]
[('a', '2'), ('b', '3')]
>>> k,v = ('a','2')
>>> k
'a'
>>> v
'2'
>>> k in d2
True
>>> d2[k]
'2'
>>> k in d2 and d2[k]==v
True
>>> [(k in d2 and d2[k]==v) for k,v in d1.iteritems()]
[True, True]
>>> ((k in d2 and d2[k]==v) for k,v in d1.iteritems())
<generator object <genexpr> at 0x02A9D2B0>
>>> ((k in d2 and d2[k]==v) for k,v in d1.iteritems()).next()
True
>>> all((k in d2 and d2[k]==v) for k,v in d1.iteritems())
True
>>>
Run Code Online (Sandbox Code Playgroud)


And*_*ous 7

对于 Python 3.9,我使用的是:

def dict_contains_dict(small: dict, big: dict):    
   return (big | small) == big
Run Code Online (Sandbox Code Playgroud)


Fre*_*ens 6

这是一个解决方案,它也可以正确地递归到字典中包含的列表和集合中。您也可以将它用于包含字典等的列表...

def is_subset(subset, superset):
    if isinstance(subset, dict):
        return all(key in superset and is_subset(val, superset[key]) for key, val in subset.items())

    if isinstance(subset, list) or isinstance(subset, set):
        return all(any(is_subset(subitem, superitem) for superitem in superset) for subitem in subset)

    # assume that subset is a plain value if none of the above match
    return subset == superset
Run Code Online (Sandbox Code Playgroud)

  • 那是因为它不是子集。如果我们将字典视为可搜索的(键值)元组集。两个集合都有一个元素:一个具有键值元组“name”:“Girls”,另一个具有由键值元组“show”:{“name”:“Girls”}组成的单个元素,并且当两个集合都有时具有一个元素,这些元素必须相等才能将集合视为彼此的子集。显然不是这种情况,它们都是键值元组,但一个具有键名称,另一个具有键显示,一个具有值 Girls,另一个具有另一个键值元组形式的值。 (3认同)