Anwendung von __dict__[]

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.
hypnoticum
User
Beiträge: 132
Registriert: Dienstag 15. März 2011, 15:43

Hallo,
ich habe eine Schleife in der ich mehrere Instanzen erzeuge. Diesen Instanzen sollen dynamisch attribute zugewiesen werden. Bei dem geschachtelten Aufruf von __dict__[] klappt das nicht mehr.
kann jemand erklären warum, bzw. mir sagen wie ich es machen soll?

Code: Alles auswählen

class ClassTyp(object):
      def __init__(self):
             GroupDict = {'Key_1': 'Val_1', 
                               'Key_2': 'Val_2',
                               'Key_3': 'Val_3'}
             for Test in GroupDict:
                    self.__dict__[Test].__dict__['Var'] = dynVal
...
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.

Beschreibe mal, was du eigentlich machen willst. Was du da vor hast sieht schon sehr seltsam aus.

Sebastian
Das Leben ist wie ein Tennisball.
BlackJack

@hypnoticum: Von `__dict__` würde ich grundsätzlich erst einmal abraten. Es gibt `getattr()` und `setattr()` um dynamisch Attribute zu lesen und zu binden.
hypnoticum
User
Beiträge: 132
Registriert: Dienstag 15. März 2011, 15:43

Ich will Checkboxen in einer Schleife erzeugen.
Jeder Checkbox wird dabei ein "text" und eine "variable" zugewiesen.
der wert der variablen kommmt aus einem dictionary.
der wert für text kommt aus einem dictionary.
beide dictionaries stimmen in den "keys" für "text" und "variable" überein
beim beenden des dialogs sollen die werte der jeweiligen variablen in dem einen dictionary gespeichert werden.
das geschieht wieder in einer schleife.
hypnoticum
User
Beiträge: 132
Registriert: Dienstag 15. März 2011, 15:43

@BlackJack:
Ich versteh mal wieder nicht wie ich das mit "setattr()" anstellen soll.
Wie erzeuge ich ein Attribut 'Var' vom Typ "Tkinter.IntVal" in dem dynamisch erzeugten Namensraum/Attribut "Test" der Kasse ClassTyp mit setattr()?
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Relativ einfach:

Code: Alles auswählen

for name in checkbutton_names:
    setattr(object, name, tkinter.IntVar())
Die Namen müssen Strings sein. Edit: Achso und "object" sollte natürlich deine Instanz sein, bei deinem Aufruf wird das warscheinlich "self" sein.
Zuletzt geändert von Xynon1 am Dienstag 12. April 2011, 14:49, insgesamt 1-mal geändert.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
hypnoticum
User
Beiträge: 132
Registriert: Dienstag 15. März 2011, 15:43

Schulle, ich meinte "Tkinter.IntVar", nicht "IntVal"
aber was mache ich mit "Test"?

Code: Alles auswählen

setattr(setattr(self, Test, None), 'Var', Tkinter.IntVar())
Zuletzt geändert von hypnoticum am Dienstag 12. April 2011, 14:52, insgesamt 1-mal geändert.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Sry, Tippfehler war gerade noch woanders mit meinen Gedanken, macht aber keinen Unterschied. Ich korrigiere es oben.
Oha, hatte das bei dir so gesehen, habe ich ebend selbst nicht mitbekommen :D
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Kannst du mal definieren, was für eine Strucktur bei dir am Ende raus kommen soll? Etwa so was hier ?

Code: Alles auswählen

instance = ClassTyp()
instance.Test.Var1
instance.Test.Var2
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
hypnoticum
User
Beiträge: 132
Registriert: Dienstag 15. März 2011, 15:43

Code: Alles auswählen

self.Key_1.Var
self.Key_2.Var
self.Key_3.Var
wobei die Attribute "Var" halt vom Typ Tkinter.IntVar" sein sollen. Der Typ von "Key_x" ist egal.
BlackJack

@hypnoticum: Wenn der Typ egal ist, warum gibt es das Objekt überhaupt? Warum steckst Du das nicht einfach in ein normales Dictionary? Beziehungsweise warum heissen die Attribute von den "egal"-Objekten alle `Var`? Schreibt tatsächlich irgendjemand Quelltext in dem über ``obj.Key_1.Var`` darauf zugegriffen wird?
hypnoticum
User
Beiträge: 132
Registriert: Dienstag 15. März 2011, 15:43

Das Objekt dient der Unterscheidung der Variablen "Var". Es war von mir eigentlich so gedacht, mehrere Namensräume "Key_x" anzulegen, in denen immer wieder die gleichen Attribute mit anderen Werten zu finden sind.
Ich werde es mal mit einem Dictionary probieren.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Also so was oder wie?

Code: Alles auswählen

self.Key_1.Var_1
self.Key_1.Var_2
self.Key_1.Var_3
self.Key_2.Var_1
self.Key_2.Var_2
self.Key_3.Var_1
self.Key_3.Var_2
....
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
hypnoticum
User
Beiträge: 132
Registriert: Dienstag 15. März 2011, 15:43

@Xynon1: ja so. Die anderen "Var" könnten dann auch andere Typen sein. Ich werde es wohl nun mit Dictionaries machen. Falls du eine Lösung weisst würde es mich trotzdem sehr interessieren.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Naja, immer noch setattr, aber bevor du damit rumspielst solltest du dir erstmal eine Datenstrucktur überlegt haben und halt prüfen ob es wirklich so kompliziert sein muss.

Denn ob man wirklich sowas haben will ist fragwürdig.

Code: Alles auswählen

class ClassTyp(object): pass
class Test(object): pass

instance = ClassTyp()
checkbutton_names = ["var_1", "var_2", "var_3"]
test_names = ["key_1", "key_2", "key_3", "key_4"]

for test in test_names:
    test_instance = Test()
    for name in checkbutton_names:
        setattr(test_instance, name, tkinter.IntVar())
    setattr(instance, test, test_instance)

>>> instance.key_1.var_2
<Tkinter.IntVar instance at 0x018584B8>
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Dictionaries sehen bereits nach dem richtigen Weg aus. Aber um meine Frage von oben noch einmal auf Dictionaries anzuwenden: was genau meinst du mit "Namensraum"? Gibt es zu jedem Schlüssel eine vorher bekannte Anzahl an Variablen und ist die Anzahl der Variablen bei allen Schlüsseln gleich? Handelt es sich nur um Daten oder sind es eher Objekte?
Das Leben ist wie ein Tennisball.
hypnoticum
User
Beiträge: 132
Registriert: Dienstag 15. März 2011, 15:43

Ich kriegs nicht hin. Die Checkboxen werden einfach nicht gestzt, obwohl die Werte im "TestDict"-Dictionary stehen. Hier mal nur für einen Namen:
(Korrektur: Es funktioniert doch, aber seltsamerweise nur in diesem Minimalbeispiel. Ich werde mal sehen, wo der Fehler ist ...)

Code: Alles auswählen

import Tkinter

TestDict = {'eins': '1', 'zwei': '0'}
class TestClass(object): pass
MasterName = Tkinter.Tk()
       
i = 0
TestInstance = TestClass()
for Test in TestDict:
    setattr(TestInstance, Test, Tkinter.IntVar())
    TestInstance.__dict__[Test].set(int(TestDict[Test]))
    Tkinter.Checkbutton(MasterName, text = Test, variable = TestInstance.__dict__[Test]).grid(sticky = Tkinter.W, row = i, column = 0, columnspan = 1)
    i = i + 1
    
MasterName.mainloop()
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Ich habe dein vorheriges Script auch noch gesehen... Was zur Hölle tust du da? Nochmal und das gilt für beide Scripte - Finger weg von .__dict__

Vieleicht wäre es besser mal den Sinn dahinter zu erklären warum du solch eine Strucktur brauchst, dann könnte man vieleicht eine adäquat Lösung finden.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
BlackJack

@hypnoticum: Ich würde mich dem anschliessen: Finger weg von `__dict__` und versuch doch mal "pythonischer" zu schreiben. Das mit der Gross-/Kleinschreibung führt zum Beispiel IMHO immer wieder zu Verständnisproblemen -- wenn etwas mit einem Grossbuchstaben beginnt, dann ist das in den Köpfen von Pythonprogrammierern der Name einer Klasse und man muss immer umdenken wenn dem dann nicht so ist. Dann kann man sich auch Zusätze wie `Class` oder `Instance` sparen, denn dann ist klar das `Test` eine Klasse und `test` ein Exemplar ist.

`MasterName` ist irreführend, weil es kein Name ist.

Das manuelle hochzählen von `i` würde man eher durch `enumerate()` ersetzen und wenn Du in der Schleife immer auch den Wert zu einem Schlüssel benötigst, kannst Du auch gleich über die Schlüssel/Wert-Paare iterieren. So sieht das IMHO auch deutlich "aufgeräumter" aus:

Code: Alles auswählen

import Tkinter as tk

name2value = {'eins': '1', 'zwei': '0'}

class Test(object):
    pass

master = tk.Tk()

test = Test()
for i, (name, value) in enumerate(name2value.iteritems()):
    int_var = tk.IntVar()
    int_var.set(int(value))
    setattr(test, name, int_var)
    checkbutton = tk.Checkbutton(master, text=name, variable=int_var)
    checkbutton.grid(sticky=tk.W, row=i, column=0)

master.mainloop()
hypnoticum
User
Beiträge: 132
Registriert: Dienstag 15. März 2011, 15:43

OK, vielen Dank für die Hilfe.
Ich werde versuchen mich hinsichtlich meiner Schreibweise zu bessern.
Eine weitere Frage habe ich aber noch. Die gehört dann eher in die Ecke Tkinter-Probleme:

Wenn ich die Variable vom Typ IntVar() in einem Toplevel-Fenster erzeuge, dann muss ich das komischerweise immer als Atttribut des Master-Fensters machen ("self.master.test"), damit die CheckButtons auch gesetzt werden. Eigentlich geht es doch das Master-fenster nichts an, oder?

Code: Alles auswählen

from Tkinter import *

class TestGUI(object):
    def __init__(self):
        self.master = Tk()
        self.init_widgets()
        self.master.mainloop()
        
    def init_widgets (self):
        btn = Button(master = self.master,
              text = 'Configure',
              command = lambda: ConfigDia(self.master))
        btn.pack()

class ConfigDia(object): 
    def __init__(self, Master):
        self.master = Master
        self.top = Toplevel()
        self.init_widgets()
          
    def init_widgets(self):
        class Test(object): 
            pass
        
        testDict = {'eins': '1', 'zwei': '0'}
        self.master.test = Test()
        for i, (name, value) in enumerate(testDict.iteritems()):
            int_var = IntVar()
            int_var.set(int(value))
            setattr(self.master.test, name, int_var)
            checkbutton = Checkbutton(self.top, text=name, variable=int_var)
            checkbutton.grid(sticky=W, row=i, column=0)
            
newTestGUI = TestGUI()
Antworten