Tính toán sự khác biệt trong các khóa có trong hai từ điển Python


171

Giả sử tôi có hai từ điển Python - dictAdictB. Tôi cần tìm hiểu xem có bất kỳ chìa khóa nào hiện diện trong dictBnhưng không có trong dictA. Cách nhanh nhất để đi về nó là gì?

Tôi có nên chuyển đổi các khóa từ điển thành một bộ và sau đó đi về?

Quan tâm đến việc biết suy nghĩ của bạn ...


Cảm ơn câu trả lời của bạn.

Xin lỗi vì không nêu rõ câu hỏi của tôi. Kịch bản của tôi là như thế này - Tôi có một dictAcái có thể giống dictBhoặc có thể thiếu một số khóa so với dictBhoặc nếu không thì giá trị của một số khóa có thể khác với giá trị của khóa dictA.

Vấn đề là từ điển không có tiêu chuẩn và có thể có các giá trị có thể bị sai khiến.

Nói

dictA={'key1':a, 'key2':b, 'key3':{'key11':cc, 'key12':dd}, 'key4':{'key111':{....}}}
dictB={'key1':a, 'key2:':newb, 'key3':{'key11':cc, 'key12':newdd, 'key13':ee}.......

Vì vậy, giá trị 'key2' phải được đặt lại thành giá trị mới và 'key13' phải được thêm vào bên trong dict. Giá trị khóa không có định dạng cố định. Nó có thể là một giá trị đơn giản hoặc một dict hoặc dict of dict.

Câu trả lời:


234

Bạn có thể sử dụng các thao tác thiết lập trên các phím:

diff = set(dictb.keys()) - set(dicta.keys())

Đây là một lớp để tìm tất cả các khả năng: những gì đã được thêm vào, những gì đã bị xóa, những cặp giá trị khóa nào giống nhau và những cặp khóa-giá trị nào được thay đổi.

class DictDiffer(object):
    """
    Calculate the difference between two dictionaries as:
    (1) items added
    (2) items removed
    (3) keys same in both but changed values
    (4) keys same in both and unchanged values
    """
    def __init__(self, current_dict, past_dict):
        self.current_dict, self.past_dict = current_dict, past_dict
        self.set_current, self.set_past = set(current_dict.keys()), set(past_dict.keys())
        self.intersect = self.set_current.intersection(self.set_past)
    def added(self):
        return self.set_current - self.intersect 
    def removed(self):
        return self.set_past - self.intersect 
    def changed(self):
        return set(o for o in self.intersect if self.past_dict[o] != self.current_dict[o])
    def unchanged(self):
        return set(o for o in self.intersect if self.past_dict[o] == self.current_dict[o])

Đây là một số đầu ra mẫu:

>>> a = {'a': 1, 'b': 1, 'c': 0}
>>> b = {'a': 1, 'b': 2, 'd': 0}
>>> d = DictDiffer(b, a)
>>> print "Added:", d.added()
Added: set(['d'])
>>> print "Removed:", d.removed()
Removed: set(['c'])
>>> print "Changed:", d.changed()
Changed: set(['b'])
>>> print "Unchanged:", d.unchanged()
Unchanged: set(['a'])

Có sẵn dưới dạng repo github: https://github.com/hughdbrown/dictdiffer


3
Giải pháp thông minh, cảm ơn! Tôi đã làm cho nó hoạt động với các ký tự lồng nhau bằng cách kiểm tra xem các giá trị thay đổi hoặc không thay đổi là các thể hiện chính tả và gọi một hàm đệ quy để kiểm tra lại chúng bằng cách sử dụng lớp của bạn.
AJJ

1
@AJJ Tôi muốn thấy việc thực hiện đó.
urschrei

1
Làm thế nào về một def update(self, new_dict): self.__init__(new_dict, self.current_dict)hoặc tương tự để bạn có thể thực hiện một so sánh cán
Nick T

Một số lưu ý: DictDifferlớp là một lớp không trạng thái và có thể là một hàm. Các giá trị changedunchangedcó thể được tính trong cùng một vòng lặp. Hai chức năng này có thể trả về một listthay vì một setchắc chắn ít tốn kém hơn. Để so sánh sâu, bạn có thể xem qua khung kiểm tra Đơn vị: docs.python.org/2/l Library / unittest.html , chỉ cần làm theo assertDictEqualphương pháp trong mã nguồn.
Laurent LAPORTE

1
FWIW, set(dictb)có lẽ tốt hơn set(dictb.keys()).
mgilson

60

Trong trường hợp bạn muốn có sự khác biệt theo cách đệ quy, tôi đã viết một gói cho python: https://github.com/seperman/deepdiff

Cài đặt

Cài đặt từ PyPi:

pip install deepdiff

Ví dụ sử dụng

Nhập khẩu

>>> from deepdiff import DeepDiff
>>> from pprint import pprint
>>> from __future__ import print_function # In case running on Python 2

Cùng một đối tượng trả về sản phẩm nào

>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = t1
>>> print(DeepDiff(t1, t2))
{}

Loại vật phẩm đã thay đổi

>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = {1:1, 2:"2", 3:3}
>>> pprint(DeepDiff(t1, t2), indent=2)
{ 'type_changes': { 'root[2]': { 'newtype': <class 'str'>,
                                 'newvalue': '2',
                                 'oldtype': <class 'int'>,
                                 'oldvalue': 2}}}

Giá trị của một mặt hàng đã thay đổi

>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = {1:1, 2:4, 3:3}
>>> pprint(DeepDiff(t1, t2), indent=2)
{'values_changed': {'root[2]': {'newvalue': 4, 'oldvalue': 2}}}

Mục được thêm và / hoặc loại bỏ

>>> t1 = {1:1, 2:2, 3:3, 4:4}
>>> t2 = {1:1, 2:4, 3:3, 5:5, 6:6}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff)
{'dic_item_added': ['root[5]', 'root[6]'],
 'dic_item_removed': ['root[4]'],
 'values_changed': {'root[2]': {'newvalue': 4, 'oldvalue': 2}}}

Chênh lệch chuỗi

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world"}}
>>> t2 = {1:1, 2:4, 3:3, 4:{"a":"hello", "b":"world!"}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{ 'values_changed': { 'root[2]': {'newvalue': 4, 'oldvalue': 2},
                      "root[4]['b']": { 'newvalue': 'world!',
                                        'oldvalue': 'world'}}}

Chuỗi chênh lệch 2

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world!\nGoodbye!\n1\n2\nEnd"}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world\n1\n2\nEnd"}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{ 'values_changed': { "root[4]['b']": { 'diff': '--- \n'
                                                '+++ \n'
                                                '@@ -1,5 +1,4 @@\n'
                                                '-world!\n'
                                                '-Goodbye!\n'
                                                '+world\n'
                                                ' 1\n'
                                                ' 2\n'
                                                ' End',
                                        'newvalue': 'world\n1\n2\nEnd',
                                        'oldvalue': 'world!\n'
                                                    'Goodbye!\n'
                                                    '1\n'
                                                    '2\n'
                                                    'End'}}}

>>> 
>>> print (ddiff['values_changed']["root[4]['b']"]["diff"])
--- 
+++ 
@@ -1,5 +1,4 @@
-world!
-Goodbye!
+world
 1
 2
 End

Thay đổi loại

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world\n\n\nEnd"}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{ 'type_changes': { "root[4]['b']": { 'newtype': <class 'str'>,
                                      'newvalue': 'world\n\n\nEnd',
                                      'oldtype': <class 'list'>,
                                      'oldvalue': [1, 2, 3]}}}

Danh sách khác biệt

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3, 4]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2]}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{'iterable_item_removed': {"root[4]['b'][2]": 3, "root[4]['b'][3]": 4}}

Danh sách khác biệt 2:

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 3, 2, 3]}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{ 'iterable_item_added': {"root[4]['b'][3]": 3},
  'values_changed': { "root[4]['b'][1]": {'newvalue': 3, 'oldvalue': 2},
                      "root[4]['b'][2]": {'newvalue': 2, 'oldvalue': 3}}}

Liệt kê sự khác biệt bỏ qua thứ tự hoặc trùng lặp: (với cùng một từ điển như trên)

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 3, 2, 3]}}
>>> ddiff = DeepDiff(t1, t2, ignore_order=True)
>>> print (ddiff)
{}

Danh sách có chứa từ điển:

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, {1:1, 2:2}]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, {1:3}]}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{ 'dic_item_removed': ["root[4]['b'][2][2]"],
  'values_changed': {"root[4]['b'][2][1]": {'newvalue': 3, 'oldvalue': 1}}}

Bộ:

>>> t1 = {1, 2, 8}
>>> t2 = {1, 2, 3, 5}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (DeepDiff(t1, t2))
{'set_item_added': ['root[3]', 'root[5]'], 'set_item_removed': ['root[8]']}

Được đặt tên là Tuples:

>>> from collections import namedtuple
>>> Point = namedtuple('Point', ['x', 'y'])
>>> t1 = Point(x=11, y=22)
>>> t2 = Point(x=11, y=23)
>>> pprint (DeepDiff(t1, t2))
{'values_changed': {'root.y': {'newvalue': 23, 'oldvalue': 22}}}

Đối tượng tùy chỉnh:

>>> class ClassA(object):
...     a = 1
...     def __init__(self, b):
...         self.b = b
... 
>>> t1 = ClassA(1)
>>> t2 = ClassA(2)
>>> 
>>> pprint(DeepDiff(t1, t2))
{'values_changed': {'root.b': {'newvalue': 2, 'oldvalue': 1}}}

Thuộc tính đối tượng được thêm vào:

>>> t2.c = "new attribute"
>>> pprint(DeepDiff(t1, t2))
{'attribute_added': ['root.c'],
 'values_changed': {'root.b': {'newvalue': 2, 'oldvalue': 1}}}

Cảm ơn vì điều này! Chỉ cần thực hiện trên dự án của tôi, hoạt động tuyệt vời!
gtalarico

1
@gtalarico Rất vui được giúp đỡ! Cảm ơn những lời tốt đẹp!
Seperman

Có bất kỳ tùy chọn để bỏ qua sự khác biệt thứ tự danh sách ? bởi vì ứng dụng của tôi không quan tâm đến nó.
Lôi Dương

Dự án tốt đẹp, đã làm tất cả công việc với nỗ lực tối thiểu từ phía tôi. Cảm ơn!
Stanislav Tsepa

@LeiYang Có bạn có thể đặt ignore_order=True. Bạn có thể tìm thấy các tài liệu tại deepdiff.readthedocs.io/en/latest/diff.html
Seperman

18

không chắc nó "nhanh" hay không, nhưng thông thường, người ta có thể làm điều này

dicta = {"a":1,"b":2,"c":3,"d":4}
dictb = {"a":1,"d":2}
for key in dicta.keys():
    if not key in dictb:
        print key

Bạn phải trao đổi dictadictbvì anh ta muốn biết những khóa dictbđó không có trong đó dicta.
Gumbo

2
for key in dicta.keys():=>for key in dicta:
Jean-François Fabre

15

Như Alex Martelli đã viết, nếu bạn chỉ muốn kiểm tra xem có bất kỳ khóa nào trong B không nằm trong A không, any(True for k in dictB if k not in dictA)sẽ là cách tốt nhất.

Để tìm các khóa bị thiếu:

diff = set(dictB)-set(dictA) #sets

C:\Dokumente und Einstellungen\thc>python -m timeit -s "dictA =    
dict(zip(range(1000),range
(1000))); dictB = dict(zip(range(0,2000,2),range(1000)))" "diff=set(dictB)-set(dictA)"
10000 loops, best of 3: 107 usec per loop

diff = [ k for k in dictB if k not in dictA ] #lc

C:\Dokumente und Einstellungen\thc>python -m timeit -s "dictA = 
dict(zip(range(1000),range
(1000))); dictB = dict(zip(range(0,2000,2),range(1000)))" "diff=[ k for k in dictB if
k not in dictA ]"
10000 loops, best of 3: 95.9 usec per loop

Vì vậy, hai giải pháp này có cùng tốc độ.


8
Điều này có ý nghĩa hơn:any(k not in dictA for k in dictB)
hughdbrown

13

Nếu bạn thực sự có ý nghĩa chính xác những gì bạn nói (rằng bạn chỉ cần tìm ra NẾU "có bất kỳ khóa nào" trong B và không phải trong A, không phải những gì ONES có thể là nếu có), cách nhanh nhất nên là:

if any(True for k in dictB if k not in dictA): ...

Nếu bạn thực sự cần phải tìm ra KHÓA NÀO, nếu có, ở B chứ không phải A, và không chỉ "NẾU" có các khóa như vậy, thì câu trả lời hiện tại khá phù hợp (nhưng tôi đề nghị chính xác hơn trong các câu hỏi trong tương lai nếu đó là thực sự những gì bạn có ý nghĩa ;-).



8

Câu trả lời hàng đầu của hughdbrown gợi ý sử dụng tập khác biệt, đây chắc chắn là cách tiếp cận tốt nhất:

diff = set(dictb.keys()) - set(dicta.keys())

Vấn đề với mã này là nó xây dựng hai danh sách chỉ để tạo hai bộ, do đó, nó lãng phí thời gian 4N và không gian 2N. Nó cũng phức tạp hơn một chút so với nó cần phải có.

Thông thường, đây không phải là một vấn đề lớn, nhưng nếu nó là:

diff = dictb.keys() - dicta

Con trăn 2

Trong Python 2, keys()trả về một danh sách các khóa, không phải là a KeysView. Vì vậy, bạn phải yêu cầu viewkeys()trực tiếp.

diff = dictb.viewkeys() - dicta

Đối với mã 2.7 / 3.x phiên bản kép, bạn hy vọng sẽ sử dụng sixhoặc một cái gì đó tương tự, vì vậy bạn có thể sử dụng six.viewkeys(dictb):

diff = six.viewkeys(dictb) - dicta

Trong 2.4-2.6, không có KeysView. Nhưng ít nhất bạn có thể cắt giảm chi phí từ 4N xuống N bằng cách xây dựng bộ bên trái của bạn trực tiếp từ một trình vòng lặp, thay vì xây dựng danh sách trước:

diff = set(dictb) - dicta

Mặt hàng

Tôi có một dictA có thể giống như dictB hoặc có thể thiếu một số khóa so với dictB hoặc nếu không thì giá trị của một số khóa có thể khác

Vì vậy, bạn thực sự không cần phải so sánh các phím, nhưng các mục. An ItemsViewchỉ là một Setnếu các giá trị có thể băm, như chuỗi. Nếu có, thật dễ dàng:

diff = dictb.items() - dicta.items()

Khác biệt đệ quy

Mặc dù câu hỏi không trực tiếp yêu cầu một khác biệt đệ quy, một số giá trị mẫu là dicts và nó xuất hiện đầu ra dự kiến ​​sẽ khác biệt đệ quy chúng. Đã có nhiều câu trả lời ở đây chỉ ra cách làm điều đó.


câu trả lời chắc chắn từ năm 2018.
Jean-François Fabre

@ Jean-FrançoisFabre Tất nhiên công cụ Python 2.4-2.6 đã khá là không liên quan trong năm 2018
abarnert

một số người bị mắc kẹt với 2.6
Jean-François Fabre


3

Đây là một cách sẽ hoạt động, cho phép các khóa đánh giá Falsevà vẫn sử dụng biểu thức trình tạo để thoát ra sớm nếu có thể. Nó không đặc biệt đẹp.

any(map(lambda x: True, (k for k in b if k not in a)))

BIÊN TẬP:

THC4k đã đăng một câu trả lời cho nhận xét của tôi về một câu trả lời khác. Đây là một cách tốt hơn, đẹp hơn để làm như trên:

any(True for k in b if k not in a)

Không chắc làm thế nào mà không bao giờ vượt qua tâm trí của tôi ...


đây là câu trả lời giống như câu trả lời trước đó của Alex Martelli
Jean-François Fabre

Nó bây giờ là. Khi tôi đăng nó (chín năm trước, lol) câu trả lời trước any(k for k in dictB if k not in dictA)đó không phải là điều tương tự (đối với các phím falsey). Kiểm tra lịch sử chỉnh sửa / dấu thời gian.
Steve Losh

3

Đây là một câu hỏi cũ và hỏi ít hơn một chút so với những gì tôi cần để câu trả lời này thực sự giải quyết được nhiều hơn câu hỏi này. Các câu trả lời trong câu hỏi này đã giúp tôi giải quyết như sau:

  1. (hỏi) Ghi lại sự khác biệt giữa hai từ điển
  2. Hợp nhất các khác biệt từ # 1 vào từ điển cơ sở
  3. (được hỏi) Hợp nhất sự khác biệt giữa hai từ điển (đối xử với từ điển số 2 như thể đó là một từ điển khác)
  4. Cố gắng phát hiện chuyển động của vật phẩm cũng như thay đổi
  5. (hỏi) Làm tất cả những điều này một cách đệ quy

Tất cả điều này kết hợp với JSON tạo nên sự hỗ trợ lưu trữ cấu hình khá mạnh mẽ.

Giải pháp ( cũng trên github ):

from collections import OrderedDict
from pprint import pprint


class izipDestinationMatching(object):
    __slots__ = ("attr", "value", "index")

    def __init__(self, attr, value, index):
        self.attr, self.value, self.index = attr, value, index

    def __repr__(self):
        return "izip_destination_matching: found match by '%s' = '%s' @ %d" % (self.attr, self.value, self.index)


def izip_destination(a, b, attrs, addMarker=True):
    """
    Returns zipped lists, but final size is equal to b with (if shorter) a padded with nulls
    Additionally also tries to find item reallocations by searching child dicts (if they are dicts) for attribute, listed in attrs)
    When addMarker == False (patching), final size will be the longer of a, b
    """
    for idx, item in enumerate(b):
        try:
            attr = next((x for x in attrs if x in item), None)  # See if the item has any of the ID attributes
            match, matchIdx = next(((orgItm, idx) for idx, orgItm in enumerate(a) if attr in orgItm and orgItm[attr] == item[attr]), (None, None)) if attr else (None, None)
            if match and matchIdx != idx and addMarker: item[izipDestinationMatching] = izipDestinationMatching(attr, item[attr], matchIdx)
        except:
            match = None
        yield (match if match else a[idx] if len(a) > idx else None), item
    if not addMarker and len(a) > len(b):
        for item in a[len(b) - len(a):]:
            yield item, item


def dictdiff(a, b, searchAttrs=[]):
    """
    returns a dictionary which represents difference from a to b
    the return dict is as short as possible:
      equal items are removed
      added / changed items are listed
      removed items are listed with value=None
    Also processes list values where the resulting list size will match that of b.
    It can also search said list items (that are dicts) for identity values to detect changed positions.
      In case such identity value is found, it is kept so that it can be re-found during the merge phase
    @param a: original dict
    @param b: new dict
    @param searchAttrs: list of strings (keys to search for in sub-dicts)
    @return: dict / list / whatever input is
    """
    if not (isinstance(a, dict) and isinstance(b, dict)):
        if isinstance(a, list) and isinstance(b, list):
            return [dictdiff(v1, v2, searchAttrs) for v1, v2 in izip_destination(a, b, searchAttrs)]
        return b
    res = OrderedDict()
    if izipDestinationMatching in b:
        keepKey = b[izipDestinationMatching].attr
        del b[izipDestinationMatching]
    else:
        keepKey = izipDestinationMatching
    for key in sorted(set(a.keys() + b.keys())):
        v1 = a.get(key, None)
        v2 = b.get(key, None)
        if keepKey == key or v1 != v2: res[key] = dictdiff(v1, v2, searchAttrs)
    if len(res) <= 1: res = dict(res)  # This is only here for pretty print (OrderedDict doesn't pprint nicely)
    return res


def dictmerge(a, b, searchAttrs=[]):
    """
    Returns a dictionary which merges differences recorded in b to base dictionary a
    Also processes list values where the resulting list size will match that of a
    It can also search said list items (that are dicts) for identity values to detect changed positions
    @param a: original dict
    @param b: diff dict to patch into a
    @param searchAttrs: list of strings (keys to search for in sub-dicts)
    @return: dict / list / whatever input is
    """
    if not (isinstance(a, dict) and isinstance(b, dict)):
        if isinstance(a, list) and isinstance(b, list):
            return [dictmerge(v1, v2, searchAttrs) for v1, v2 in izip_destination(a, b, searchAttrs, False)]
        return b
    res = OrderedDict()
    for key in sorted(set(a.keys() + b.keys())):
        v1 = a.get(key, None)
        v2 = b.get(key, None)
        #print "processing", key, v1, v2, key not in b, dictmerge(v1, v2)
        if v2 is not None: res[key] = dictmerge(v1, v2, searchAttrs)
        elif key not in b: res[key] = v1
    if len(res) <= 1: res = dict(res)  # This is only here for pretty print (OrderedDict doesn't pprint nicely)
    return res

2

những gì về standart (so sánh FULL Object)

PyDev-> Mô-đun PyDev mới-> Mô-đun: không đáng tin cậy nhất

import unittest


class Test(unittest.TestCase):


    def testName(self):
        obj1 = {1:1, 2:2}
        obj2 = {1:1, 2:2}
        self.maxDiff = None # sometimes is usefull
        self.assertDictEqual(d1, d2)

if __name__ == "__main__":
    #import sys;sys.argv = ['', 'Test.testName']

    unittest.main()

Điều này thật tuyệt vời nếu bạn có một từ điển lồng nhau rất lớn và bạn muốn so sánh mọi thứ bên trong và thấy sự khác biệt. Cảm ơn!
Matthew Moisen

2

Nếu trên Python ≥ 2.7:

# update different values in dictB
# I would assume only dictA should be updated,
# but the question specifies otherwise

for k in dictA.viewkeys() & dictB.viewkeys():
    if dictA[k] != dictB[k]:
        dictB[k]= dictA[k]

# add missing keys to dictA

dictA.update( (k,dictB[k]) for k in dictB.viewkeys() - dictA.viewkeys() )

1

Đây là một giải pháp để so sánh sâu 2 phím từ điển:

def compareDictKeys(dict1, dict2):
  if type(dict1) != dict or type(dict2) != dict:
      return False

  keys1, keys2 = dict1.keys(), dict2.keys()
  diff = set(keys1) - set(keys2) or set(keys2) - set(keys1)

  if not diff:
      for key in keys1:
          if (type(dict1[key]) == dict or type(dict2[key]) == dict) and not compareDictKeys(dict1[key], dict2[key]):
              diff = True
              break

  return not diff

1

đây là một giải pháp có thể so sánh nhiều hơn hai dicts:

def diff_dict(dicts, default=None):
    diff_dict = {}
    # add 'list()' around 'd.keys()' for python 3 compatibility
    for k in set(sum([d.keys() for d in dicts], [])):
        # we can just use "values = [d.get(k, default) ..." below if 
        # we don't care that d1[k]=default and d2[k]=missing will
        # be treated as equal
        if any(k not in d for d in dicts):
            diff_dict[k] = [d.get(k, default) for d in dicts]
        else:
            values = [d[k] for d in dicts]
            if any(v != values[0] for v in values):
                diff_dict[k] = values
    return diff_dict

ví dụ sử dụng:

import matplotlib.pyplot as plt
diff_dict([plt.rcParams, plt.rcParamsDefault, plt.matplotlib.rcParamsOrig])

1

Công thức của tôi về sự khác biệt đối xứng giữa hai từ điển:

def find_dict_diffs(dict1, dict2):
    unequal_keys = []
    unequal_keys.extend(set(dict1.keys()).symmetric_difference(set(dict2.keys())))
    for k in dict1.keys():
        if dict1.get(k, 'N\A') != dict2.get(k, 'N\A'):
            unequal_keys.append(k)
    if unequal_keys:
        print 'param', 'dict1\t', 'dict2'
        for k in set(unequal_keys):
            print str(k)+'\t'+dict1.get(k, 'N\A')+'\t '+dict2.get(k, 'N\A')
    else:
        print 'Dicts are equal'

dict1 = {1:'a', 2:'b', 3:'c', 4:'d', 5:'e'}
dict2 = {1:'b', 2:'a', 3:'c', 4:'d', 6:'f'}

find_dict_diffs(dict1, dict2)

Và kết quả là:

param   dict1   dict2
1       a       b
2       b       a
5       e       N\A
6       N\A     f

1

Như đã đề cập trong các câu trả lời khác, unittest tạo ra một số kết quả tốt để so sánh các ký tự, nhưng trong ví dụ này, chúng tôi không muốn phải xây dựng toàn bộ bài kiểm tra trước.

Quét nguồn không đáng tin cậy nhất, có vẻ như bạn có thể có được một giải pháp công bằng chỉ với điều này:

import difflib
import pprint

def diff_dicts(a, b):
    if a == b:
        return ''
    return '\n'.join(
        difflib.ndiff(pprint.pformat(a, width=30).splitlines(),
                      pprint.pformat(b, width=30).splitlines())
    )

vì thế

dictA = dict(zip(range(7), map(ord, 'python')))
dictB = {0: 112, 1: 'spam', 2: [1,2,3], 3: 104, 4: 111}
print diff_dicts(dictA, dictB)

Kết quả trong:

{0: 112,
-  1: 121,
-  2: 116,
+  1: 'spam',
+  2: [1, 2, 3],
   3: 104,
-  4: 111,
?        ^

+  4: 111}
?        ^

-  5: 110}

Ở đâu:

  • '-' biểu thị khóa / giá trị trong lệnh đầu tiên nhưng không phải là thứ hai
  • '+' chỉ ra khóa / giá trị trong giây nhưng không phải là lệnh đầu tiên

Giống như trong trường hợp nhỏ nhất, cảnh báo duy nhất là ánh xạ cuối cùng có thể được coi là khác biệt, do dấu phẩy / dấu ngoặc.


1

@Maxx có một câu trả lời tuyệt vời, hãy sử dụng các unittestcông cụ được cung cấp bởi Python:

import unittest


class Test(unittest.TestCase):
    def runTest(self):
        pass

    def testDict(self, d1, d2, maxDiff=None):
        self.maxDiff = maxDiff
        self.assertDictEqual(d1, d2)

Sau đó, bất cứ nơi nào trong mã của bạn, bạn có thể gọi:

try:
    Test().testDict(dict1, dict2)
except Exception, e:
    print e

Kết quả đầu ra trông giống như đầu ra từ diff, in đẹp các từ điển có +hoặc -thêm vào mỗi dòng khác nhau.


0

Không chắc nó có còn liên quan hay không nhưng tôi đã gặp phải vấn đề này, tình huống của tôi là tôi chỉ cần trả lại một từ điển các thay đổi cho tất cả các từ điển lồng nhau, v.v. Không thể tìm thấy một giải pháp tốt ngoài đó nhưng cuối cùng tôi đã viết một chức năng đơn giản để làm điều này . Hi vọng điêu nay co ich,


2
Tốt nhất là nên có số lượng mã nhỏ nhất khắc phục sự cố của OP thực sự trong câu trả lời, thay vì liên kết. Nếu liên kết chết hoặc di chuyển, câu trả lời của bạn trở nên vô dụng.
George Stocker

0

Nếu bạn muốn một giải pháp tích hợp để so sánh đầy đủ với các cấu trúc chính tả tùy ý, câu trả lời của @ Maxx là một khởi đầu tốt.

import unittest

test = unittest.TestCase()
test.assertEqual(dictA, dictB)

Bạn dường như không thể khởi tạo một lớp kiểm tra như thế, quá tệ.
Ben Liyanage

0

Dựa trên câu trả lời của ghostdog74,

dicta = {"a":1,"d":2}
dictb = {"a":5,"d":2}

for value in dicta.values():
    if not value in dictb.values():
        print value

sẽ in giá trị khác nhau của dicta


0

Hãy thử điều này để tìm giao lộ, các phím nằm trong cả hai từ điển, nếu bạn muốn các khóa không được tìm thấy trên từ điển thứ hai, chỉ cần sử dụng không trong ...

intersect = filter(lambda x, dictB=dictB.keys(): x in dictB, dictA.keys())
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.