Differences between two dictionaries

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Daikoku
User
Beiträge: 66
Registriert: Montag 20. April 2015, 21:14

Hallo,
in der Funktion foo2() erhalte ich ein fuer mich nicht erwartetes Ergebnis.

Das ausfuehren von oldDict['updated'] = time.time() fuehrt zu dem Ergebnis,
welches sich das Ergebnis von d.changed() ebenfalls aendert.

In der Funktion foo1 ist das nicht der Fall.

Was veranlasst Python dazu, das Ergebnis von d.changed() in foo2 zu aktualisieren und in foo1 nicht ???

vielen Dank für Eure Hilfe.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf8 -*-

import time

class DictDifferences(object):
    # http://stackoverflow.com/questions/1165352/
    #       fast-comparison-between-two-python-dictionary
    """
    Differences 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 = current_dict
        self.past_dict = past_dict

        self.set_current = set(current_dict.keys())
        self.set_past = 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])


def foo1():
    old = { 'Name_1' : 'Maria',
            'Name_2' : 'Schulze',
            'Strasse': 'Hauptstr. 6',
            'PLZ'    : '12487',
            'Ort'    : 'Berlin',
            'Land'   : 'Deutschland',
            'Telefon': '+49 30 333435',
            'Mobile' : '+49 172 123456',
            'updated': '' }

    new = { 'Name_1' : 'Maria',
            'Name_2' : 'Schulze',
            'Strasse': 'Hauptstr. 6',
            'PLZ'    : '12487',
            'Ort'    : 'Berlin',
            'Land'   : 'Deutschland',
            'Telefon': '+49 30 434445',
            'Mobile' : '+49 172 123456',
            'updated': '' }

    # Nur der Key Telefon hat sich geaendert.
    # - old = +49 30 333435
    # - new = +49 30 434445
    DictDiff = DictDifferences

    d = DictDiff(new, old)

    print d.changed()
    # [OUT] set(['Telefon'])

    old['update'] = time.time()

    print d.changed()
    # [OUT] set(['Telefon'])


def foo2():
    old = { 'Personen'  : [ { 'Name_1' : 'Maria',
                              'Name_2' : 'Schulze',
                              'Strasse': 'Hauptstr. 6',
                              'PLZ'    : '12487',
                              'Ort'    : 'Berlin',
                              'Land'   : 'Deutschland',
                              'Telefon': '+49 30 333435',
                              'Mobile' : '+49 172 1234567',
                              'updated': '' },

                            { 'Name_1' : 'Tom',
                              'Name_2' : 'Maier',
                              'Strasse': 'Karlstr. 57',
                              'PLZ'    : '10243',
                              'Ort'    : 'Berlin',
                              'Land'   : 'Deutschland',
                              'Telefon': '+49 30 677565',
                              'Mobile' : '+49 172 9876543',
                              'updated': '' } ] }

    new = { 'Personen'  : [ { 'Name_1' : 'Maria',
                              'Name_2' : 'Schulze',
                              'Strasse': 'Hauptstr. 6',
                              'PLZ'    : '12487',
                              'Ort'    : 'Berlin',
                              'Land'   : 'Deutschland',
                              'Telefon': '+49 30 434445',
                              'Mobile' : '+49 172 1234567',
                              'updated': '' },

                            { 'Name_1' : 'Tom',
                              'Name_2' : 'Maier',
                              'Strasse': 'Karlstr. 57',
                              'PLZ'    : '10243',
                              'Ort'    : 'Berlin',
                              'Land'   : 'Deutschland',
                              'Telefon': '+49 30 788664',
                              'Mobile' : '+49 172 9876543',
                              'updated': '' } ] }

    # Nur der Key Telefon hat sich geaendert.
    # - old = +49 30 333435 - new = +49 30 434445 (Maria)
    # - old = +49 30 677565 - new = +49 30 788664 (Tom)

    DictDiff = DictDifferences

    for i,v in new.iteritems():
        if i in old:
            for newDict in v:
                for oldDict in old[i]:
                    d = DictDiff(newDict, oldDict)
                    if d.unchanged().issuperset(set(['Name_1', 'Name_2'])):
                        print '1. d.changed() = {}'.format(d.changed())
                        # [OUT] 1. d.changed() = set(['Telefon'])                  !!!!!!!!!!

                        oldDict['updated'] = time.time()

                        print '2. d.changed() = {}'.format(d.changed())
                        # [OUT] 2. d.changed() = set(['updated', 'Telefon'])     Warum ist das so ???????



if __name__ == '__main__':
    print '\n=> foo1()'
    foo1()

    print '\n=> foo2()'
    foo2()

    print '\n=> Ende ....'
BlackJack

@Daikoku: Bist Du sicher das Du hier nicht etwas falsches erwartest weil Du einen kleinen Fehler gemacht hast: 'update' != 'updated'. Wenn Du in `foo1()` nämlich keinen *neuen* Schlüssel 'update' hinzufügst sondern dem vorhanden Schlüssel 'updated' einen neuen Wert zuweist, dann kommt ziemlich sicher das Ergebnis welches Du erwartest:

Code: Alles auswählen

=> foo1()
set(['Telefon'])
set(['updated', 'Telefon'])
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ständig `self.intersect` zu verwenden, finde ich irritierend. Hier mal eine versionsübergreifende Variante, die sich ein paar neue Features von Python 3 zunutze macht:

Code: Alles auswählen

import sys

if sys.version_info >= (3, 0):
    def get_key_diff(a, b):
        return a.keys() - b.keys()

    def get_key_intersection(a, b):
        return a.keys() & b.keys()
else:
    def get_key_diff(a, b):
        return set(a.keys()).difference(b.keys())

    def get_key_intersection(a, b):
        return set(a.keys()).intersection(b.keys())


class DictDifferences(object):
    def __init__(self, before, now):
        self.before = before
        self.now = now

    def get_added_keys(self):
        return get_key_diff(self.now, self.before)

    def get_removed_keys(self):
        return get_key_diff(self.before, self.now)

    def get_changed_keys(self):
        return {
            key for key in get_key_intersection(self.before, self.now)
            if self.before[key] != self.now[key]
        }

    def get_unchanged_keys(self):
        return {
            key for key in get_key_intersection(self.before, self.now)
            if self.before[key] == self.now[key]
        }
Daikoku
User
Beiträge: 66
Registriert: Montag 20. April 2015, 21:14

@BlackJack: Ja Du hast recht. War mein Fehler. Sorry.

Ich habe da noch eine Frage zu meinem besseren Verständnis.

1. Step : d = DictDiff(newDict, oldDict)
2. Step : oldDict['updated'] = time.time()

Mit Step 2 aendert sich der Wert, sodass eine neue Differenz entsteht.

Woher weiß denn jetzt d.changed() das der Key sich geaendert hat, ohne das ein erneutes d = DictDiff(newDict, oldDict)
ausgefuehrt wird.

Vielen Dank fuer Deine Antwort


Sorry hätte ich etwas länger nachgedacht, hätte ich mir diese Frage sparen können.
Mit d.changed(), d.unchanged(), etc. frage ich die zu diesem Zeitpunkt aktuellen dictionaries ab, welche ich mit DictDiff(newDict, oldDict) zuvor definiert habe.

Danke.
Zuletzt geändert von Daikoku am Sonntag 28. Juni 2015, 22:23, insgesamt 1-mal geändert.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Daikoku: DictDifferences bekommt nur Referenzen auf old und new. Das ist ja gerade das Problem bei Deiner DictDifferences-Klasse im Gegensatz zu der von snafu: changed wird immer mit den aktuellen Values von old und new ausgewertet, die Keys werden jedoch nicht aktualisiert, da sie schon beim __init__ einmalig ausgewertet und in intersect gespeichert werden.
Daikoku
User
Beiträge: 66
Registriert: Montag 20. April 2015, 21:14

@Sirius3 Vielen dank für Deine Antwort.

Ich würde dieses nicht als ein Problem bezeichnen, sondern eher als ein spezielles Feature.
Ich habe für ein Problem nach einer Lösung gesucht und unter
http://stackoverflow.com/questions/1165 ... dictionary
auch gefunden. Vielleicht ist dieses Feature hier auch bewusst so gewollt.

Mein Problem ist, dass ich noch nicht so Fitt im Umgang mit Python bin, dass ich auch wirklich
zu 100% verstehe was da wirklich passiert. Selbst wenn ich die Dokumentationen zu den einzelnen
Funktionen lese, kommen für mich dann hin und wieder unerwartete Ergebnisse.

Deine kurze Erklärung hat mir geholfen das Modul besser zu verstehen.

Vielen Dank.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Naja, das wäre aber ein sehr fragiles Feature. Bei so einer Art von API würde ich schon erwarten, dass Veränderungen an den übergebenen Objekten (hier: den beiden Wörterbüchern) sich auch in den Ergebnissen von nachfolgenden Methodenaufrufen niederschlagen. Wenn man da wirklich seitens der Klasse mehrfache Berechnungen der benötigten Mengen einsparen will, dann sollte man doch schon ordentliches Caching betreiben, anstatt den Anwender unnötig zu verwirren. Man sieht ja, was dabei herauskommt.

EDIT: Ich würde das im Grunde überhaupt nicht mit einem Klassendesign implementieren, sondern die benötigten Operationen einfach als Funktionen schreiben. Man stelle sich 3 Mio Wörterbücher vor, die auf Unterschiede geprüft werden sollen und für die jedes Mal ein kurzlebiges `DictDifferences`-Objekt erstellt werden muss...
Daikoku
User
Beiträge: 66
Registriert: Montag 20. April 2015, 21:14

@snafu Vielen Dank für Deine Ausführungen und Anregungen.

Ich werde diese jetzt einmal Testen und komme dann darauf zurück.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Neue Version (nutzt die `viewkeys()`-Methode unter Python 2.7):

Code: Alles auswählen

import operator
import sys

ON_PY3 = sys.version_info >= (3, 0)

_keys = operator.methodcaller(
    'keys' if ON_PY3 else 'viewkeys'
)

class DictDifferences(object):
    def __init__(self, before, now):
        self.before = before
        self.now = now
 
    def get_added_keys(self):
        return _keys(self.now) - _keys(self.before)
 
    def get_removed_keys(self):
        return _keys(self.before) - _keys(self.now)
 
    def get_same_keys(self):
        return _keys(self.before) & _keys(self.now)

    def get_changed_keys(self):
        return {
            key for key in self.get_same_keys()
            if self.before[key] != self.now[key]
        }
 
    def get_unchanged_keys(self):
        return {
            key for key in self.get_same_keys()
            if self.before[key] == self.now[key]
        }
Daikoku
User
Beiträge: 66
Registriert: Montag 20. April 2015, 21:14

Ab hier nehme ich nur noch Bezug auf den letzten Vorschlag von snafu. Vielen Dank dafür.

Ich selber benutze :
*** Python 2.7.10 (default, May 23 2015, 09:44:00) [MSC v.1500 64 bit (AMD64)] on win32. ***
sollte aber auch mit Python 3.x funktionieren.

Step 1.
ich habe den Vorschlag von snafu, jetzt einmal als eigenständiges Modul dictDifferences.py umgesetzt und dabei auf die Implementierung einer Klasse verzichtet.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf8 -*-
#
#    Modul : dictDifferences.py
#
#    Calculate the differences between two dictionaries as:
#    (1.) get_added_keys
#    (2.) get_removed_keys
#    (3.) get_same_keys
#         - keys same in both, values can be different.
#    (4.) get_changed_keys
#         - keys same in both, but changed values.
#    (5.) get_unchanged_keys
#         - keys same in both and unchanged values.

import operator
import sys

ON_PY3 = sys.version_info >= (3, 0)

_keys = operator.methodcaller(
    'keys' if ON_PY3 else 'viewkeys'
)

def get_added_keys(now, before):
    """get_added_keys(new Dict, old Dict)  --> set([.., ..])
    """
    return _keys(now) - _keys(before)

def get_removed_keys(now, before):
    """get_removed_keys(new Dict, old Dict)  --> set([.., ..])
    """
    return _keys(before) - _keys(now)

def get_same_keys(now, before):
    """get_same_keys(new Dict, old Dict)  --> set([.., ..])
    """
    return _keys(before) & _keys(now)

def get_changed_keys(now, before):
    """get_changed_keys(new Dict, old Dict)  --> set([.., ..])
    """
    return {
        key for key in get_same_keys(now, before)
        if before[key] != now[key]
    }

def get_unchanged_keys(now, before):
    """get_unchanged_keys(new Dict, old Dict)  --> set([.., ..])
    """
    return {
        key for key in get_same_keys(now, before)
        if before[key] == now[key]
     }
2. Step - Die Anwendung

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf8 -*-

import sys
import time

# eigene Module
import dictDifferences as dd


old = { 'Name_1' : 'Maria',
        'Name_2' : 'Schulze',
        'Strasse': 'Hauptstr. 6',
        'PLZ'    : '12487',
        'Ort'    : 'Berlin',
        'Land'   : 'Deutschland',
        'Telefon': '+49 30 333435',
        'Mobile' : '+49 172 123456',
        'updated': '' }

new = old.copy()

#    Calculate the differences between two dictionaries as:
#    1. get_added_keys
#    2. get_removed_keys
#    3. get_same_keys
#        - keys same in both, values can be different.
#    4. get_changed_keys
#        - keys same in both, but changed values.
#    5. get_unchanged_keys
#        - keys same in both and unchanged values.

# 1. get_added_keys()
print '=> 1. get_added_keys   : {}'.format(dd.get_added_keys(new, old))
new['LKZ'] = 'D'
print '=>    get_added_keys   : {}\n'.format(dd.get_added_keys(new, old))

# 2. get_removed_keys()
print '=> 2. get_removed_keys : {}'.format(dd.get_removed_keys(new, old))
new.pop('Mobile')
print '=>    get_removed_keys : {}\n'.format(dd.get_removed_keys(new, old))

# 3. get_same_keys()  -  der key Mobile ist in new bereits in 2. geloescht.
print '=> 3. get_same_keys    : {}'.format(dd.get_same_keys(new, old))
old['Name_1'] = 'Maria'
print '=>    get_same_keys    : {}\n'.format(dd.get_same_keys(new, old))

# 4. get_changed_keys()
print '=> 4. get_changed_keys : {}'.format(dd.get_changed_keys(new, old))
old['updated'] = time.time()
print '=>    get_changed_keys : {}\n'.format(dd.get_changed_keys(new, old))

# 5. get_unchanged_keys()
print '=> 5. get_unchanged_keys : {}'.format(dd.get_unchanged_keys(new, old))
old['Name_1'] = 'Marie'
print '=>    get_unchanged_keys : {}\n'.format(dd.get_unchanged_keys(new, old))

#=> 1. get_added_keys   : set([])
#=>    get_added_keys   : set(['LKZ'])
#
#=> 2. get_removed_keys : set([])
#=>    get_removed_keys : set(['Mobile'])
#
#=> 3. get_same_keys    : set(['Name_2', 'updated', 'Land', 'Ort', 'PLZ', 'Telefon', 'Name_1', 'Strasse'])
#=>    get_same_keys    : set(['Name_2', 'updated', 'Land', 'Ort', 'PLZ', 'Telefon', 'Name_1', 'Strasse'])
#
#=> 4. get_changed_keys : set([])
#=>    get_changed_keys : set(['updated'])
#
#=> 5. get_unchanged_keys : set(['Name_2', 'Name_1', 'Ort', 'PLZ', 'Telefon', 'Land', 'Strasse'])
#=>    get_unchanged_keys : set(['Name_2', 'Land', 'Ort', 'PLZ', 'Telefon', 'Strasse'])
Ich selber bin mit dem Ergebnis mehr als zufrieden.

Ich habe dennoch eine Frage zu :

Code: Alles auswählen

    return {
        key for key in get_same_keys(now, before)
        if before[key] == now[key]
     }
Bei einen return { .... } würde ich die Rückgabe eines Dictionaries erwarten.
Die list comprehension selber habe ich verstanden.

Aber warum hier return { ... } ???
Warum die geschweiften Klammern und was bewirken diese ??
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Daikoku: die geschweiften Klammern bezeichnen eine Set-Comprehension.
Daikoku
User
Beiträge: 66
Registriert: Montag 20. April 2015, 21:14

@Sirius3 Vielen Dank.

Habe wieder etwas Neues dazu gelernt.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Dass geschweifte Klammern je nach Kontext sowohl ein Dictionary, als auch eine Dict-Comprehension oder eine Set-Comprehension bezeichnen können, ist ein eher unglücklicher Umstand. Geschweifte Klammern sind halt die aus der Mathematik üblichen Literale für Mengen. Aber gleichzeitig sind sie in Python die "traditionelle" Art, ein Wörterbuch zu erstellen.

Wenn man das zu verwirrend findet, dann kann man auf die Literale in Zusammenhang mit Mengen verzichten und stattdessen die Form ``set(each for each in elems if condition)`` schreiben. Zu beachten ist natürlich, dass hier ganz bewusst *keine* eckigen Klammern innerhalb von ``set(...)`` gesetzt wurden. Ansonsten hätte Python nämlich als unnötigen Zwischenschritt eine Liste mit den benötigten Elementen erstellt und würde diese Liste dann ans `set`-Objekt übergeben, um die Liste direkt im Anschluss wieder wegzuwerfen.
Daikoku
User
Beiträge: 66
Registriert: Montag 20. April 2015, 21:14

@snafu, und Du schreibst mir, dass mein Code an der ein oder anderen Stelle irritierend ist.
Ich habe auch nur einen unglücklichen Umstand zur Lösung gewählt.

Ich habe verstanden, was Du dem Grunde nach gemeint hast.
Ich finde Deinen Code auch viel klarer strukturiert und werde diesen in der Zukunft verwenden.

Vielen Dank für Deine Zeit und Mühen.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Daikoku hat geschrieben:@snafu, und Du schreibst mir, dass mein Code an der ein oder anderen Stelle irritierend ist.
Ich habe auch nur einen unglücklichen Umstand zur Lösung gewählt.
Verdammt, jetzt hat er mich... :(

;)
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Man kann das Spiel mit den mengenbasierten Operationen übrigens noch lustig weiter treiben (geändert wurden die Implementierungen für `get_changed_keys` und `get_unchanged_keys`):

Code: Alles auswählen

import operator
import sys
 
ON_PY3 = sys.version_info >= (3, 0)
 
_keys = operator.methodcaller('keys' if ON_PY3 else 'viewkeys')

_items = operator.methodcaller('items' if ON_PY3 else 'viewitems')
 
def get_added_keys(now, before):
    return _keys(now) - _keys(before)
 
def get_removed_keys(now, before):
    return _keys(before) - _keys(now)
 
def get_same_keys(now, before):
    return _keys(now) & _keys(before)

def get_unchanged_keys(now, before):
    return {k for k, v in _items(now) & _items(before)}
 
def get_changed_keys(now, before):
    return get_same_keys(now, before) - get_unchanged_keys(now, before)
Daikoku
User
Beiträge: 66
Registriert: Montag 20. April 2015, 21:14

@snafu, Dein letztes Script gefällt mir nicht so sehr, weil es performance technisch langsamer ist.
siehe hierzu Step 3.

Ich habe das aus meiner Sicht beste Ergebnis unten noch einmal angefügt.
Gibt es hier noch Möglichkeiten der Optimierung ?

Code: Alles auswählen


# Step 1 : Mein ursprüngliches Script mit einem Klassendesign

         2100002 function calls in 6.140 seconds

   Ordered by: cumulative time, internal time, call count

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    1.222    1.222    6.140    6.140 loeschen_3.py:14(foo)
   100000    1.131    0.000    2.269    0.000 dictDifferences3.py:34(get_unchanged_keys)
   100000    0.729    0.000    1.140    0.000 dictDifferences3.py:15(__init__)
   899999    1.137    0.000    1.137    0.000 dictDifferences3.py:35(<genexpr>)
   100000    0.435    0.000    0.888    0.000 dictDifferences3.py:30(get_changed_keys)
   200000    0.452    0.000    0.452    0.000 dictDifferences3.py:31(<genexpr>)
   200000    0.233    0.000    0.233    0.000 {method 'keys' of 'dict' objects}
   100000    0.178    0.000    0.178    0.000 {method 'intersection' of 'set' objects}
   100000    0.178    0.000    0.178    0.000 dictDifferences3.py:24(get_added_keys)
   100001    0.176    0.000    0.176    0.000 {method 'copy' of 'dict' objects}
   100000    0.163    0.000    0.163    0.000 dictDifferences3.py:27(get_removed_keys)
   100000    0.104    0.000    0.104    0.000 {time.time}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        0    0.000             0.000          profile:0(profiler)

# Step 2 : Dein erstes Script

         1100003 function calls in 4.408 seconds

   Ordered by: cumulative time, internal time, call count

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    1.110    1.110    4.408    4.408 loeschen_3.py:14(foo)
   100000    0.407    0.000    1.128    0.000 dictDifferences1.py:54(get_unchanged_keys)
   100000    0.416    0.000    1.096    0.000 dictDifferences1.py:45(get_changed_keys)
   200000    0.772    0.000    0.772    0.000 dictDifferences1.py:39(get_same_keys)
   100000    0.365    0.000    0.365    0.000 dictDifferences1.py:27(get_added_keys)
   100000    0.331    0.000    0.331    0.000 dictDifferences1.py:58(<setcomp>)
   100000    0.322    0.000    0.322    0.000 dictDifferences1.py:33(get_removed_keys)
   100000    0.298    0.000    0.298    0.000 dictDifferences1.py:49(<setcomp>)
   100001    0.175    0.000    0.175    0.000 {method 'copy' of 'dict' objects}
   100000    0.107    0.000    0.107    0.000 {time.time}
   100000    0.106    0.000    0.106    0.000 {method 'pop' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        0    0.000             0.000          profile:0(profiler)

# Step 3 : Dein letztes Script
         1100003 function calls in 4.820 seconds

   Ordered by: cumulative time, internal time, call count

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    1.093    1.093    4.820    4.820 loeschen_3.py:14(foo)
   200000    1.330    0.000    1.796    0.000 dictDifferences2.py:50(get_unchanged_keys)
   100000    0.452    0.000    1.738    0.000 dictDifferences2.py:44(get_changed_keys)
   200000    0.466    0.000    0.466    0.000 dictDifferences2.py:53(<setcomp>)
   100000    0.382    0.000    0.382    0.000 dictDifferences2.py:38(get_same_keys)
   100000    0.371    0.000    0.371    0.000 dictDifferences2.py:26(get_added_keys)
   100000    0.329    0.000    0.329    0.000 dictDifferences2.py:32(get_removed_keys)
   100001    0.178    0.000    0.178    0.000 {method 'copy' of 'dict' objects}
   100000    0.110    0.000    0.110    0.000 {time.time}
   100000    0.109    0.000    0.109    0.000 {method 'pop' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        0    0.000             0.000          profile:0(profiler)
Das aus meiner Sicht beste Ergebnis :

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf8 -*-
#
#    Modul : dictDifferences.py
#
#    Calculate the differences between two dictionaries as:
#    (1.) get_added_keys
#    (2.) get_removed_keys
#    (3.) get_same_keys
#         - keys same in both, values can be different.
#    (4.) get_changed_keys
#         - keys same in both, but changed values.
#    (5.) get_unchanged_keys
#         - keys same in both and unchanged values.

import operator
import sys


ON_PY3 = sys.version_info >= (3, 0)

_keys = operator.methodcaller(
    'keys' if ON_PY3 else 'viewkeys'
)

def get_added_keys(now, before):
    """get_added_keys(new Dict, old Dict)  --> set([.., ..])
    """
    return _keys(now) - _keys(before)

def get_removed_keys(now, before):
    """get_removed_keys(new Dict, old Dict)  --> set([.., ..])
    """
    return _keys(before) - _keys(now)

def get_same_keys(now, before):
    """get_same_keys(new Dict, old Dict)  --> set([.., ..])
    """
    return _keys(before) & _keys(now)

def get_changed_keys(now, before):
    """get_changed_keys(new Dict, old Dict)  --> set([.., ..])
    """
    return {
        key for key in get_same_keys(now, before)
        if before[key] != now[key]
    }

def get_unchanged_keys(now, before):
    """get_unchanged_keys(new Dict, old Dict)  --> set([.., ..])
    """
    return {
        key for key in get_same_keys(now, before)
        if before[key] == now[key]
     }
Antworten