callback Probleme beim Zugriff

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
Pf@nne
User
Beiträge: 43
Registriert: Donnerstag 18. April 2013, 16:50

Moin,

scheinbar habe ich hier Murx programmiert.
Aber wie würde man es richtig machen?

Wie kann ich aus dem Callback heraus functions sowohl in c1 als auch c2 aufrufen?
In dem Beispiel passiert beim drücken von "1" mal garnix, nicht mal ein Fehler.
Beim Drücken von "2" gibt es einen Fehler "name 'c2' is not defined".

Vielleicht erbarmt sich ja jemand einem Anfänger python zu erklären......

Code: Alles auswählen

import threading

class Class1():
    def print_class1():
        print("--------")
        print("class1")
        print("--------")

class Class2():
    def __init__(self, callback):
        self.callback = callback
        thread = threading.Thread(target=self.Wait_for_key)
        thread.start()
        thread.join()
        
    def print_class2():
        print("--------")
        print("class2")
        print("--------")
        
    def Wait_for_key(self):
        while 1:
            print("1 = class 1")
            print("2 = class 2")
            print("q = quit")
            key = input()
            if key == "1":
                self.callback(key)
            if key == "2":
                self.callback(key)
            if key == 'q':
                print("bye")
                exit()
 
def on_callback(key):
    print("{} pressed (callback)".format(key))
    if key == "1": c1.print_class1
    if key == "2": c2.print_class2    

c1 = Class1()
c2 = Class2(on_callback)

def main():
    pass

if __name__ == '__main__':
    main()
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@Pf@nne,
unter anderem ist das größte Problem, dass du schon versuchst auf die Instanz "c2" der Klasse "Class2" zuzugreifen bevor die "__init__" von "Class2" gelaufen ist.
Ich kann aber nicht erkennen, was du eigentlich erreichen willst, daher kann ich dir auch nicht sagen wie man es besser machen kann.

Insbesondere ist unklar warum der Userinput in seinem eigenen Thread laufen muss. In der aktuellen Version gibt es dafür keinen Grund.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Keine der Klassen ist nötig, geschweige denn sinnvoll. Ebenso wenig der Thread. Nutzereingaben sollten sowieso immer im Hauptthread erfolgen.
Wenn man in einem callback Variablen von wo anders braucht, muss man sie als Argumente übergeben; das geht mit functools.partial.

Code: Alles auswählen

from functools import partial

def wait_for_key(callback):
    while True:
        print("1 = class 1")
        print("2 = class 2")
        print("q = quit")
        key = input()
        if key == "1":
            callback(key)
        elif key == "2":
            callback(key)
        elif key == 'q':
            print("bye")
            return

def on_callback(c1, c2, key):
    print(f"{key} pressed (callback)")
    if key == "1":
        print(c1)
    elif key == "2":
        print(c2)

def main():
    wait_for_key(partial(callback, 11, 22))

if __name__ == "__main__":
    main()
Pf@nne
User
Beiträge: 43
Registriert: Donnerstag 18. April 2013, 16:50

Da ist ein vereinfachtes Beispiel aus meinem aktuellen Projekt.

Class1 ist eine von zwei Hauptklassen die eigentlich aktiv nichts macht und nur initialisiert wird um dann functions zum Ausführen von Aufgaben enthält.
Class2 ist eine TCP-Serverklasse die die nach dem Empfang von Daten einen Calback in main aufruft.

Von diesem Callback aus möchte ich sowohl functions in Class1 als auch in Class2 aufrufen.
Die functions von Class2 könnte ich natürlich auch ohne den Umweg über das Callback aufrufen.
Ich fand es aber übersichtlicher dies im Callback des Main zu tun.

Gibt es denn eine Möglichkeit die Klassen vorher zu deklarieren und zu initialisieren?
Wenn ich dies an den Anfang setze ist die Callback-Funktion noch unbekannt.
Irgendwie beißt sich die Katze in den Schwanz......

Code: Alles auswählen

import threading
###############################################################################
#   CALLBACKS
###############################################################################

class WorkerClass1():
    def print_class1():
        print("--------")
        print("class1")
        print("--------")

class ServerClass2():
    def __init__(self, callback):
        self.callback = callback
        thread = threading.Thread(target=self.Wait_for_client)
        thread.start()
        thread.join()
        
    def print_class2():
        print("--------")
        print("class2")
        print("--------")
        
    def Wait_for_client(self):
        while 1:
            print("1 = class 1")
            print("2 = class 2")
            print("q = quit")
            key = input()
            if key == "1":
                self.callback(key)
            if key == "2":
                self.callback(key)
            if key == 'q':
                print("bye")
                exit()


def on_callback(key):
    print("{} pressed (callback)".format(key))
    if key == "1": c1.print_class1
    if key == "2": c2.print_class2 
       
c1 = WorkerClass1()
c2 = ServerClass2(on_callback)

def main():
    pass

if __name__ == '__main__':
    main()
    
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@Pf@nne
Was, meinst du, macht das hier?

Code: Alles auswählen

def main():
    pass

if __name__ == '__main__':
    main()
Hier testest du, ob das Modul als Programm aufgerufen wurde - also nicht bloß von woanders importiert wurde - und rufst dann die Funktion main() auf, die nichts macht. Denn das ist es, was das pass-Statement bedeutet: Mach nichts. Wenn du den ganzen Block und die Funktion weglassen würdest, wäre das Ergebnis dasselbe.

Code: Alles auswählen

class Class1():
    ...
class Class2():
    ...
Was bewirken die Klammern hinter den Klassennamen? Richtig, nichts. Also lass sie weg.

Code: Alles auswählen

class Class1:
    def print_class1():
        ...
class Class2:
    ...    
    def print_class2():
        ...
Methoden brauchen wenigstens einen Parameter, den ersten. Dieser wird per Konvention in Python self genannt.

Code: Alles auswählen

import threading
...
class Class2:
    def __init__(self, callback):
        self.callback = callback
        thread = threading.Thread(target=self.Wait_for_key)
        thread.start()
        thread.join()
Thread.join() blockiert den aufrufenden Thread - also den, in dem der Aufruf steht. Wenn das in einer __init__()-Methode geschieht, dann endet die Erzeugung des entsprechenden Objektes erst, wenn der Thread beendet wurde. Ich vermute, das ist nicht das, was du willst.

Code: Alles auswählen

        while 1:
            ...
Es gibt True und False in Python. Man verwendet hier nicht 1.

Code: Alles auswählen

    if key == "1": c1.print_class1
    if key == "2": c2.print_class2    
Hier hast du vergessen, die Funktionen auch aufzurufen. Vergleiche:

Code: Alles auswählen

>>> def foo():
...     print('foo() wurde aufgerufen')
... 
>>> foo
<function foo at 0x7f9284fc0670>
>>> foo()
foo() wurde aufgerufen
Mir ist auch nicht klar, wozu du die zwei Klassen brauchst. Ich würde ja eher sowas machen:

Code: Alles auswählen

import threading


def foo():
    print('foo!')


def bar():
    print('bar!')


def dispatch_input(callback_map):
    while True:
        data = input('Bitte irgendwas eingeben:').strip().lower()
        if data == 'q':
            break
        elif data in callback_map:
            callback_map[data]()
        else:
            print('Whatcha sayin\'?')


def main():
    thread = threading.Thread(
        target=dispatch_input,
        args=({'1': foo, '2': bar},)
    )
    thread.start()
    thread.join()


if __name__ == '__main__':
    main()
In specifications, Murphy's Law supersedes Ohm's.
Antworten