Seite 1 von 2
Zugreifen auf Variablen innerhalb einer Funktion.
Verfasst: Dienstag 11. Februar 2020, 11:51
von Spedex
Hey,
folgender Code-Ausschnitt:
Code: Alles auswählen
balance_cash = 99999
def add_money():
balance_cash += 100
add_money()
Ich habe also die Integer Variable
balance_cash. Mit der Funktion
add_money() möchte ich einen Wert von 100 zur Variable
balance_cash hinzuzählen.
Dabei entsteht jedoch folgende Fehlermeldung:
Code: Alles auswählen
UnboundLocalError: local variable 'balance_cash' referenced before assignment
Auch folgendes funktioniert nicht:
Code: Alles auswählen
balance_cash = 99999
def add_money():
balance_cash += 100
return balance_cash
balance_cash = add_money()
Im Internet habe ich folgenden Beispielcode gefunden:
Dieser funktioniert einwandfrei. Dementsprechend gehe ich davon aus, dass man innerhalb der Funktion zwar Variablen auslesen kann, allerdings nicht "bearbeiten". Lässt sich mein Vorhaben trotzdem umsetzten?
LG Spedex
Re: Zugreifen auf Variablen innerhalb einer Funktion.
Verfasst: Dienstag 11. Februar 2020, 11:59
von Sirius3
Du kannst keine globalen Variablen in einer Funktion ändern. Globale Variablen sollten eh nicht verwendet werden. Alles was eine Funktion braucht, bekommt sie über ihre Argumente und gibt das Ergebnis per return zurück:
Code: Alles auswählen
def add_money(balance):
balance += 100
return balance
balance_cash = 99999
balance_cash = add_money(balance_cash)
Re: Zugreifen auf Variablen innerhalb einer Funktion.
Verfasst: Dienstag 11. Februar 2020, 12:04
von sparrow
Das was du versuchst, tut man nicht.
Funktionen erhalten alles, was sie benötigen, als Parameter und geben das Resultat mit return zurück.
Aus einer Funktion heraus auf eine Variable zuzugreifen, die sich in einem anderen Namensraum befindet, ist unsauber, nicht nachvollziehbar und kann zu seltsamen Effekten führen. Gewöhne es dir erst gar nicht an.
Code: Alles auswählen
def add_money(stock, amount):
return stock + amount
balance_cash = 99999
balance_cash = add_money(balance_cash, 100)
Re: Zugreifen auf Variablen innerhalb einer Funktion.
Verfasst: Dienstag 11. Februar 2020, 12:17
von Spedex
Ok. Vielen Dank
Re: Zugreifen auf Variablen innerhalb einer Funktion.
Verfasst: Dienstag 11. Februar 2020, 12:32
von Spedex
Soweit so gut, allerdings ist mir gerade aufgefallen, dass das mit Tkinter nicht so einfach ist.
Deswegen sind jetzt wieder die Tkinter Profis gefragt.
Denn das Problem liegt dabei, dass die Funktion nicht einfach so aufgerufen wird:
Code: Alles auswählen
def add_cash(balance, menge):
balance += menge
return balance
add_cash(balance_cash, 100)
Die Funktion wird nämlich mit einem Button aufgerufen.
Das hat zum Beispiel folgende Form:
Code: Alles auswählen
button_add_cash.configure(command=lambda : add_cash(balance_cash, 100))
Dementsprechend die Frage: Lässt sich hier auch irgendwie der return-Value einer Variable zuweisen?
Re: Zugreifen auf Variablen innerhalb einer Funktion.
Verfasst: Dienstag 11. Februar 2020, 12:43
von __blackjack__
@Spedex: Jede nicht-triviale GUI-Anwendung benötigt objektorientierte Programmierung. Der Kontostand ist Zustand eines Konto-Objekts, wenn man GUI und Programmlogik sauber trennt. Wenn man es nicht sauber trennt, würde sich ein `tkinter.IntVar`- oder `tkinter.DoubleVar`-Objekt anbieten.
Statt eines ``lambda``-Ausdrucks hätte ich hier übrigens `functools.partial()` verwendet.
Re: Zugreifen auf Variablen innerhalb einer Funktion.
Verfasst: Dienstag 11. Februar 2020, 14:03
von Spedex
Aha ok. Ich arbeite also mit
var.set() und
var.get(). Aber wie ich das jetzt nutzen soll, um den return-Value einer Variable zuzuweisen, verstehe ich leider nicht. Habe mir schon folgendes durchgelesen:
https://effbot.org/tkinterbook/variable.htm sowie
https://stackoverflow.com/questions/130 ... in-command und
viewtopic.php?t=30046, aber das hilft mir ehrlich gesagt nicht weiter.
Führe ich also folgendes mit meiner Variable
balance_cash durch?:
Wofür soll das gut sein?
Re: Zugreifen auf Variablen innerhalb einer Funktion.
Verfasst: Dienstag 11. Februar 2020, 14:20
von __blackjack__
@Spedex: Du hast da jetzt ein Objekt das als Zustand eine ganze Zahl hat und Du kannst den Zustand auslesen (`get()`) und auf einen anderen Wert setzen (`set()`). Und das geht von überall her wenn Du auf das Objekt zugriff hast. Also im Gegensatz zum binden des Namens `balance_cash` irgendwo lokal in einer Funktion oder Methode.
Code: Alles auswählen
def main():
...
cash_balance_var = tk.IntVar(value=99_999)
...
add_cash_button["command"] = lambda: cash_balance_var.set(
cash_balance_var.get() + 100
)
...
Re: Zugreifen auf Variablen innerhalb einer Funktion.
Verfasst: Dienstag 11. Februar 2020, 14:21
von __deets__
Du solltest dich mit Objektorientierung auseinandersetzen. Eine Skizze:
Code: Alles auswählen
class MeineAnwendung:
__init__(self, ...):
self._balance = tk.IntVar()
self._label = tk.Label(..., textvariable=self._balance)
self._button = tk.Button(..., command=self._update_balance
def update_balance(self):
self._balance.set(self._balance.get() + 100)
Re: Zugreifen auf Variablen innerhalb einer Funktion.
Verfasst: Dienstag 11. Februar 2020, 15:31
von Spedex
Ich muss sagen, mit OOP hatte ich bis jetzt noch nichts am Hut und hatte eingentlich auch vor, dass das so bleibt. Aber anscheinend komm ich nicht drum herum.
Bei mir schaut das jetzt wie folgt aus (Auschnitt):
Code: Alles auswählen
balance_cash = IntVar(value=9)
label_atm_cash_show = Label(frame_atm, text=f"{str(balance_cash)}€", font="arial 25", bg="white")
label_atm_cash_show.place(x=620, y=203) #place ist schlecht, ich weiß es
button_add_cash.configure(command=lambda : balance_cash.set(balance_cash.get() + 100))
Das sind natürlich nur Ausschnitte.
Wenn ich jetzt jedoch die Variable
balance_cash anschaue, schaut die wie folgt aus:
PY_VAR0 bzw. eigentlich:
PY_VAR0€
Auch nachdem ich den +100 Button gedrückt habe, ändert sich nichts an
balance_cash. Das bleibt weiterhin
PY_VAR0. Ich kann jedoch nicht sagen, ober der Button nicht funktioniert oder ob es an was anderem liegt.
Re: Zugreifen auf Variablen innerhalb einer Funktion.
Verfasst: Dienstag 11. Februar 2020, 15:34
von sparrow
Wie schaust du denn die Variable an?
Re: Zugreifen auf Variablen innerhalb einer Funktion.
Verfasst: Dienstag 11. Februar 2020, 15:40
von Spedex
Also ich hab sie test-weise mit print() ausgeben lassen. Aber ich hab sie auch so angeschaut, wie sie normalerweise angeschaut wird. Im Hauptfenster ist ein Button "ATM", welcher einen Frame öffnet (mit öffnen meine ich tkraise()), dort steht dann als Label die Variable. Im Hauptfenster ist ebenfalls der Button "Add Cash", welcher zum aktuellen Kontostand 100€ hinzufügt. Wenn ich den ATM-Frame öffne, wird das Label, auf dem die "balance_cash" Variable angezeigt wird, aktualisiert. Ansonsten würde dieses, selbst wenn "balance_cash" einen neuen Wert hätte, immer nur den Wert zeigen, der beim erstmaligen erstellen aller Frames vorhanden war.
Re: Zugreifen auf Variablen innerhalb einer Funktion.
Verfasst: Dienstag 11. Februar 2020, 15:44
von sparrow
Ein paar Zeichen Code sagen mehr als 1000 Worte.
__blackjack__ hat doch oben geschrieben, wie man den Wert _setzt_ und wie man den Wert _holt_. Für beides musst du eine Funktion auf dem Objekt aufrufen. set für das Setzen, get für das Holen.
Das steht auch alles in der
Dokumentation.
Und wenn du dich grundsätzlich mit Sachen nicht auseinandersetzen willst - zum Beispiel Dokumentation, Objektorientierung oder wie man vernünftig eine Oberfläche baust - dann wirst du kurzfristig ein Problem mit deinen Hausaufgaben haben.
Re: Zugreifen auf Variablen innerhalb einer Funktion.
Verfasst: Dienstag 11. Februar 2020, 16:22
von __blackjack__
@Spedex: Der Wert von `balance_cash` ist das `IntVar`-Objekt. Als Zeichenkette umgewandelt bekommt man immer den Variablennamen unter dem das im Tcl-Interpreter zur Verfügung steht. Und der Name ändert sich nicht. `tkinter` scheint da einfach Namen nach dem Muster PY_VAR<laufende nummer> zu generieren. Das ist aber letztlich nur ein Implementierungsdetail. Du willst nicht das `IntVar`-Objekt in dem Label darstellen sondern den Wert der *in* dem Objekt gespeichert ist. Wofür man den Wert dort heraus holen muss. Mit `get()`. Und wenn man das so macht, dann muss man das auch jedes mal machen wenn man den Wert aktualisiert. Automatisch geht das nur wenn man das `IntVar`-Objekt als `textvariable` an das `Label` übergibt. Dann ist das aber nur der Wert und kann keine zusätzlichen Prä- oder Suffixe enthalten. Wenn da ein € hinter stehen soll, muss man das also entweder in ein eigenes Label stecken das rechts von dem Label mit dem Zahlwert angezeigt wird, oder man muss Code schreiben der jedes mal das Label aktualisiert und den aktuellen Zahlwert + € in das Label setzt.
Re: Zugreifen auf Variablen innerhalb einer Funktion.
Verfasst: Dienstag 11. Februar 2020, 18:29
von Spedex
Ok, das hat soweit funktioniert. Vielen Dank vorerst.
Es ist nur eine Frage der Zeit, bis beim "command-Teil" des Buttons nicht eine direkte Anweisung steht (so wie eben), sondern wirklich eine Funktion. Hier zum Beispiel:
Code: Alles auswählen
def transf_geld(konto1, konto2, menge):
konto1 -= menge
konto2 += menge
button_atm_abheben_200.configure(command=lambda : transf_geld(balance_bank.get(), balance_cash.get(), 200))
Man bemerke. Es fehlt einerseits das return-Statement, andererseits die
set Methoden.
Wenn ich die Methode auf einen return-Parameter beschränken würde, würde mit folgende Lösung einfallen:
Code: Alles auswählen
def transf_geld(konto2, menge):
konto2 += menge
return konto2
button_atm_abheben_200.configure(command=lambda : balance_cash.set(transf_geld(balance_cash.get(), 200)))
Das funktioniert.
Es geht also darum, dass mehr als ein Wert zurückgegeben werden muss, und diese zurückgegebenen Werte dann mit set() gleich zwei verschieden Variablen zugeordnet werden müssen.
Ich hab gelesen, dass wenn mehr als 1 Wert mit return zurückgegeben wird, das ganze als "Tuple" zurückgegeben wird. Aber wie kann ich die Einträge des Tuples dann den Variablen zuordnen.
Klar kann ich mir vorstellen, dass sowas in der Art funktioniert:
oder
Allerdings muss die Zuordnung ja innerhalb einer Zeile erfolgen, oder nicht?
Re: Zugreifen auf Variablen innerhalb einer Funktion.
Verfasst: Dienstag 11. Februar 2020, 18:49
von __deets__
Und was hindert dich daran, KEIN lambda zu nehmen, so das du in aller Ruhe mehrere Statements hintereinander absetzen kannst, statt alles in genau einen Ausdruck falten zu muessen?
Re: Zugreifen auf Variablen innerhalb einer Funktion.
Verfasst: Mittwoch 12. Februar 2020, 09:30
von Spedex
Ich verstehe leider nicht ganz,was du meinst. Also soweit ich das weiß, nimmt man
lambda her, um einer Funktion auch Parameter bzw. Argumente mitgeben zu können. Ohne Lambda würde das innerhalb der
command-Funktion im Button zum Beispiel so aussehen.
Code: Alles auswählen
def testfunction():
pass
button_test.configure(command=testfunction)
Man kann also keine Parameter mitgeben.
Mit lambda würde das ganze dann zum Beispiel so aussehen:
Code: Alles auswählen
def testfunction(var1):
var1 += 10
return var1
button_test.configure(command=lambda : testfunction(var_xy))
Deswegen bin ich davon ausgegangen, dass ich lambda benötige. Aber selbst wenn ich es nicht benötigen würde, weiß ich nicht was du mit "mehrere Statements hintereinander absetzen kannst" meinst?
Re: Zugreifen auf Variablen innerhalb einer Funktion.
Verfasst: Mittwoch 12. Februar 2020, 10:07
von Sirius3
Um fixe Parameter mit zu geben, benutzt man functools.partial, wie das __blackjack__ schon in seinem ersten Beitrag geschrieben hat.
` lambda` benutzt man um EINEN Ausdruck ohne Funktionsdefinition benutzen zu können. Du hast aber nicht EINEN, sondern mehrere.
Re: Zugreifen auf Variablen innerhalb einer Funktion.
Verfasst: Mittwoch 12. Februar 2020, 11:03
von Spedex
Also mit
partial würde bei mir das jetzt so aussehen:
Code: Alles auswählen
def transf_geld(konto1, konto2, menge):
konto1 -= menge
konto2 += menge
# label_atm_cash_show_abh.configure(text=f"{str(balance_cash.get())}€")
# label_atm_bank_show_abh.configure(text=f"{str(balance_bank.get())}€")
return konto1, konto2
button_atm_abheben_200.configure(command=partial(transf_geld, balance_bank.get(), balance_cash.get(), 200))
Ich hab im Internet zwar einiges über
partial gefunden, allerdings nirgendswo ein Beispiel wo etwas mit
return zurückgegeben wird. Dementsprechend bin ich mir immer noch unsicher, wie ich jetzt die return-Werte den Variablen zuordnen soll.
Ich habe ja schätzungsweise einerseits
balance_bank.set() und andererseits
balance_cash.set(). Innerhalb der set-Klammern kommt denke ich mal die Funktion, die etwas zurückgibt mit
return. Aber ich hab zwei set-Klammern und nur eine Funktion, die zwei Werte auf einmal zurückgibt. Wie soll ich das verwalten?
Re: Zugreifen auf Variablen innerhalb einer Funktion.
Verfasst: Mittwoch 12. Februar 2020, 11:49
von __deets__
Das du das .get() schon wieder in das partial reinziehst ist falsch. Du willst die Variablen doch danach als Objekt zur Verfuegung haben, nicht nur deren Werte. Und dann ist da auch kein return mehr, sondern einfach das setzen der neuen Werte.