Seite 1 von 1

Dictionary vor Veränderung schützen! (kein CBR sondern CBV)

Verfasst: Donnerstag 14. Juni 2012, 10:10
von Apotekarnes
Hallo!

Mein Programm benutzt Dictionaries von mehreren Gigabytes im Hauptspeicher (Großrechner). Diese werden teilweise an Funktionen übergeben. In den Funktionen sollen (lokal!) Modifikationen an den übergebenen Dictionaries durchgeführt, die sich jedoch leider direkt nach außerhalb der Funktionen durchschlagen. Klar: Call by reference, da mutabler Datentyp!
Wie kann ich die Dictionaries vor Veränderung durch die Funktionen schützen?

Sprich - ich will innerhalb der Funktion das Dictionary nur als lokale Variable behandeln!

Ich kenne bereits den Trick für Listen innerhalb der Funktionen mit templist = oldlist[:].

Gibt es sowas auch für Dictionaries?

Ich habe es auch schon mit dem Modul "copy" und dann templist = copy.deepcopy(oldlist) probiert. Das funktioniert, dauert aber leider unglaublich lange bei Dictionaries von mehreren GB Größe!

Es wäre also eine Schutzfunktion wünschenswert, bei der nicht die gesamte Variable in gesamter Größe im Hauptspeicher verdoppelt / kopiert werden muss.
Die Dictionaries werden durch Auslesen aus riesigen Dateien erzeugt. Durch die Übergabe schon ausgelesener Daten an die Funktionen will ich ein neues zeitaufwändiges Auslesen der Dateien vermeiden. Leider verliere ich diese gesparte Zeit wieder beim Anfertigen der Arbeitskopie.

Irgendwelche Ideen? Bräuchte da dringend Hilfe.

Danke,
Apotekarnes

Re: Dictionary vor Veränderung schützen! (kein CBR sondern C

Verfasst: Donnerstag 14. Juni 2012, 10:32
von cofi
Nein Dictionaries kann man nicht auf read-only schalten, selbst wenn wuerde das (im naiven Fall) doch wieder in Kopien resultieren und die fallen deiner Analyse zufolge ja weg.

Beschreibe doch mal wie die Zugriffs-/Modifikationsmuster aussehen.

Wenn du z.B. nur eine lokale "Modifikation" brauchst, wuerde ich ein 2-stufiges Dictionary bauen, das aus einem Lokalen Teil und einem globalen/default Teil besteht. Saemtliche Modifikationen finden im Lokalen Dictionary statt und Lookups in beiden, wobei das Lokale bevorzugt wird.

Re: Dictionary vor Veränderung schützen! (kein CBR sondern C

Verfasst: Donnerstag 14. Juni 2012, 10:50
von Apotekarnes
Die Modifikation wäre einfach die Veränderung eines Values bei einem bestimmten Key.
Es können aber auch tausende Einträge verändert werden. Das muss die entsprechende Funktion feststellen.

Eine Aufteilung in global / lokal wird entweder bedeuten, dass ich den gesamten Inhalt des urspründlichen Dictionaries verdoppeln muss, was wieder in einer Verdopplung des Speicherbedarfs resultiert oder ich deklariere genau die zu verändernden Bereiche als lokal. Das geht auch nicht, da ich diese Bereiche nicht von außerhalb der Funktion kenne und wenn ich sie kennen würde, so würde ich trotzdem nur Veränderungen am Dictionary innerhalb der Funktion erlauben wollen.


Man kommt wohl nicht drum herum, irgendeine Form von Kopie der Daten anzufertigen. Da ich diese Kopie nicht außerhalb, sondern nur innerhalb der Funktionen haben will (zusätzlicher Speicher wird dann nur während des Funktionsdurchlaufs benötigt), muss ich sie zu Beginn der Funktion erstellen.
copy.deepcopy() ist aber VIEL zu langsam.
Ist es geschickter, das Originaldictionary Key für Key durchzugehen und die Einträge in ein neues Dictionary abzuschreiben?
Laufzeit?

Re: Dictionary vor Veränderung schützen! (kein CBR sondern C

Verfasst: Donnerstag 14. Juni 2012, 11:01
von cofi
Nein du verstehst mich falsch. Du deklarierst nichts, sondern Kapselst dein Dictionary in eine Klasse, die ein weiteres Dictionary enthaelt, eben das lokale.

Speicherplatz belegst du nur fuer neue Werte mehr, das hast du aber sowieso, denn die Paare, die du behalten willst, musst du ja immernoch speichern.

Hier eine kurze Skizze in Code:

Code: Alles auswählen

class TwoLevelDictionary(object):
    def __init__(self, default_dict):
        self.default = default_dict
        self.local = {}

    def __setitem__(self, key, value):
        self.local[key] = value

    def __getitem__(self, key):
        if key in self.local:
            return self.local[key]
        return self.default[key]
Das default_dict wird hierbei nie veraendert. Damit du das ganze auch wirklich als dict benutzen kannst, brauchst du evtl noch ein bisschen mehr Arbeit, aber das kommt auf deine Anforderungen an.

Re: Dictionary vor Veränderung schützen! (kein CBR sondern C

Verfasst: Donnerstag 14. Juni 2012, 11:06
von EyDu
Noch als kleine Ergänzung:

Code: Alles auswählen

    def __getitem__(self, key):
        try:
            return self.local[key]
        except KeyError:
            return self.default[key]

Re: Dictionary vor Veränderung schützen! (kein CBR sondern C

Verfasst: Donnerstag 14. Juni 2012, 11:19
von kbr
Hier noch eine Variante für __getitem__():

Code: Alles auswählen

def __getitem__(self, key):
    return self.local.get(key, self.default[key])

Re: Dictionary vor Veränderung schützen! (kein CBR sondern C

Verfasst: Donnerstag 14. Juni 2012, 11:26
von cofi
kbr hat geschrieben:Hier noch eine Variante für __getitem__():

Code: Alles auswählen

def __getitem__(self, key):
    return self.local.get(key, self.default[key])
Davon ist abzuraten. `self.default[key]` wird _immer_ ausgewertet, auch wenn es den Key in local gibt. Das fuehrt zu einem Fehler, wenn der Key neu in local ist und nicht ueberschrieben wurde.

Code: Alles auswählen

In [14]: %paste
class TwoLevelDictionary(object):
    def __init__(self, default_dict):
        self.default = default_dict
        self.local = {}

    def __setitem__(self, key, value):
        self.local[key] = value

    def __getitem__(self, key):
        if key in self.local:
            return self.local[key]
        return self.default[key]
## -- End pasted text --

In [15]: d = TwoLevelDictionary({})

In [16]: d['foo']
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
/home/cofi/<ipython-input-16-1af12a7c631f> in <module>()
----> 1 d['foo']

/home/cofi/<ipython-input-14-b4cc57198c76> in __getitem__(self, key)
     10         if key in self.local:
     11             return self.local[key]
---> 12         return self.default[key]

KeyError: 'foo'

In [17]: d['foo'] = 2

In [18]: TwoLevelDictionary.__getitem__ = lambda self, key: self.local.get(key, self.default[key])

In [19]: d['foo']
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
/home/cofi/<ipython-input-19-1af12a7c631f> in <module>()
----> 1 d['foo']

/home/cofi/<ipython-input-18-e0ea848b36db> in <lambda>(self, key)
----> 1 TwoLevelDictionary.__getitem__ = lambda self, key: self.local.get(key, self.default[key])

KeyError: 'foo'

In [20]: d.local
Out[20]: {'foo': 2}

Re: Dictionary vor Veränderung schützen! (kein CBR sondern C

Verfasst: Donnerstag 14. Juni 2012, 11:46
von Apotekarnes
Vielen Dank, das sind wirklich konstruktive Vorschläge!

Wenn ich mich nicht irre, hat diese Datenstruktur auch sämtliche Funktionen des ursprünglichen Dictionaries.
Sehr gut!

Re: Dictionary vor Veränderung schützen! (kein CBR sondern C

Verfasst: Donnerstag 14. Juni 2012, 11:50
von kbr
cofi hat geschrieben:`self.default[key]` wird _immer_ ausgewertet, auch wenn es den Key in local gibt.
Danke, stimmt, kann dann einen KeyError auslösen - und der User wundert sich. Wieder ein Argument mehr für TDD.

Re: Dictionary vor Veränderung schützen! (kein CBR sondern C

Verfasst: Donnerstag 14. Juni 2012, 12:03
von Dav1d
Es gibt doch seit Python 2.7 `dictionary-views`, wären die nichts?

http://docs.python.org/dev/whatsnew/2.7 ... nary-views
http://docs.python.org/library/stdtypes.html#dict

Edit: Anscheinend nicht, ich dachte man kann `views` auf einzelne Key-Value Paare haben.

Re: Dictionary vor Veränderung schützen! (kein CBR sondern C

Verfasst: Donnerstag 14. Juni 2012, 12:36
von cofi
Apotekarnes hat geschrieben:Wenn ich mich nicht irre, hat diese Datenstruktur auch sämtliche Funktionen des ursprünglichen Dictionaries.
Nein, du irrst dich. Iterieren ueber das dictionary geht beispielsweise nicht, ueberpruefen ob ein Key enthalten ist ebenfalls nicht, usw.
Aber ob das wichtig ist, haengt von deinen Anforderungen ab, wenn du sowas brauchst, schau dir `collections.MutableMapping` an und erbe davon.

@kbr: Ich weiss nicht ob die fehlende Kenntnis von Python Semantik wirklich ein gueltiges oder ernstzunehmendes Argument fuer TDD ist ;)

@Dav1d: Views koennten ein Teil der Loesung sein, aber am Ende hat man ja doch noch das Problem, dass man gar kein Immutable Dict haben will, sondern ein selektiv veraenderbares. D.h. man braeuchte auch wieder eine Moeglichkeit beides zu kombinieren und landet dann IMO auch wieder bei einem Proxy wie ich ihn skizziert habe. Mit views koennte man aber IMO sicher gehn, dass die Funktionen das urspruengliche Dict auf keinen Fall aendern, aber ich glaube man bekommt keinen view, der sich tatsaechlich dict-like verhaelt.

Re: Dictionary vor Veränderung schützen! (kein CBR sondern C

Verfasst: Donnerstag 14. Juni 2012, 14:32
von snafu
EyDu hat geschrieben:Noch als kleine Ergänzung:

Code: Alles auswählen

    def __getitem__(self, key):
        try:
            return self.local[key]
        except KeyError:
            return self.default[key]
Das macht eigentlich nur dann Sinn, wenn verhältnismäßig viele Werte lokal vorgehalten werden, da das Heraussuchen des Wertes (bzw des Schlüsselnamens als Hashwert) in dem Fall nur einmal stattfände. Andernfalls erzeugt diese Variante jedoch relativ hohe (Performance-)Kosten für das jeweilige Werfen der Ausnahme, auf die man dann ja mit dem globalen Wert reagieren würde.

Man könnte halt beide Möglichkeiten mal in der Praxis testen (sofern der Unterschied überhaupt eine Rolle spielen sollte). Es kommt irgendwo auch darauf an, was die Funktionen genau mit dem übergebenen `dict` veranstalten. Denn berechtigt ist die Annahme, dass lokal eingefügte Werte auch lokal benötigt werden, ja durchaus. Fragt sich halt nur, in welchem Verhältnis diese zu der Anzahl an benötigten Original-Werten stehen.