Funktion... die bei jedem Aufruf etwas anderes macht

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
Benutzeravatar
madddiin
User
Beiträge: 12
Registriert: Donnerstag 15. Oktober 2020, 17:39

Hallo zusammen,

bestimmt gibt es zu meinem "Problem" eine Lösung. In Excel VBA habe ich dies immer mit einer globalen Variable gelöst. Dies soll ich ja in Python aber nicht machen.

Ich habe mir ein Programm geschrieben, dass mir für meine tägliche Arbeit Hilfestellung gibt. Um testen zu können, habe ich mir eine Funktion geschrieben, die mir aus einem Dictionary die Daten in die TKIntenr Felder schreibt. Es gibt 2 Optionen. Zur Zeit löste ich dies mit 2 Funktionen und 2 Buttons. Das ist aber nicht sehr elegant. Ich möchte dies mit einer Funktion machen und dachte mir, in der Funktion eine globale Variable auszuwerten. In meinem Python Programmierbuch (Rheinwerk) steht aber, man solle keine globalen Variablen dazu nehmen.

In Excel VBA gab es hierzu Variablen, die ihren Wert nicht verlieren nachdem die Funktion verlassen wurde. Hier im Forum wurde geschrieben, dies mit einer Klasse zu machen. Ich habe noch Probleme damit, OOP richtig verstanden zu haben. Ich weiß, wozu es da ist. Nur es sitzt noch nicht im Blut.

Ist bitte jemand so nett mir dies zu zeigen... verständlich. Was muss ich machen, um abhängig vom Zustand eines Funktionaufrufes Entscheidungen zu treffen. Bei mir wären es 2. Und dies immer im Wechsel.

Ich hoffe, dass dies keine zu blöde Frage war.

Danke
Weg von Excel VBA... hin zu Python.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Auch in VBA sollst du keine globalen Variablen benutzen. Mit Python hat das nichts zu tun, die sind in jeder Sprache schlecht. Denn der Grund dafuer, dass man den Ueberblick ueber die Einflussfaktoren auf seinen Code verliert, ist ueberall gleich.

Wenn ich dich richtig verstanden habe, dann ist das hier eine Loesung fuer dein Problem. Ohne OOP geht's aber nicht wirklich, wie zu sehen. Und das braucht man fuer GUIs eh.

Code: Alles auswählen

import tkinter as tk


class App:

    def __init__(self, root):
        self._state = True
        button = tk.Button(root, text="Drueck mich!", command=self._toggle)
        button.pack()

    def _toggle(self):
        self._state = not self._state
        print("toggle", self._state)


def main():
    root = tk.Tk()
    app = App(root)
    root.mainloop()


if __name__ == '__main__':
    main()
Benutzeravatar
madddiin
User
Beiträge: 12
Registriert: Donnerstag 15. Oktober 2020, 17:39

__deets__ hat geschrieben: Montag 11. April 2022, 09:46 Auch in VBA sollst du keine globalen Variablen benutzen. Mit Python hat das nichts zu tun, die sind in jeder Sprache schlecht. Denn der Grund dafuer, dass man den Ueberblick ueber die Einflussfaktoren auf seinen Code verliert, ist ueberall gleich.

Wenn ich dich richtig verstanden habe, dann ist das hier eine Loesung fuer dein Problem. Ohne OOP geht's aber nicht wirklich, wie zu sehen. Und das braucht man fuer GUIs eh.

Vielen Dank... das hilft mir sehr weiter.

Ich habe ja schon mit Klassen gearbeitet. Es ist nur noch nicht ins Fleisch übergegangen. Mache alles ohne OOP... weil die Programme doch dann so klein sind. Werde das alles mal sacken lassen und mich Richtung OOP bewegen. Ist eh überfällig.

Gruß

Martin
Weg von Excel VBA... hin zu Python.
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

__deets__ hat geschrieben: Montag 11. April 2022, 09:46 Wenn ich dich richtig verstanden habe, dann ist das hier eine Loesung fuer dein Problem. Ohne OOP geht's aber nicht wirklich, wie zu sehen. Und das braucht man fuer GUIs eh.

Code: Alles auswählen

[...]

class App:

    def __init__(self, root):
        self._state = True
        button = tk.Button(root, text="Drueck mich!", command=self._toggle)
        button.pack()

    def _toggle(self): #[...]

def main():
    root = tk.Tk()
    app = App(root)
    root.mainloop()
[...]
Aberaberaber... also wenn wir ohnehin OOP benutzen, wäre es dann nicht elegant, das durchgängig zu machen? NB: unter meinem Linux kann Tkinter auch Umlaute.

Code: Alles auswählen

#!/usr/bin/env python
import tkinter as tk


class App(tk.Tk):

    def __init__(self):
        super().__init__()
        self._state = True
        tk.Button(self, text="Drück mich!", command=self._toggle).pack()

    def _toggle(self):
        self._state = not self._state
        print("toggle", self._state)


def main():
    App().mainloop()


if __name__ == '__main__':
    main()
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich halte wenig von Vererbung aus Prinzip. Da du deiner App-Klasse keinerlei Ueberladung der Tk-Klasse angedeihen laesst - die das auch nicht wirklich vorsieht, mit zB abstrakten Methoden - ist das in meinen Augen ueberfluessig, oder sogar gefaehrlich. Weisst du auf Anhieb, welche Methoden die hat, und ob du die nicht aus versehen ueberschreibst?

Aber Eleganz ist halt so ein Ding. Ich finde das unelegant. Du nicht. Und nun? Belegbar ist nur: es ist nicht *notwendig* fuer die Funktionalitaet, anders als bei explizit dazu gedachten Klassen, wie zB einem klassischen Visitor-Pattern in statisch typisierten Sprachen. Darum sehe ich da keinen Mehrwert.

Umlaute hat etwas mit meinem englischen DVORAK-Keyboard und dem Aufwand, da ein composite-character zu erzeugen zu tun.
imonbln
User
Beiträge: 149
Registriert: Freitag 3. Dezember 2021, 17:07

LukeNukem hat geschrieben: Mittwoch 13. April 2022, 12:19
Aberaberaber... also wenn wir ohnehin OOP benutzen, wäre es dann nicht elegant, das durchgängig zu machen? NB: unter meinem Linux kann Tkinter auch Umlaute.
Das kommt wie immer darauf an! Wie sieht das Realworld Problem hinter den Code Snippet aus.

Generell denke ich das eine Komposition besser ist als eine Vererbung ist. So hat der Vorschlag von __deets__ den Vorteil, dass man die Klasse App in verschieden Projekten recyclen kann, außerdem kann, wenn gewünscht einfach ein Mock zum Testen übergeben werden. Bei Deinem Vorschlag ist beides auch möglich, aber leider nicht so elegant.

Aber wie gesagt, das ist Design, darüber kann man streiten und ob die Komposition tatsächlich, ihre Vorteile spielen kann, hängt von hier unbekannten Faktoren ab.
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

imonbln hat geschrieben: Mittwoch 13. April 2022, 13:26 Das kommt wie immer darauf an! Wie sieht das Realworld Problem hinter den Code Snippet aus.
Klar. Die wenigsten Leute werden eine GUI benutzen, um eine Boolsche Variable hin und her zu schalten. ;-)
imonbln hat geschrieben: Mittwoch 13. April 2022, 13:26 Generell denke ich das eine Komposition besser ist als eine Vererbung ist. So hat der Vorschlag von __deets__ den Vorteil, dass man die Klasse App in verschieden Projekten recyclen kann, außerdem kann, wenn gewünscht einfach ein Mock zum Testen übergeben werden. Bei Deinem Vorschlag ist beides auch möglich, aber leider nicht so elegant.
Was Vererbung versus Komposition angeht... *hüstel* Seit der Zeit, als die Objektorientierung als Buzzword von den Marketing-Leuten entdeckt wurde, ist die Vererbung leider ein bisschen in Verruf geraten, aber ich glaube, sie ist besser als ihr Ruf. Insofern stelle ich mir bei der Entscheidung immer die Frage: "ist es ein <Dings> oder hat es eines"? In meinem Beispielcode ist "App" ein Hauptfenster, daher halte ich Vererbung hier für sinnvoll, auch und gerade vor dem Hintergrund des PLOA (Principle Of Least Astonishment) -- insbesondere für Leute, die auch andere GUI-Toolkits wie WxWidgets oder Qt kennen. Und zuletzt ist es auch mein Eindruck, daß damit das objektorientierte Prinzip der Kapselung besser realisiert wird...

Natürlich kann man die Komponenten auch in diesem Fall wiederverwendbar machen:

Code: Alles auswählen

#!/usr/bin/env python
import tkinter as tk


class App(tk.Frame):
    def __init__(self, master, *args, **kwargs):
        self.master = master
        super().__init__(self.master, *args, **kwargs)
        self._state = True
        tk.Button(self, text="Drück mich!", command=self.toggle).pack()

    def toggle(self, *args, **kwargs):
        self._state = not self._state
        print("toggle", self._state)


class Menubar(tk.Menu):
    def __init__(self, master, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.master = master
        self.filemenu = tk.Menu(self.master, tearoff=False)
        self.filemenu.add_command(command=self.master.app.toggle, label='Drück mich')
        self.filemenu.add_command(command=self.master.destroy, label='Beenden')
        self.add_cascade(label='Datei', menu=self.filemenu)


class MainWin(tk.Tk):
    def __init__(self):
        super().__init__()
        self.app = App(self)
        self.app.pack(side=tk.TOP)
        self.config(menu=Menubar(self))


def main():
    MainWin().mainloop()


if __name__ == '__main__':
    main()
Dieser Code verwendet beides: Vererbung (für die einzelnen Widgets, die ein tk.Tk-Hauptfenster, ein tk.Frame-Widget und eine tk.Menubar-Menüleisten sind), und Komposition: das tk.Tk-Hauptfenster hat eine Menüleiste und ein App-Framewidget, und das tk.Frame-Widget hat einen tk.Button. Dieser Code ist ansonsten schon relativ nahe an dem, was ich produktiv machen würde -- wobei ich in den letzten Jahren aber zunehmend von klassischen GUI abkomme und mich stattdessen vermehrt den Webapplikationen zuwende.

Insgesamt weiß ich natürlich, daß das, was deets zeigt, der in den meisten Tkinter-Tutorials gezeigte Weg ist und man super() in früheren Versionen von Tkinter unter Python2 nicht benutzen konnte, weil die Klassen noch old style waren (also nicht explizit von object() erbten). Trotzdem rollen sich mir immer die Fußnägel auf, wenn ich es sehe. Ich kenne kaum einen Bereich in der Softwareentwicklung, bei dem sich Objektorientierung und auch Vererbung so "natürlich" anfühlen wie in der GUI-Entwicklung -- sogar das in C geschriebene GTK verwendet ein eigenes System zur objektorientierten Programmierung. Zudem fühlen sich die Variablen in dem "root=tk.Tk()"-Stil für mich trotz "main()" und "if __name__ == '__main__'" immer noch irgendwie... wie globale Variablen an. (Ja, ich weiß...)

Insofern habt Ihr beide Recht: es ist und bleibt im Wesentlichen eine Geschmacksfrage...
imonbln hat geschrieben: Mittwoch 13. April 2022, 13:26 Aber wie gesagt, das ist Design, darüber kann man streiten und ob die Komposition tatsächlich, ihre Vorteile spielen kann, hängt von hier unbekannten Faktoren ab.
Hm, ich weiß nicht... es ist und bleibt natürlich eine Designfrage, und im Endeffekt ist das eine Design so gut oder schlecht wie das andere. Technische oder sogar funktionale Gründe dafür, sich für das Eine oder Andere zu entscheiden, sehe ich da jetzt eher keine.
Antworten