辞書は2つありますが、簡単にするために、これら2つを取ります。
>>> x = dict(a=1, b=2)
>>> y = dict(a=2, b=2)
それでは、x
内の各key, value
ペアがy
内の対応する同じ値を持つかどうかを比較したいと思います。だから私はこれを書いた:
>>> for x_values, y_values in Zip(x.iteritems(), y.iteritems()):
if x_values == y_values:
print 'Ok', x_values, y_values
else:
print 'Not', x_values, y_values
そしてそれはTuple
が返されてから等価かどうか比較されるのでうまくいきます。
私の質問:
これは正しいです?これを行うにはより良い方法がありますか?速さではなく、コードの優雅さについて話しています。
更新:私は、何個のkey, value
ペアが等しいかをチェックしなければならないことを言及するのを忘れていました。
両方の辞書で一致する値の数を知りたい場合は、次のようにしてください。
多分このようなもの:
shared_items = {k: x[k] for k in x if k in y and x[k] == y[k]}
print len(shared_items)
あなたがやりたいことは単にx==y
です
辞書の中の項目に順序があるとは想定されていないため、あなたがすることはお勧めできません。 [('a',1),('b',1)]
と[('b',1), ('a',1)]
を比較しているかもしれません(同じ辞書、違う順番)。
たとえば、これを参照してください。
>>> x = dict(a=2, b=2,c=3, d=4)
>>> x
{'a': 2, 'c': 3, 'b': 2, 'd': 4}
>>> y = dict(b=2,c=3, d=4)
>>> y
{'c': 3, 'b': 2, 'd': 4}
>>> Zip(x.iteritems(), y.iteritems())
[(('a', 2), ('c', 3)), (('c', 3), ('b', 2)), (('b', 2), ('d', 4))]
違いは1つの項目だけですが、あなたのアルゴリズムはすべての項目が異なることを見ます
def dict_compare(d1, d2):
d1_keys = set(d1.keys())
d2_keys = set(d2.keys())
intersect_keys = d1_keys.intersection(d2_keys)
added = d1_keys - d2_keys
removed = d2_keys - d1_keys
modified = {o : (d1[o], d2[o]) for o in intersect_keys if d1[o] != d2[o]}
same = set(o for o in intersect_keys if d1[o] == d2[o])
return added, removed, modified, same
x = dict(a=1, b=2)
y = dict(a=2, b=2)
added, removed, modified, same = dict_compare(x, y)
dic1 == dic2
説明のために、以下の例はすべて
{"one": 1, "two": 2, "three": 3}
に辞書equalを返します。>>> a = dict(one=1, two=2, three=3) >>> b = {'one': 1, 'two': 2, 'three': 3} >>> c = dict(Zip(['one', 'two', 'three'], [1, 2, 3])) >>> d = dict([('two', 2), ('one', 1), ('three', 3)]) >>> e = dict({'three': 3, 'one': 1, 'two': 2}) >>> a == b == c == d == e True
最初の例のようにキーワード引数を指定することは、有効なPython識別子であるキーに対してのみ機能します。それ以外の場合は、有効なキーを使用できます。
py2
およびpy3
に有効です。
私はpythonが初めてですが、@ mouadに似たことをしてしまいました
unmatched_item = set(dict_1.items()) ^ set(dict_2.items())
len(unmatched_item) # should be 0
XOR演算子(^
)は、辞書のすべての要素が両方の辞書で同じ場合、それらの要素をすべて削除する必要があります。
ただ使用してください:
assert cmp(dict1, dict2) == 0
deepdiff
はだれにも言及されていないようなので、完全を期すためにここで追加します。一般的に(ネストした)オブジェクトの差分を取得するのに非常に便利です。
import deepdiff
from pprint import pprint
aa = {
"a": 1,
"nested": {
"b": 1,
}
}
bb = {
"a": 2,
"nested": {
"b": 2,
}
}
pprint(deepdiff.DeepDiff(aa, bb))
出力:
{'values_changed': {"root['a']": {'new_value': 2, 'old_value': 1},
"root['nested']['b']": {'new_value': 2, 'old_value': 1}}}
注意:
deepdiff
パッケージは標準パッケージではないのでインストールする必要があります
結果を解析するためにいくらかの努力を払う必要があります。
しかし、辞書の差分をとるためには、 dictdiffer
がとても便利だと思います。
両方の辞書に単純な値が含まれていると仮定した場合、@ mouadの答えはいいです。ただし、辞書を含む辞書がある場合、辞書はハッシュ化できないため、例外が発生します。
私の頭の上では、このようなものがうまくいくかもしれません:
def compare_dictionaries(dict1, dict2):
if dict1 is None or dict2 is None:
print('Nones')
return False
if (not isinstance(dict1, dict)) or (not isinstance(dict2, dict)):
print('Not dict')
return False
shared_keys = set(dict2.keys()) & set(dict2.keys())
if not ( len(shared_keys) == len(dict1.keys()) and len(shared_keys) == len(dict2.keys())):
print('Not all keys are shared')
return False
dicts_are_equal = True
for key in dict1.keys():
if isinstance(dict1[key], dict) or isinstance(dict2[key], dict):
dicts_are_equal = dicts_are_equal and compare_dictionaries(dict1[key], dict2[key])
else:
dicts_are_equal = dicts_are_equal and all(atleast_1d(dict1[key] == dict2[key]))
return dicts_are_equal
もう一つの可能性は、OPの最後のメモまでは、JSONとしてダンプされた辞書のハッシュ(SHA
またはMD
)を比較することです。ハッシュの構築方法は、それらが等しい場合、ソース文字列も同じであることを保証します。これは非常に高速で数学的に正しいです。
import json
import hashlib
def hash_dict(d):
return hashlib.sha1(json.dumps(d, sort_keys=True)).hexdigest()
x = dict(a=1, b=2)
y = dict(a=2, b=2)
z = dict(a=1, b=2)
print(hash_dict(x) == hash_dict(y))
print(hash_dict(x) == hash_dict(z))
2つの辞書のキーと値が等しいかどうかをテストするには
def dicts_equal(d1,d2):
""" return True if all keys and values are the same """
return all(k in d2 and d1[k] == d2[k]
for k in d1) \
and all(k in d1 and d1[k] == d2[k]
for k in d2)
異なる値を返したい場合は、別の方法で書いてください。
def dict1_minus_d2(d1, d2):
""" return the subset of d1 where the keys don't exist in d2 or
the values in d2 are different, as a dict """
return {k,v for k,v in d1.items() if k in d2 and v == d2[k]}
あなたはそれを2回呼ばなければならないでしょう。
dict1_minus_d2(d1,d2).extend(dict1_minus_d2(d2,d1))
機能は素晴らしいIMO、明確で直感的です。しかし、あなたに(別の)答えを与えるために、これが私の行くところです:
def compare_dict(dict1, dict2):
for x1 in dict1.keys():
z = dict1.get(x1) == dict2.get(x1)
if not z:
print('key', x1)
print('value A', dict1.get(x1), '\nvalue B', dict2.get(x1))
print('-----\n')
あなたや他の人に役立つことができます..
def equal(a, b):
type_a = type(a)
type_b = type(b)
if type_a != type_b:
return False
if isinstance(a, dict):
if len(a) != len(b):
return False
for key in a:
if key not in b:
return False
if not equal(a[key], b[key]):
return False
return True
Elif isinstance(a, list):
if len(a) != len(b):
return False
while len(a):
x = a.pop()
index = indexof(x, b)
if index == -1:
return False
del b[index]
return True
else:
return a == b
def indexof(x, a):
for i in range(len(a)):
if equal(x, a[i]):
return i
return -1
>>> a = {
'number': 1,
'list': ['one', 'two']
}
>>> b = {
'list': ['two', 'one'],
'number': 1
}
>>> equal(a, b)
True
私はPython 3で私のために完璧に働くこの解決策を使っています
import logging
log = logging.getLogger(__name__)
...
def deep_compare(self,left, right, level=0):
if type(left) != type(right):
log.info("Exit 1 - Different types")
return False
Elif type(left) is dict:
# Dict comparison
for key in left:
if key not in right:
log.info("Exit 2 - missing {} in right".format(key))
return False
else:
if not deep_compare(left[str(key)], right[str(key)], level +1 ):
log.info("Exit 3 - different children")
return False
return True
Elif type(left) is list:
# List comparison
for key in left:
if key not in right:
log.info("Exit 4 - missing {} in right".format(key))
return False
else:
if not deep_compare(left[left.index(key)], right[right.index(key)], level +1 ):
log.info("Exit 5 - different children")
return False
return True
else:
# Other comparison
return left == right
return False
これは、dict、list、および "=="演算子をそれ自体で実装しているその他の型を比較します。他の何かを比較する必要がある場合は、 "if tree"に新しいブランチを追加する必要があります。
それが役立つことを願っています。
私の返事が遅れるのは、決してしないよりはましです!
Not_Equalの比較は、Equalの比較よりも効率的です。一方の辞書のキー値がもう一方の辞書に見つからない場合、そのような2つの辞書は等しくありません。以下のコードは、デフォルトの辞書を比較している可能性があることを考慮しているため、getitem[]の代わりにgetを使用します。
Getコールでデフォルトとして一種の乱数を使用します - 辞書の値が一方の辞書にNoneであり、そのキーがもう一方の辞書に存在しない場合に備えて。また、両側からキーと値のチェックを同時に行っているため、get!=条件が効率的ではない条件の前にチェックされます。
def Dicts_Not_Equal(first,second):
""" return True if both do not have same length or if any keys and values are not the same """
if len(first) == len(second):
for k in first:
if first.get(k) != second.get(k,k) or k not in second: return (True)
for k in second:
if first.get(k,k) != second.get(k) or k not in first: return (True)
return (False)
return (True)
辞書ビューオブジェクトを参照してください: https://docs.python.org/2/library/stdtypes.html#dict
このようにして、dictView1からdictView2を減算すると、dictView2とは異なるキーと値のペアのセットが返されます。
original = {'one':1,'two':2,'ACTION':'ADD'}
originalView=original.viewitems()
updatedDict = {'one':1,'two':2,'ACTION':'REPLACE'}
updatedDictView=updatedDict.viewitems()
delta=original | updatedDict
print delta
>>set([('ACTION', 'REPLACE')])
これらの辞書ビューオブジェクトは、交差、結合、差(上記)、対称差が可能です。
もっといいですか?もっと早く? - よくわからないが、標準ライブラリの一部 - 移植性には大きなプラス
>>> hash_1
{'a': 'foo', 'b': 'bar'}
>>> hash_2
{'a': 'foo', 'b': 'bar'}
>>> set_1 = set (hash_1.iteritems())
>>> set_1
set([('a', 'foo'), ('b', 'bar')])
>>> set_2 = set (hash_2.iteritems())
>>> set_2
set([('a', 'foo'), ('b', 'bar')])
>>> len (set_1.difference(set_2))
0
>>> if (len(set_1.difference(set_2)) | len(set_2.difference(set_1))) == False:
... print "The two hashes match."
...
The two hashes match.
>>> hash_2['c'] = 'baz'
>>> hash_2
{'a': 'foo', 'c': 'baz', 'b': 'bar'}
>>> if (len(set_1.difference(set_2)) | len(set_2.difference(set_1))) == False:
... print "The two hashes match."
...
>>>
>>> hash_2.pop('c')
'baz'
これは別の選択肢です:
>>> id(hash_1)
140640738806240
>>> id(hash_2)
140640738994848
ご覧のとおり、2つのIDは異なります。しかし、 豊富な比較演算子 がうまくいくようです。
>>> hash_1 == hash_2
True
>>>
>>> hash_2
{'a': 'foo', 'b': 'bar'}
>>> set_2 = set (hash_2.iteritems())
>>> if (len(set_1.difference(set_2)) | len(set_2.difference(set_1))) == False:
... print "The two hashes match."
...
The two hashes match.
>>>
以下のコードは、pythonの辞書のリストを比較するのに役立ちます。
def compate_generic_types(object1, object2):
if isinstance(object1, str) and isinstance(object2, str):
return object1 == object2
Elif isinstance(object1, unicode) and isinstance(object2, unicode):
return object1 == object2
Elif isinstance(object1, bool) and isinstance(object2, bool):
return object1 == object2
Elif isinstance(object1, int) and isinstance(object2, int):
return object1 == object2
Elif isinstance(object1, float) and isinstance(object2, float):
return object1 == object2
Elif isinstance(object1, float) and isinstance(object2, int):
return object1 == float(object2)
Elif isinstance(object1, int) and isinstance(object2, float):
return float(object1) == object2
return True
def deep_list_compare(object1, object2):
retval = True
count = len(object1)
object1 = sorted(object1)
object2 = sorted(object2)
for x in range(count):
if isinstance(object1[x], dict) and isinstance(object2[x], dict):
retval = deep_dict_compare(object1[x], object2[x])
if retval is False:
print "Unable to match [{0}] element in list".format(x)
return False
Elif isinstance(object1[x], list) and isinstance(object2[x], list):
retval = deep_list_compare(object1[x], object2[x])
if retval is False:
print "Unable to match [{0}] element in list".format(x)
return False
else:
retval = compate_generic_types(object1[x], object2[x])
if retval is False:
print "Unable to match [{0}] element in list".format(x)
return False
return retval
def deep_dict_compare(object1, object2):
retval = True
if len(object1) != len(object2):
return False
for k in object1.iterkeys():
obj1 = object1[k]
obj2 = object2[k]
if isinstance(obj1, list) and isinstance(obj2, list):
retval = deep_list_compare(obj1, obj2)
if retval is False:
print "Unable to match [{0}]".format(k)
return False
Elif isinstance(obj1, dict) and isinstance(obj2, dict):
retval = deep_dict_compare(obj1, obj2)
if retval is False:
print "Unable to match [{0}]".format(k)
return False
else:
retval = compate_generic_types(obj1, obj2)
if retval is False:
print "Unable to match [{0}]".format(k)
return False
return retval
PyUnitには辞書を美しく比較する方法があります。私は以下の2つの辞書を使ってそれをテストしました、そしてそれはまさにあなたが探していることをします。
d1 = {1: "value1",
2: [{"subKey1":"subValue1",
"subKey2":"subValue2"}]}
d2 = {1: "value1",
2: [{"subKey2":"subValue2",
"subKey1": "subValue1"}]
}
def assertDictEqual(self, d1, d2, msg=None):
self.assertIsInstance(d1, dict, 'First argument is not a dictionary')
self.assertIsInstance(d2, dict, 'Second argument is not a dictionary')
if d1 != d2:
standardMsg = '%s != %s' % (safe_repr(d1, True), safe_repr(d2, True))
diff = ('\n' + '\n'.join(difflib.ndiff(
pprint.pformat(d1).splitlines(),
pprint.pformat(d2).splitlines())))
standardMsg = self._truncateMessage(standardMsg, diff)
self.fail(self._formatMessage(msg, standardMsg))
本番コードにunittest
をインポートすることはお勧めできません。私の考えでは、PyUnitのソースは本番環境で実行するために作り直すことができます。辞書を「きれいに印刷する」pprint
を使います。このコードを「製造準備完了」にするのは非常に簡単です。
Python 3.6では、それは次のように行うことができます -
if (len(dict_1)==len(dict_2):
for i in dict_1.items():
ret=bool(i in dict_2.items())
dict_1のすべての項目がdict_2に存在する場合、ret変数はtrueになります。
これが私の答えです。再帰的な方法を使ってください。
def dict_equals(da, db):
if not isinstance(da, dict) or not isinstance(db, dict):
return False
if len(da) != len(db):
return False
for da_key in da:
if da_key not in db:
return False
if not isinstance(db[da_key], type(da[da_key])):
return False
if isinstance(da[da_key], dict):
res = dict_equals(da[da_key], db[da_key])
if res is False:
return False
Elif da[da_key] != db[da_key]:
return False
return True
a = {1:{2:3, 'name': 'cc', "dd": {3:4, 21:"nm"}}}
b = {1:{2:3, 'name': 'cc', "dd": {3:4, 21:"nm"}}}
print dict_equals(a, b)
それが役立つことを願っています!