私は任意の数のkwargsを取り、以下を含むデータベースのようなリストの要素を含むリストを返すカスタムフィルターメソッドを記述しようとしています。それらkwargs。
たとえば、_d1 = {'a':'2', 'b':'3'}
_と_d2
_ =同じことを想定します。 _d1 == d2
_はTrueになります。しかし、_d2
_ =同じものに加えて他の多くのものを考えてください。私のメソッドは、d1 in d2かどうかを判断できる必要がありますが、Pythonは辞書ではできません。
コンテキスト:
Wordクラスがあり、各オブジェクトにはWord
、definition
、_part_of_speech
_などのプロパティがあります。 Word.objects.filter(Word='jump', part_of_speech='verb-intransitive')
のように、これらの単語のメインリストでフィルターメソッドを呼び出すことができるようにします。これらのキーと値を同時に管理する方法がわかりません。しかし、これは他の人にとってはこの文脈の外でより大きな機能を持つことができます。
アイテムのペアに変換し、封じ込めを確認します。
all(item in superset.items() for item in subset.items())
最適化は読者の演習として残されています。
Python 3)では、dict.items()
を使用してdictアイテムのセットのようなビューを取得できます。その後、_<=
_演算子を使用して、ビューはもう一方の「サブセット」です。
_d1.items() <= d2.items()
_
Python 2.7では、dict.viewitems()
を使用して同じことを行います。
_d1.viewitems() <= d2.viewitems()
_
Python 2.6以下では、all()
を使用するなど、別のソリューションが必要になります。
_all(key in d2 and d2[key] == d1[key] for key in d1)
_
単体テストでこれを必要とする人々への注意:PythonのTestCase
クラスにはassertDictContainsSubset()
メソッドもあります。
ただし、3.2では非推奨になりましたが、その理由は定かではありませんが、代わりになる可能性があります。
キーと値のチェックに使用:set(d1.items()).issubset(set(d2.items()))
キーのみをチェックする必要がある場合:set(d1).issubset(set(d2))
完全を期すために、次のこともできます。
def is_subdict(small, big):
return dict(big, **small) == big
ただし、速度(またはその欠如)または読みやすさ(またはその欠如)については一切主張しません。
>>> 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
コンテキスト:
>>> 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
>>>
これを再帰的に行う同じ目的の私の関数:
_def dictMatch(patn, real):
"""does real dict match pattern?"""
try:
for pkey, pvalue in patn.iteritems():
if type(pvalue) is dict:
result = dictMatch(pvalue, real[pkey])
assert result
else:
assert real[pkey] == pvalue
result = True
except (AssertionError, KeyError):
result = False
return result
_
あなたの例では、dictMatch(d1, d2)
は、d2に他のものが含まれている場合でもTrueを返す必要があります。さらに、下位レベルにも適用されます。
_d1 = {'a':'2', 'b':{3: 'iii'}}
d2 = {'a':'2', 'b':{3: 'iii', 4: 'iv'},'c':'4'}
dictMatch(d1, d2) # True
_
注:if type(pvalue) is dict
句を使用せず、より広い範囲のケース(ハッシュのリストなど)に適用する、さらに優れたソリューションがあります。また、再帰はここでは制限されないため、自己責任で使用してください。 ;)
私はこの質問が古いことを知っていますが、ネストされた辞書が別のネストされた辞書の一部であるかどうかをチェックするための私のソリューションです。ソリューションは再帰的です。
def compare_dicts(a, b):
for key, value in a.items():
if key in b:
if isinstance(a[key], dict):
if not compare_dicts(a[key], b[key]):
return False
Elif value != b[key]:
return False
else:
return False
return True
与えられた問題に対する一般的な再帰的解法は次のとおりです。
import traceback
import unittest
def is_subset(superset, subset):
for key, value in subset.items():
if key not in superset:
return False
if isinstance(value, dict):
if not is_subset(superset[key], value):
return False
Elif isinstance(value, str):
if value not in superset[key]:
return False
Elif isinstance(value, list):
if not set(value) <= set(superset[key]):
return False
Elif isinstance(value, set):
if not value <= superset[key]:
return False
else:
if not value == superset[key]:
return False
return True
class Foo(unittest.TestCase):
def setUp(self):
self.dct = {
'a': 'hello world',
'b': 12345,
'c': 1.2345,
'd': [1, 2, 3, 4, 5],
'e': {1, 2, 3, 4, 5},
'f': {
'a': 'hello world',
'b': 12345,
'c': 1.2345,
'd': [1, 2, 3, 4, 5],
'e': {1, 2, 3, 4, 5},
'g': False,
'h': None
},
'g': False,
'h': None,
'question': 'mcve',
'metadata': {}
}
def tearDown(self):
pass
def check_true(self, superset, subset):
return self.assertEqual(is_subset(superset, subset), True)
def check_false(self, superset, subset):
return self.assertEqual(is_subset(superset, subset), False)
def test_simple_cases(self):
self.check_true(self.dct, {'a': 'hello world'})
self.check_true(self.dct, {'b': 12345})
self.check_true(self.dct, {'c': 1.2345})
self.check_true(self.dct, {'d': [1, 2, 3, 4, 5]})
self.check_true(self.dct, {'e': {1, 2, 3, 4, 5}})
self.check_true(self.dct, {'f': {
'a': 'hello world',
'b': 12345,
'c': 1.2345,
'd': [1, 2, 3, 4, 5],
'e': {1, 2, 3, 4, 5},
}})
self.check_true(self.dct, {'g': False})
self.check_true(self.dct, {'h': None})
def test_tricky_cases(self):
self.check_true(self.dct, {'a': 'hello'})
self.check_true(self.dct, {'d': [1, 2, 3]})
self.check_true(self.dct, {'e': {3, 4}})
self.check_true(self.dct, {'f': {
'a': 'hello world',
'h': None
}})
self.check_false(
self.dct, {'question': 'mcve', 'metadata': {'author': 'BPL'}})
self.check_true(
self.dct, {'question': 'mcve', 'metadata': {}})
self.check_false(
self.dct, {'question1': 'mcve', 'metadata': {}})
if __== "__main__":
unittest.main()
注:特定の場合、元のコードは失敗します。 fixing のクレジットは @olivier-melançon になります
この一見単純な問題により、100%信頼性の高いソリューションを見つけるための調査に数時間かかるため、この回答で見つけたものを文書化しました。
"Pythonic-ally"と言えば、_small_dict <= big_dict
_が最も直感的な方法ですが、あまりにもひどいため動作しません。 _{'a': 1} < {'a': 1, 'b': 2}
_はPython 2で動作しているように見えますが、公式のドキュメントでは明示的に呼び出されているため、信頼できません。 「平等以外の結果は一貫して解決されますが、他の方法で定義されていません」検索を実行します。 in このセクション 。言うまでもなく、Python 3の2つの辞書を比較すると、TypeError例外が発生します。
2番目に直感的なのは、Python 2.7のみのsmall.viewitems() <= big.viewitems()
と、Python 3.のsmall.items() <= big.items()
です。しかし、注意が1つあります。 潜在的にバギーです。プログラムがPython <= 2.6で使用される可能性がある場合、そのd1.items() <= d2.items()
は実際には特定の順序なしに2つのタプルのリストを比較しているため、最終結果は信頼できず、プログラムの厄介なバグ。 Python <= 2.6の別の実装を作成するつもりはありませんが、(サポートされていないプラットフォーム上であっても)コードに既知のバグが含まれていることに不安を感じています。だから私はこのアプローチを放棄します。
@ blubberdiblubの答え (クレジットは彼に行きます)で落ち着きます:
def is_subdict(small, big): return dict(big, **small) == big
この答えは、辞書間の_==
_動作に依存していることを指摘する価値があります。これは公式文書で明確に定義されているため、はすべてのPythonバージョン。検索に行く:
ネストされた辞書で機能する短い再帰実装:
def compare_dicts(a,b):
if not a: return True
if isinstance(a, dict):
key, val = a.popitem()
return isinstance(b, dict) and key in b and compare_dicts(val, b.pop(key)) and compare_dicts(a, b)
return a == b
これにより、aおよびbのディクテーションが消費されます。他の回答のように部分的に反復的な解決策に頼らずにそれを回避する良い方法を誰かが知っているなら、教えてください。キーに基づいて、辞書を頭と尾に分割する方法が必要です。
このコードは、プログラミング演習としてより有用であり、おそらく、再帰と反復を混在させる他のソリューションよりもはるかに低速です。 @ Nutcracker's solution は、ネストされた辞書に適しています。
Pydashを使用してもかまわない場合は、is_matchがあります。
import pydash
a = {1:2, 3:4, 5:{6:7}}
b = {3:4.0, 5:{6:8}}
c = {3:4.0, 5:{6:7}}
pydash.predicates.is_match(a, b) # False
pydash.predicates.is_match(a, c) # True
以下は、辞書に含まれるリストとセットにも適切に再帰するソリューションです。また、辞書などを含むリストにもこれを使用できます...
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
この関数は、ハッシュできない値に対して機能します。また、明確で読みやすいと思います。
def isSubDict(subDict,dictionary):
for key in subDict.keys():
if (not key in dictionary) or (not subDict[key] == dictionary[key]):
return False
return True
In [126]: isSubDict({1:2},{3:4})
Out[126]: False
In [127]: isSubDict({1:2},{1:2,3:4})
Out[127]: True
In [128]: isSubDict({1:{2:3}},{1:{2:3},3:4})
Out[128]: True
In [129]: isSubDict({1:{2:3}},{1:{2:4},3:4})
Out[129]: False