Lamda

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
Brevista
User
Beiträge: 32
Registriert: Sonntag 2. Juli 2017, 00:48

Hallo ich habe folgenden code für einen TR(Ich weiß es ist nicht effizient geschrieben)

Code: Alles auswählen

def berechnen(event):
    lel = eingabe.get()
    eingabe.delete(0, END)
    try:
        eingabe.insert(0, eval(lel))
    except:
        eingabe.insert(0, "ERROR")

top = Tk()

eingabe = Entry(top)
eingabe.grid(row=0, columnspan=4)



eins = Button(top,text="1")
eins.grid(row=1,column=0)
zwei = Button(top,text="2")
zwei.grid(row=1,column=1)
drei = Button(top,text="3")
drei.grid(row=1,column=2)
vier = Button(top,text="4")
vier.grid(row=2,column=0)
fünf = Button(top,text="5")
fünf.grid(row=2,column=1)
sechs = Button(top,text="6")
sechs.grid(row=2,column=2)
sieben = Button(top,text="7")
sieben.grid(row=3,column=0)
acht = Button(top,text="8")
acht.grid(row=3,column=1)
neun = Button(top,text="9")
neun.grid(row=3,column=2)
plus = Button(top,text="+")
plus.grid(row=1,column=4)
minus = Button(top,text="-")
minus.grid(row=2,column=4)
mal = Button(top,text="*")
mal.grid(row=3,column=4)
geteilt = Button(top,text="/")
geteilt.grid(row=4,column=4)
gleich = Button(top,text="=")
gleich.grid(row=4,column=0)
clean = Button(top,text="C")
clean.grid(row=4,column=2)
null = Button(top,text="0")
null.grid(row=4,column=1)

eins.bind("<Button-1>", lambda x: eingabe.insert(END, "1"))
zwei.bind("<Button-1>", lambda x: eingabe.insert(END, "2"))
drei.bind("<Button-1>", lambda x: eingabe.insert(END, "3"))
vier.bind("<Button-1>", lambda x: eingabe.insert(END, "4"))
fünf.bind("<Button-1>", lambda x: eingabe.insert(END, "5"))
sechs.bind("<Button-1>", lambda x: eingabe.insert(END, "6"))
sieben.bind("<Button-1>", lambda x: eingabe.insert(END, "7"))
acht.bind("<Button-1>", lambda x: eingabe.insert(END, "8"))
neun.bind("<Button-1>", lambda x: eingabe.insert(END, "9"))
plus.bind("<Button-1>", lambda x: eingabe.insert(END, "+"))
minus.bind("<Button-1>", lambda x: eingabe.insert(END, "-"))
mal.bind("<Button-1>", lambda x: eingabe.insert(END, "*"))
geteilt.bind("<Button-1>", lambda x: eingabe.insert(END, "/"))
null.bind("<Button-1>", lambda x: eingabe.insert(END, "0"))
clean.bind("<Button-1>", lambda x: eingabe.delete(0, END))
gleich.bind("<Button-1>", berechnen)




top.mainloop()
Meine Frage ist nun was dem x bei lambda entspricht

Code: Alles auswählen

eins.bind("<Button-1>", lambda x: eingabe.insert(END, "1"))
Wenn man bei lambda den parameter in der Rechnung nicht angibt wird ein Fehler Ausgegeben

Code: Alles auswählen

f = lambda x : 2 * 5 
print(f)
Zuletzt geändert von Anonymous am Montag 17. Juli 2017, 22:03, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@Brevista: Lass Dir das Argument doch einfach mal ausgeben. Und eventuell dessen Typ, den man mit der `type()`-Funktion ermitteln kann. Bei `berechnen()` hast Du diesem Argument ja auch einen beschreibenderen Namen als `x` gegeben. Und zu guter Letzt solltest Du in der/einer Dokumentation nachlesen was die `bind()`-Methode als Argumente erwartet und was dann beim Rückruf übergeben wird. Zum Beispiel auf den Seiten vom Effbot oder in der Referenz bei der New Mexico Tech. Sollten beide in der Dokumentation zum `tkinter`-Modul verlinkt sein.

Wobei `bind()` der falsche Weg ist. `Button` hat ein `command`-Argument für so etwas. Dann verhält sich die Schaltfläche auch wirklich so wie der Anwender das gewohnt ist.

Programmierer sind faul und wiederholen üblicherweise weder Code noch Daten. Bis auf beim 'C' wiederholen sich die Beschriftung und der immer gleiche ``lambda``-Ausdruck, der sich nur um den Wert der Beschriftung unterscheidet. Das ist nicht nur viel Tipparbeit sondern birgt auch die Gefahr von Fehlern bei der Ersteingabe oder später wenn man etwas ändern möchte. Das würde man eher über eine Datenstruktur lösen in der die Beschriftungen stecken und dann Code der darüber iteriert und die Schaltflächen daraus erzeugt.

`lel` ist kein sinnvolle Name für — ja wofür soll da überhaupt stehen? Namen sollen dem Leser die Bedeutung des Wertes dahinter vermitteln und keine Rätsel sein. :-)
BlackJack

@Brevista: Bei dem Code fehlt der Import und es sieht nach einem Sternchen-Import aus, was man vermeiden sollte.

Funktionen und Methoden sollten nur Werte (ausser Konstanten) verwenden, die als Argumente übergeben wurden. `berechnen()` dürfte `eingabe` also nicht einfach so ”magisch” aus der Umgebung kennen, sondern sollte es als Argument beim Aufruf übergeben bekommen.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Dann kann `berechnen()` auch gar nicht mehr auf `eingabe` zugreifen, denn das wäre lokal in einer anderen Funktion definiert. Zur Übergabe braucht man dann eine ``lambda``-Funktion, `functools.partial()`, oder objektorientierte Programmierung. Die man für GUI-Programmierung letztendlich sowieso beherrschen muss.

`eingabe` wird auch zur Ausgabe verwendet, was den Namen etwas unglücklich macht.

Insbesondere ein nacktes ``except`` ohne konkrete Ausnahme sollte so wenig wie möglich Code umfassen. Hier ist ja eigentlich nur `eval()` interessant, was man auch nicht verwenden sollte, da das gefährlich ist. In dem Taschenrechner kann der Benutzer beliebige Python-Ausdrücke eingeben, die von `eval()` ausgeführt werden. Auch so etwas wie ``__import__('os').remove('wichtigedatei')``.

Als Zwischenergebnis würde ich dann auf so etwas kommen:

Code: Alles auswählen

from functools import partial
import tkinter as tk


def berechnen(display):
    expression = display.get()
    try:
        result = eval(expression)
    except:
        result = 'ERROR'
    display.delete(0, tk.END)
    display.insert(0, result)


def main():
    top = tk.Tk()
    top.title('Taschenrechner')
     
    display = tk.Entry(top)
    display.grid(row=0, columnspan=4)
     
    pad_data = [
        ['1', '2', '3', '+'],
        ['4', '5', '6', '-'],
        ['7', '8', '9', '*'],
        [
            ('=', partial(berechnen, display)),
            '0',
            ('C', partial(display.delete, 0, tk.END)),
            '/',
        ],
    ]
    for row, row_data in enumerate(pad_data, 1):
        for column, button_data in enumerate(row_data):
            try:
                text, callback = button_data
            except ValueError:
                text = button_data
                callback = partial(display.insert, tk.END, text)
            tk.Button(
                top, text=text, command=callback
            ).grid(row=row, column=column, sticky=tk.NSEW)

    top.mainloop()


if __name__ == '__main__':
    main()
Brevista
User
Beiträge: 32
Registriert: Sonntag 2. Juli 2017, 00:48

Wieso das partial in Zeile 27?
Man hat doch eh nur einen Parameter und mit partial fügt man doch nur einen Parameter hinzu und kann den anderen wo anders hinzufügen
wieso geht nicht einfach nur berechne(=)

Eine weiter Frage noch, das enumerate(pad_data, 1) was führt das genau aus oder was macht es? und wieso die 1?

Und was genau ist row_data? Ich kenne nur row und column

und was ist callback und sticky?

zudem wollte ich dir noch danken @BlackJack, du Antwortest auf fast jede Frage die ich gestellt habe und ich finde es echt toll das sich jemand heute noch die Zeit nimmt und anderen Leuten einfach hilft ohne dafür Geld zu verlangen!!
BlackJack

@Brevista: Nur `berechne` geht nicht weil die `berechne()`-Funktion ein Argument erwartet das man übergeben muss. Und Tk weiss das ja nicht, das ruft das was man für `command` beim `Button` übergibt grundsätzlich ohne Argumente auf.

Für die Fragen zu `enumerate()`: Schau mal in die Dokumentation und füge ein `print()` ein das `row` und `row_data` in jedem Schleifendurchlauf ausgibt. Für `column` und `button_data` könntest Du das auch machen.

`callback` ist der Name an den die Rückruffunktion für das `command`-Argument von `Button` gebunden wird. Das ist entweder die Funktion die in `pad_data` bei dem Wert für einen Knopf angegeben wurde, oder eine Funktion die den Text für den Knopf der Einzeige hinzufügt.

Das `sticky`-Argument beim `grid()`-Aufruf wird in der Dokumentation bei effbot.org und bei der New Mexico Tech beschrieben.
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

@BlackJack: die Mischung der Datentypen in `pad_data` wäre mir zu magisch

Code: Alles auswählen

def main():
    top = tk.Tk()
    top.title('Taschenrechner')
     
    display = tk.Entry(top)
    display.grid(row=0, columnspan=4)
    callbacks = {
        '=': partial(berechnen, display),
        'C': partial(display.delete, 0, tk.END),
    }
    pad_data = [
        ['1', '2', '3', '+'],
        ['4', '5', '6', '-'],
        ['7', '8', '9', '*'],
        ['=', '0', 'C', '/'],
    ]
    for row, row_data in enumerate(pad_data, 1):
        for column, text in enumerate(row_data):
            try:
                callback = callbacks[text]
            except KeyError:
                callback = partial(display.insert, tk.END, text)
            tk.Button(
                top, text=text, command=callback
            ).grid(row=row, column=column, sticky=tk.NSEW)
    top.mainloop()
Antworten