@Onomatopoesie: Du hast in Deinem Beispiel übrigens den Wert `None` an die Namen die mit `button_` beginnen gebunden. `pack()` gibt nicht das `Button`-Objekt zurück, sondern ”Nichts”.
Man kann das simple Beispiel mit Tk noch ohne eigene Klasse lösen weil Tk Objekte bietet die genau einen Wert kapseln (und netterweise auch das „observable“-Entwurfsmuster implementieren was an anderer Stelle (`Label`) auch gleich genutzt wird und man sich die Funktion zur Anzeige des Zählerstands sparen kann):
Code: Alles auswählen
#!/usr/bin/env python3
import tkinter as tk
from functools import partial
def button1_funktion(klickzaehler_var):
klickzaehler_var.set(klickzaehler_var.get() + 1)
#
# Die zweite Funktion tut das Gleiche wie bei button1 - es geht mir hier nur um
# das Prinzip, ich weiß, dass es so nicht sinnvoll ist
#
def button2_funktion(klickzaehler_var):
klickzaehler_var.set(klickzaehler_var.get() + 1)
def main():
root = tk.Tk()
klickzaehler_var = tk.IntVar()
for nummer, aktion in enumerate([button1_funktion, button2_funktion], 1):
tk.Button(
root,
text=f"Button {nummer}",
command=partial(aktion, klickzaehler_var),
).pack()
frame = tk.Frame(root)
tk.Label(frame, text="Es wurden ").pack(side=tk.LEFT)
tk.Label(frame, textvariable=klickzaehler_var).pack(side=tk.LEFT)
tk.Label(frame, text=" Klicks auf verschiedene Buttons getätigt.").pack(
side=tk.LEFT
)
frame.pack()
root.mainloop()
if __name__ == "__main__":
main()
Eine saubere funktionale Lösung ohne globale Variablen würde man in anderen Sprache mit Closures machen. Das geht in Python noch gar nicht so lange ohne komische Notlösungen sondern erst seit es ``nonlocal`` gibt:
Code: Alles auswählen
#!/usr/bin/env python3
import tkinter as tk
from functools import partial
def erstelle_zaehler():
stand = 0
def hole():
nonlocal stand
return stand
def erhoehe():
nonlocal stand
stand += 1
return hole, erhoehe
def button1_funktion(label, hole_zaehlerstand, erhoehe_zaehlerstand):
erhoehe_zaehlerstand()
label["text"] = hole_zaehlerstand()
#
# Die zweite Funktion tut das Gleiche wie bei button1 - es geht mir hier nur um
# das Prinzip, ich weiß, dass es so nicht sinnvoll ist
#
def button2_funktion(label, hole_zaehlerstand, erhoehe_zaehlerstand):
erhoehe_zaehlerstand()
label["text"] = hole_zaehlerstand()
def main():
root = tk.Tk()
hole_zaehlerstand, erhoehe_zaehlerstand = erstelle_zaehler()
frame = tk.Frame(root)
tk.Label(frame, text="Es wurden ").pack(side=tk.LEFT)
zaehlerstand_label = tk.Label(frame, text=hole_zaehlerstand())
zaehlerstand_label.pack(side=tk.LEFT)
tk.Label(frame, text=" Klicks auf verschiedene Buttons getätigt.").pack(
side=tk.LEFT
)
for nummer, aktion in enumerate([button1_funktion, button2_funktion], 1):
tk.Button(
root,
text=f"Button {nummer}",
command=partial(
aktion,
zaehlerstand_label,
hole_zaehlerstand,
erhoehe_zaehlerstand,
),
).pack()
frame.pack()
root.mainloop()
if __name__ == "__main__":
main()
Der Zähler hat nur zwei Funktionen die ihn beeinflussen, aber auch da ist es schon ein bisschen nervig, dass man zwei Werte für den einen Zähler an die Button-Funktionen übergeben muss. Die meisten funktionalen Programmiersprachen haben auch einen Verbunddatentyp wo man einzelne Werte zusammenfassen kann und den Bestandteilen Namen geben kann. Also so etwas wie `collections.namedtuple()`:
Code: Alles auswählen
#!/usr/bin/env python3
import tkinter as tk
from collections import namedtuple
from functools import partial
Zaehler = namedtuple("Zaehler", "hole erhoehe")
def erstelle_zaehler():
stand = 0
def hole():
nonlocal stand
return stand
def erhoehe():
nonlocal stand
stand += 1
return Zaehler(hole, erhoehe)
def button1_funktion(label, zaehler):
zaehler.erhoehe()
label["text"] = zaehler.hole()
#
# Die zweite Funktion tut das Gleiche wie bei button1 - es geht mir hier nur um
# das Prinzip, ich weiß, dass es so nicht sinnvoll ist
#
def button2_funktion(label, zaehler):
zaehler.erhoehe()
label["text"] = zaehler.hole()
def main():
root = tk.Tk()
zaehler = erstelle_zaehler()
frame = tk.Frame(root)
tk.Label(frame, text="Es wurden ").pack(side=tk.LEFT)
zaehlerstand_label = tk.Label(frame, text=zaehler.hole())
zaehlerstand_label.pack(side=tk.LEFT)
tk.Label(frame, text=" Klicks auf verschiedene Buttons getätigt.").pack(
side=tk.LEFT
)
for nummer, aktion in enumerate([button1_funktion, button2_funktion], 1):
tk.Button(
root,
text=f"Button {nummer}",
command=partial(aktion, zaehlerstand_label, zaehler),
).pack()
frame.pack()
root.mainloop()
if __name__ == "__main__":
main()
Nur würde man das in Python so nicht schreiben, weil das für Python-Verhältnisse eine sehr ungewöhnliche Art wäre etwas zu schreiben was man mit einer Klasse löst:
Code: Alles auswählen
#!/usr/bin/env python3
import tkinter as tk
from functools import partial
class Zaehler:
def __init__(self):
self.stand = 0
def erhoehe(self):
self.stand += 1
def button1_funktion(label, zaehler):
zaehler.erhoehe()
label["text"] = zaehler.stand
#
# Die zweite Funktion tut das Gleiche wie bei button1 - es geht mir hier nur um
# das Prinzip, ich weiß, dass es so nicht sinnvoll ist
#
def button2_funktion(label, zaehler):
zaehler.erhoehe()
label["text"] = zaehler.stand
def main():
root = tk.Tk()
zaehler = Zaehler()
frame = tk.Frame(root)
tk.Label(frame, text="Es wurden ").pack(side=tk.LEFT)
zaehlerstand_label = tk.Label(frame, text=zaehler.stand)
zaehlerstand_label.pack(side=tk.LEFT)
tk.Label(frame, text=" Klicks auf verschiedene Buttons getätigt.").pack(
side=tk.LEFT
)
for nummer, aktion in enumerate([button1_funktion, button2_funktion], 1):
tk.Button(
root,
text=f"Button {nummer}",
command=partial(aktion, zaehlerstand_label, zaehler),
).pack()
frame.pack()
root.mainloop()
if __name__ == "__main__":
main()
Wenn man mehr als einen Wert hat, der im Closure gekapselt wird, sieht man wie unhandlich das werden kann, weil man in jeder ”Methode” alle benutzen ”Attribute” als ``nonlocal`` deklarieren muss. Also wenn man beispielweise das Label für die Anzeige des Zählerstandes mit in den Zähler integriert:
Code: Alles auswählen
#!/usr/bin/env python3
import tkinter as tk
from collections import namedtuple
from functools import partial
Zaehler = namedtuple("Zaehler", "label hole erhoehe")
def erstelle_zaehler(master):
stand = 0
label = tk.Label(master, text=stand)
def hole():
nonlocal stand
return stand
def erhoehe():
nonlocal stand, label
stand += 1
label["text"] = stand
return Zaehler(label, hole, erhoehe)
def button1_funktion(zaehler):
zaehler.erhoehe()
#
# Die zweite Funktion tut das Gleiche wie bei button1 - es geht mir hier nur um
# das Prinzip, ich weiß, dass es so nicht sinnvoll ist
#
def button2_funktion(zaehler):
zaehler.erhoehe()
def main():
root = tk.Tk()
frame = tk.Frame(root)
tk.Label(frame, text="Es wurden ").pack(side=tk.LEFT)
zaehler = erstelle_zaehler(frame)
zaehler.label.pack(side=tk.LEFT)
tk.Label(frame, text=" Klicks auf verschiedene Buttons getätigt.").pack(
side=tk.LEFT
)
for nummer, aktion in enumerate([button1_funktion, button2_funktion], 1):
tk.Button(
root, text=f"Button {nummer}", command=partial(aktion, zaehler)
).pack()
frame.pack()
root.mainloop()
if __name__ == "__main__":
main()