Zugriff auf dictionary im dictionary

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
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

Hi,

ich habe einen Dictionary, der einen Dictionary beinhaltet:

Code: Alles auswählen

mydict = {'a':{'hexe':'besen}, 'b':{'hexe':'Kuh'}}
Jetzt will ich a->hexe->besen verändern:

Clever wie ich bin mache ich folgendes:

Code: Alles auswählen

neu = 'Sandkastenhexe'

mydict['a'].update('hexe':neu)
print (mydict) liefert aber:

Code: Alles auswählen

{'a':{'hexe':'Sandkastenhexe'}, 'b':{'hexe':'Sandkastenhexe}}
Es wurden also bei A und B Veränderungen vorgenommen. Ebenso wenn ich in den Dictionary bei b was appendieren möchte:

Code: Alles auswählen

append = {'milka':'schokolade'}

mydict['b'].update(append)
dann wird das Dingen bei beiden ergänzt und ich kriege die Appends in beiden Teilen:

Code: Alles auswählen

{'a':{'hexe':'Sandkastenhexe', 'milka':'schokolade'}, 'b':{'hexe':'Sandkastenhexe,'milka':'schokolade'}}
Was mache ich falsch?
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@Papp Nase: Du solltest keine Beispiele posten, die Du so nicht durchgeführt hast, und die deshalb auch wunderbar funktionieren:

Code: Alles auswählen

>>> mydict = {'a':{'hexe':'besen'}, 'b':{'hexe':'Kuh'}}
>>> mydict['a'].update({'hexe':'kochtopf'})
>>> mydict
{'a': {'hexe': 'kochtopf'}, 'b': {'hexe': 'Kuh'}}
sondern das richtige Beispiel:

Code: Alles auswählen

>>> hexe = {'hexe':'besen'}
>>> mydict = {'a': hexe, 'b':hexe}
>>> mydict['a'].update({'hexe':'kochtopf'})
>>> mydict
{'a': {'hexe': 'kochtopf'}, 'b': {'hexe': 'kochtopf'}}
Und hier siehst Du gleich, dass 'a' und 'b' auf das gleiche Unterdictionary verweisen. Du änderst halt das hexe-Dictionary.
Übrigens ist ein update-Aufruf mit einem Key blödsinn, weil man per Indexzuweisung viel klarer sieht, was gemacht wird.
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

Sirus3 hat geschrieben:Und hier siehst Du gleich, dass 'a' und 'b' auf das gleiche Unterdictionary verweisen
ok, genau dass war mein Fehler. Kann ich es in Python irgendwie so machen, dass Hexe = {'Hexe':'Besen'} nicht als Variable angesehen wird, sondern als Konstante? Oder als neue Variable mit unabhängigen Speicherstellen im Speicher?
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Wenn es eine Konstante wäre, dann könntest du es nicht ändern. Wenn du zwei verschiedene Dictionaries möchtest, dann musst du einfach zwei erstellen. In diesem Fall ginge das mit der copy-Methode auf einem Dictionary. Dabei werden allerdings keine Kopien der Schlüssel und der Werte erzeugt. Meistens reicht das aber aus. Was willst du denn überhaupt machen, dass du dieses Verhalten benötigst?
Das Leben ist wie ein Tennisball.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Deine ganze Klasse ist merkwürdig. (Edit: ... und wenn du den Code nicht gelöscht hättest wüsste man auch noch worauf ich mich beziehe :evil: )

set-Methoden benötigen keinen Rückgabewert. Bezeichner sollten keinen Typ enthalten. Das was du als Parameter button nennst ist kein Button sondern der Name eines Buttons.

Alternativ könnte das so aussehen - und dann hat man langsam das Gefühl gar keine Klasse zu benötigen, zumindest nicht mit den bisher vorhandenen Methoden.

Code: Alles auswählen

class Buttons():
    def __init__(self):
        self.buttons = {}

    def add_button(self, name):
        self.buttons[name] = {'id': None, 'options': None}

    def set_ID(self, name, ID):
        if name in self.buttons:
            self.buttons[name]['id'] = ID

    def get_ID(self, name):
        return self.buttons[name]['id'] if name in self.buttons else None

    def set_options(self, name, options):
        if name in self.buttons:
            self.buttons[name]['options'] = options

    def get_options(self, name):
        return self.buttons[name]['options'] if name in self.buttons else None

    def get_names(self):
        return self.buttons.keys()
Jetzt gibt es natürlich immer noch das Problem, wie du beim Abruf von Daten über die get-Methoden mit nicht vorhandenen Buttons umgehst. Existiert der Name nicht, dann bekommst du None, den gleichen Wert der als Default existiert. Du weißt also beim Einsatz dieser Methoden von außen nicht, ob der Button existiert oder nicht. Ich halte das für keine glückliche Designentscheidung, aber vielleicht passt es ja in deinem Kontext.

Das gleiche Problem hast du bei deinen set-Methoden. Existiert der angegebene Button nicht, dann bekommst du beim Versuch dessen (nicht vorhandene) Werte zu ändern keinen Fehler. Das ist definitiv seltsam.
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

/me hat geschrieben: Das ist definitiv seltsam.
Warum ist denn das seltsam? Wenn der Rückgabewert für das Element - z.B. ID noch nicht gesetzt wurde, dann ist der Wert noch nicht gesetzt und ich kann ihn nicht aus versehen für eine weitere Operation verwenden.

Wenn Du eine bessere Möglichkeit hättest, wie würdest Du sie denn lösen? Würde es einen struct-Befehl wie in C geben, dann könnte ich mir selber schön einen Datensatz basteln und den in ein Array packen. Geht ja hier aber nicht. Also muss ich mir mittels Klasse ein Ersatzkonstrukt erzeugen. Ich stehe grade mächtig auf einem Schlauch - nicht ein Wasserschlauch, sondern Schlauch steht hier für den Ausdruck eines Problems, dass es zu bewältigen gilt. Das Problem ist, wie transportiere ich Informationen zwischen unterschiedlichen Klassen hinauf und herunter.

Ich habe jetzt ein Programmentwurfsmuster, dass aus mehreren Dateien/Klassen besteht. Ganz oben in der Hirachie steht eine Chef-Klasse - gewissermassen eine Wurzel. Mein Programm hat jetzt mehrere Aufgaben zu erfüllen, deshalb gibt es mehrere Klassen für die Aufgaben, die dann von der Chef-Klasse aufgerufen werden. Eine Kommunikation findet nur im direkten Strang statt. Aufgabenklasse A kommuniziert nicht mit Aufgabenklasse B, sondern nur über die Chef-Klasse.

Folgendes Beispiel:

Ein Programm holt Messwerte von drei Thermometern. Die Messwerte sollen in Diagrammen dargestellt werden. Jedes Diagramm hat spezifische Funktionen. Es gibt daneben eine Hauptbedienleite, um das Programm zu beenden, eine Messung zu starten ... .

Die Hauptklasse steuert alles - also ein HauptController - das ist die root-Klasse. Dann gibt es eine Klasse, die die Anzeige steuert. Die Anzeigeklassen besteht wieder aus Unterelementen - ein Anzeige (Frame) für die Hauptbuttons und Frames für die einzelnen Plots. Dann gibt es eine weitere Klasse, die Daten aus dem Messfühler holt.

class MainController ---> class MainView
---> class GetData

class MainView ---> class PlotView1
---> class PlotView2
---> class ButtonView

in Mainview erzeuge ich Frames für jeden Bereich, den ich an die Unterklasse übergebe

class Mainview ()
def __init__(self, master)
self.plot1frame = tk.Frame(master)
self.buttonframe = tk.Frame(master)
buttonview = ButtonView(master)

In ButtonView lege ich fest, wie die einzelnen Buttons angeordnet werden (z.B. mit der Pack- oder der Grid-Methode).

ABER - in Maincontroller befindet sich z.B. die Information, wie das Programm beendet wird - dort gibt's die Funktion do_exit, die die mainloop beendet und darauf reagieren soll, wenn ich den Button Beenden drücke.

Also muss ich irgendwie die Informationen über die Buttons vom MainController zum ButtonView transportieren. Dazu erzeuge ich mir im MainController ein Button-Element aus der Button-Klasse und schleuse das von der Wurzel bis zum Ende hin durch:

MainController:

buttons=Buttons()
Dort will ich die Buttons definieren, z.B. die Namen und auch die Info, dass die Funktion do_exit aufgerufen werden soll. Und wenn ich viele Buttons habe, dann muss ich viele Informationen durchschleusen, darum wollte ich das in Form einer Klasse lösen, wo ich alle wichtigen Informationen zu den Buttons speichere und dann in jedem Strang verfügbar habe. Ich übergebe also Buttons bei jedem Aufruf

MainController():
root = tk.Tk()
buttons = Buttons()
MainView(root, buttons)

MainView():
ButtonView(self.buttonframe, buttons).

Und in ButtonView kann ich dann mit den Befehlen grid/pack die Buttons anordnen und über Options die Optionen wie Schriftart, Größe, Breite, Farbe ... übergeben.

Aber Options wie "command=" muss ich ja im MainController übergeben, weil die Funktion do_exit im MainController vorhanden ist, da dort auch die Funktion root.mainloop gestartet wird. Ich könnte also entweder den Zeiger für die Funktion durchschleusen, was ich aber unpraktisch halte - drum habe ich mich entschieden, eine Datenklasse Buttons zu erstellen, um die nötigen Informationen zu den Buttons durch alle Ebenen hindurchschleusen kann. Und genauso soll es später auch für andere Anzeigedinger sein - wie die Hakekästchen und so.

Hast Du eine bessere Idee?
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@Papp Nase: Dort wo man in C gezwungen wird, ein Struct zu benutzen, gibt es in Dynamischen Sprachen, wie Python, viele Möglichkeiten. Oft wird statt eines Structs ein Dictionary benutzt. Das erklärt aber nicht, für was Du eine Button-Sammel-Klasse brauchst und warum Du so Low-Level-Objekte wie Knöpfe herumreichen mußt.

Wenn Du Dateien/Klassen schreibst, meinst Du hoffentlich nicht, dass Du pro Datei eine Klasse definiert hast. Python ist kein Java.

Was soll eine Klasse GetData denn machen? Das ist eine Tätigkeit, also eine Funktion und keine Klasse.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Papp Nase hat geschrieben:Warum ist denn das seltsam? Wenn der Rückgabewert für das Element - z.B. ID noch nicht gesetzt wurde, dann ist der Wert noch nicht gesetzt und ich kann ihn nicht aus versehen für eine weitere Operation verwenden.
Und wenn das Element selber nicht existiert, dann akzeptierst du eine Zuweisung einer ID an ein nicht vorhandenes ohne Rückmeldung, dass das Element noch nicht existiert?

Ich versuche es mal mit einer Analogie: Ich sage meinem Kollegen: "Hefte diese Unterlagen im Ordner 2015 ab." Ordner 2015 existiert aber nicht. Mit deiner Logik würdest du als Kollege jetzt sagen: "Alles klar. Erledigt." Es ist aber natürlich nichts erledigt. Erwarten würde ich daher, dass ein Hinweis darauf kommt, dass der Ordner nicht vorhanden ist. Erst dann kann ich mir Gedanken darüber machen, welchen Fehler ich bei dieser Anweisung gemacht habe und das Problem korrigieren.
Antworten