Callback aufruf führt zu unbekannten propertys auf self

  • Hallo zusammen,

    in javascript lebt ein callback in seinem Object weiter und kann auch auf propertys in einer "Klasse" zugreifen.
    Also weis die Callback in welcher Instanz sie sich befindet und kann so mit der Instanz interagieren.

    Genau das klappt aber by python nicht, jedenfalls bei mir.

    Wenn ich eine Funktion in meinem Objekt aufrufe via Callback und dann auf self.foo zugreife ist foo leider nicht bekannt.
    Obwohl mir der Debugger im Breakpoint sagt, es sei
    1. Das richtige Objekt
    2. Der richtige Speicher

    self = {<messaging.messageprocess.MessageProcess object at 0x76298630>}

    Genau hier ist aber das Problem:
    self.message_model ist kein attribute auf dem objekt, wenn ich aus einem Callback kommt.

    Meine Frage ist daher, wie bekomme ich im Callback zugriff auf die property self.message_model.
    Vielen Dank für die Hilfe!

    LG
    Ipad

  • Callback aufruf führt zu unbekannten propertys auf self? Schau mal ob du hier fündig wirst!

  • Ich versteh das Problem irgendwie nicht... Kannst du innerhalb der Klasse MessageProcess() nicht auf message_model zugreifen, oder nicht von ausserhalb?
    Wie sieht denn MessageModel() aus?

    Wenn ich dich richtig verstehe möchtest du sowas machen:

    :huh:

  • Hallo,

    der Code kann IMHO so wie so nicht wirklich funktioniere...

    `process_dict['update_from_json'] = self.update_model_from_json` sollte schon einen Fehler werfen, weil a) `update_model_from_json` eine Funktion ist, du es aber wie ein Attribut aufrufst und b) die Funktion einen Wert für `json_obj` erwartet, was du aber nicht übergibst.

    Die Funktion `fetch_callbacks` ist auch komisch. Mal abgesehen davon, dass es IMHO verwirrend ist, Variablen im Scope der Funktion zu haben, die genau so heißen wie Attribute der Klasse, macht das Setzen von `hardware_callback_received = True` usw keine Sinn, weil der Wert nie mehr benötigt wird. Die Variable "lebt" nur im Scope der Funktion und ist danach weg. Rufst du die Funktion neu auf, werden auf die lokalen Variablen neu mit `False` belegt.

    Die Funktion `notify_update` ist auch komisch. `self.hardware_callback` usw. wird in `__init__` auf `None` gesetzt, in `fetch_callback` an einen Wert aus einem Dict gebunden und in `nofitfy_update` wie eine Funktion aufgerufen und zwar mit einer Instanz einer Klasse. Klar kann das funktionieren, es macht den Code / den Zustand des Programms schwer nachvollziehbar bzw. für Aussenstehende wie uns quasi unmöglich nachvollziehbar.

    Gruß, noisefloor

  • Zitat von &quot;noisefloor&quot; pid='295981' dateline='1503142385'


    `process_dict['update_from_json'] = self.update_model_from_json` sollte schon einen Fehler werfen, weil a) `update_model_from_json` eine Funktion ist, du es aber wie ein Attribut aufrufst und b) die Funktion einen Wert für `json_obj` erwartet, was du aber nicht übergibst.

    Das funktioniert IMHO so aber trotzdem, so wie er es vermutlich vor hat:

    Zitat von &quot;noisefloor&quot; pid='295981' dateline='1503142385'


    Die Funktion `fetch_callbacks` ist auch komisch. Mal abgesehen davon, dass es IMHO verwirrend ist, Variablen im Scope der Funktion zu haben, die genau so heißen wie Attribute der Klasse, macht das Setzen von `hardware_callback_received = True` usw keine Sinn, weil der Wert nie mehr benötigt wird. Die Variable "lebt" nur im Scope der Funktion und ist danach weg. Rufst du die Funktion neu auf, werden auf die lokalen Variablen neu mit `False` belegt.

    Das ist auch Sinn der Sache ;)
    Die while in fetch_callbacks() läuft bis die Callbacks gefunden wurden. Vermutlich von einem anderen Vorgang - siehe process_dict. Wenn sie gefunden wurden werden sie innerhalb MessageProcess() separaten konstanten zugewiesen....
    Allerdings ist das IMHO in der Tat überflüssig, siehe Anfang. Wenn es bereits (von wo auch immer) ins process_dict eingetragen wurde ist es bereits in der Klasse verfügbar.
    Also ja doch, es ist komisch, funktioniert aber.

  • Hallo:

    noisefloor:

    das ist kein aufruf sondern die zuweisung eines callbacks.
    ich möchte die Funktion zuweisen und übergeben und dann zu einem späterem Zeitpunkt ausführen.

    In Javascript, kann man so eine async aufruf "syncrhon" machen da die callback erst dann aufgerufen wird, wenn der asynchrone
    Aufruf durch ist (z.B.: Ajax-request).
    der Callback kennt seine position im Speicher und und somit auch seine Instanz. So kann in JS zwischen objekten kommuniziert werden.

    Die fetching Methode ist dafür da um Raceconditions abzufangen damit nicht auf etwas zugegriffen wird, was noch nicht existiert.
    Das waren meine Gedanken hinter dem Code.

    @meigrafd

    Danke, genau das. SO habe ich es gemeint.
    danke für den Hinweis, ich habe dabei aber bemerkt, das eine der Callbacks noch nicht vorhanden ist.
    der MessageProcess ist leider doppelt mit einem Process verbunden, heißt:
    Er braucht eine Funktion von Prozess1 und er gibt eine Funktion an Prozess1. Daher habe ich die Racecondition und die Logik (welche ich selber absolut unschön finde).
    Leider kenne ich gerade noch keinen besseren Weg (mangelnde Erfahrung mit Python)

    anyway: vielen dank für hilfe und Zet! :) an beide.

    Einmal editiert, zuletzt von Ipad0214 (19. August 2017 um 14:28)

  • Hallo,

    ok... dann habe ich den Code wohl in Teilen falsch verstanden.

    Zitat

    Leider kenne ich gerade noch keinen besseren Weg (mangelnde Erfahrung mit Python)


    Grundsätzlich solltest du vermeiden, den Programmierstil von JavaScript auf Python zu übertragen, weil das schief geht (umgekehrt auch, BTW ;) ).

    Python kennt z.B. kein globales Objekt im Sinne von JavaScript es kennt und das ganze Event-getriebene / asynchrone kennt Python OOTB auch nicht. Die Verwendung von Callbacks ist in Python auch nicht gängige Praxis, jedenfalls nicht in der Form, wie es bei JavaScript üblich ist (was dann schon mal gerne in der Callback-Hölle endet :) ).

    Wie meigrafd schon sagt, sag' doch mal, was du vorhast. Vermutlich gibt es eine "pythonischere" Lösung als das, was dir gerade vorschwebt.

    Gruß, noisefloor

  • Hallo zusammen,

    noisefloor

    wahrscheinlich hast du recht, daher war ja meine Frage ob das auch besser machen kann. Ich habe versuch mein Problem soweit zu abstrahieren. Allerdings funktioniert es dann, es muss also ein Problem sein bei inter Prozess aufrufen.

    Zitat

    wie es bei JavaScript üblich ist (was dann schon mal gerne in der Callback-Hölle endet :) )

    Wenn man es nicht richtig strukturiert, ja allerdings kann man JS und vor allem Coffeescript oder Typescript so gut strukturieren das keine Höhle entsteht.
    Für UI Entwicklung, sogar für Desktops gibt es nicht besseres als JS, HTML. (mMn). ;.)
    Callbacks wurden aber über die letzten 3-4 Jahre in vielen Hohen Sprachen nachgezogen. C# durch Actions, Java (Android), C++.
    Es ist ja nur ein pointer auf eine Funktion. Also kann jede Pointerbasierende Sprache Callbacks anwenden.

    Zitat

    den Programmierstil von JavaScript auf Python zu übertragen


    Aber wenn mir Proifs in Python sagen, das man es anders machen sollte, bin ich natürlich offen. Ich will ja was lernen und meinen Horizont erweitern.

    Ich habe mein gesamtes Programm mal als .zip an diesen Post angehangen, vielleicht erkennt ihr sofort einen Fehler. (was wahrscheinlich ist).
    Im Context ist es vielleicht einfacher das Problem zu sehen.

    Was ich genau möchte:

    Die Raspberry PI soll das Herzstück einer Robotersteuerung sein.
    Die Raspberry pi hat 3 aufgaben:
    1. Senden von Eingabewerten (Joystick input, buttons auf einer Android App/Desktopapp an einen Arduino über NRF24
    2. Senden von Rückgabewerden des Arduino an UI-Geräte über einen Websocket. X Geräte können daher die Daten lesen aber nur einer ist aktiv und darf auch wieder senden.
    3. Jostick-eingaben erkennen und beide anderen Module senden.

    Ich würde mich sehr freuen, wenn ihr mir vielleicht sagen könnt ob der Weg kompletter Schwachsinn ist und es viel einfacher geht oder ob es grundsätzlich richtig ist.
    Python Version ist 3.4.2 auf der PI
    Da ich remote debugge ist der Interpreter immer auf der gleichen Version wie die der Pi.

    Vielen Dank für eure Hilfe und Zeit!
    Patrick

  • Kommt mir teilweise bekannt vor der Weg ;)

    Für mich ist es immer noch schwer nachvollziehbar was es mit der "callback" Orgie auf sich hat... Überall hast du "callback" drin ob alleine oder irgendwo angefügt, aber warum?

    Ein mögliches Problem was ich sehe ist: Du verwendest "dict" als eigene Variable ("dict = Manager().dict()") und nutzt in websocket.socketprocess selber direkt "dict(callback=callback)"... deine eigener Variablenamen überschreibt aber die interne Funktion, du musst also deinen eigenen Variablenamen anders setzen.

    Von Wo/Was werden deine Scripts überhaupt ausgeführt bzw in Gang gebracht? Ich sehe nirgends eine Verarbeitung, wo das ganze nicht nur initialisiert sondern wirklich ausgeführt wird? Wo interagieren die einzelnen Script miteinander? Wo wird die jeweilige callback deiner Klassen ausgeführt? :s


    Also ich glaub Du brichst dir da ziemlich ein ab, was man IMHO auch einfacher hinkriegen könnte.

  • In dem main.py
    dort initialisiere ich die scripts und starte die Prozesse, oder was genau meinst du?

    Ja, bestimmt kann man das einfacher schaffen. Mein Problem war, das der WEbsocket das Programm blockt.
    In der Zeit kann also auch nichts vom NRF24 empfangen werden. Somit habe ich 2 Deadlocks, daher die Auslagerung in verschiedene Prozesse

  • Hallo,

    Zitat

    Callbacks wurden aber über die letzten 3-4 Jahre in vielen Hohen Sprachen nachgezogen. C# durch Actions, Java (Android), C++.


    Asyncronität OOTB hat in (C)Python mit dem dem asyncio und dem `async def ...` / `await` Syntax Einzug gehalten, also so richtig in Python 3.5. Module, die die Funktionen bereitstellen, gibt's aber schon viel länger.

    Ich finde den Code auch ziemlich undurchsichtig. Der Kernpunkt, den ich nicht verstehe ist, warum die Tornado bzw. dessen Klassen für Websockets konsequent nutzt. Die haben doch alle Methoden und können auf Events reagieren.

    Hier kann dir aber meigrafd wohl besser weiterhelfen, weil sein Roboter das AFAIK auch nutzt.

    Gruß, noisefloor

  • Zitat von &quot;noisefloor&quot; pid='296143' dateline='1503235858'


    Ich finde den Code auch ziemlich undurchsichtig. Der Kernpunkt, den ich nicht verstehe ist, warum die Tornado bzw. dessen Klassen für Websockets konsequent nutzt. Die haben doch alle Methoden und können auf Events reagieren.
    Gruß, noisefloor


    Wenn es eine möglichkeit gibt die on_message event getrieben zu steuern, würde ich das sehr sehr gerne machen. Allerdings habe ich dazu kein Beispiel gefunden der nicht blockt.

  • Hallo,

    `on_message`wird doch bei einer eingehend Nachricht aufgerufen und dann der Code darin ausgeführt. Das dauert halt so lange, bis der Code durch ist. Wenn du da was länger laufendes hast, dann musst du das ggf. in eine weiteren Thread / Prozess auslagern und oder du nutzt eine asynchorn Task Queue, die das im Hintergrund abarbeitet.

    Wobei bei einer Robotorsteuerung IMHO nichts lang laufendes dabei sein sollte...

    Nachmal zu deinem eigentliche Problem: Du kannst in Python nicht nur mit Callbacks arbeiten. Du brauchst schon einen Mainloop, der sich um das Programm kümmert.

    Gruß, noisefloor

  • Aber genau da liegt das Problem, was ist wenn der Prozess blockiert ist, der Flyer aber eine Message sendet, welche bearbeitet werden muss.
    Dann wird die Message gar nicht erst empfangen, da der Prozess durch on_message blcokiert ist.
    Daher meine Überlegung, dass der MessageHandler zwischen den beiden Prozessen steht und annimmt und übrgibt.
    Die Möglichkeit das dieser frei arbeiten kann ohne Blockaden, konnte ich mir nur über Prozesse vorstellen.

  • Hallo,

    eigentlich ist Tornado vom Kern auf so gestrickt, dass asynchron arbeitet, also dass es auch mehrere (tausend) Request auf einmal abarbeiten kann. In wie fern das auch bei den Websockets implementiert ist, kann ich dir nicht sagen. Sollte IMHO aber genau so funktionieren.

    Der andere Ansatz ist, alles eingehenden Nachtrichten des Websockets in eine Queue zu schieben und einen Prozess zu haben, der die Quese Nachricht für Nachricht abarbeitet. Das wäre IMHO zumindest für die Steuerung des Roboters der "klassische" Ansatz mit Python.

    Gruß, noisefloor

  • Zitat von &quot;meigrafd&quot; pid='296100' dateline='1503218550'


    Ein mögliches Problem was ich sehe ist: Du verwendest "dict" als eigene Variable ("dict = Manager().dict()") und nutzt in websocket.socketprocess selber direkt "dict(callback=callback)"... deine eigener Variablenamen überschreibt aber die interne Funktion, du musst also deinen eigenen Variablenamen anders setzen.

    Das verstehe ich nicht. In der Scope lebt dict doch gar nicht mehr, es ist ja nicht global. ich übergebe dict an den socketprocess als parameter und
    arbeite im Konstruktor mit manager weiter.

    Du meinst dict ist in dem Fall global?

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!