key error mit lambda

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.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Ich bekomme einn kay error mit lambda. Di Fehlermeldung ist:

Code: Alles auswählen

  File "communication.py", line 30, in <module>
    Testing()
  File "communication.py", line 28, in __init__
    send('PRINT',1,2,3)
  File "communication.py", line 13, in <lambda>
    send =  lambda *args: self.communication.send(*args)
NameError: name 'self' is not defined
Und das ist mein Code:

Code: Alles auswählen

#import proxy

class SelfCommunication:

    def __init__(self,proxy=None):
        # self.__proxy = proxy if proxy else proxy.Proxy()
        self.__proxy = proxy

    def send(self,*args):
        self,__proxy.send(*args)


send =  lambda *args: self.communication.send(*args)

# for testing ===========================

class TestProxy:

    def send(self,*args):
        print(*args)


class Testing:
    def __init__(self):
        self.communication = SelfCommunication(TestProxy())

        # testing
        send('PRINT',1,2,3)

Testing()
Ich glaube, man kann erkennen, was ich bezwecke. Wie kann ich das lösen?
Sirius3
User
Beiträge: 18265
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: das ist weder ein KeyError noch ein kay error, sondern ein NameError. Woher soll an der Stelle auch self kommen? Das wäre ja wieder eine globale Variable. Sollte man nicht machen. Man erkennt auch nicht, was Du da versuchst zu machen, weil das ergibt alles irgendwie keinen Sinn. Kannst Du mal in Worten beschreiben, was Du erreichen willst, aber nicht, wie Du denkst, wie die Lösung aussehen könnte.
Benutzeravatar
noisefloor
User
Beiträge: 4187
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

du hast keine Key-Error, du hast einen NameError. Warum, steht ja in der Fehlermeldung. `self` ist in der Zeile 13 nicht bekannt. Und IMHO macht das `lambda` da so wie so keinen Sinn.

Und wenn du den Fehler behoben hast, dann bekommst du auch in Zeile 10 und Zeile 28 einen Fehler.

Gruß, noisefloor
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:Das wäre ja wieder eine globale Variable. Sollte man nicht machen. Man erkennt auch nicht, was Du da versuchst zu machen, weil das ergibt alles irgendwie keinen Sinn. Kannst Du mal in Worten beschreiben, was Du erreichen willst, aber nicht, wie Du denkst, wie die Lösung aussehen könnte.
Nein ich versuche es ja jetzt gerade ohne globale Variabale zu machen. Und es war noch ein Tippfehlder drin, nämlich Komma statt Punkt. Jetzt habe ich ich nochmals einen Kommentar eingefügt, der beschreibt, was ich tun wil:

Code: Alles auswählen

#import proxy

class SelfCommunication:

    def __init__(self,proxy=None):
        # self.__proxy = proxy if proxy else proxy.Proxy()
        self.__proxy = proxy

    def send(self,*args):
        self.__proxy.send(*args)

send =  lambda *args: self.communication.send(*args)

# for testing ===========================

class TestProxy:

    def send(self,*args):
        print(*args)

class Testing:
    def __init__(self):
        self.communication = SelfCommunication(TestProxy())

        # testing
        # also das geht:

        self.communication.send('PRINT',1,2,3)

        # ich möchte es aber so haben und ohne globale Variable
        # send('PRINT',1,2,3)

        # Der Ausdruck soll eine Macro sein, der selber auf self.communication
        # des Objektes zugreift, in dem er aufgerufen wurde

Testing()
Benutzeravatar
noisefloor
User
Beiträge: 4187
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

das beseitigt den NameError aber nicht. Zeile 13 steht auf _oberste_ Ebene des Skript, d.h. diese ("gewöhnliche) Zeile wird _immer_ ausgeführt, wenn du das Skript ausführst. Und in Zeile 13 ist `self` nicht definiert.

Gegenfrage, weil du in anderen Threads eigentlich immer den Eindruck erweckst, dass du eine leise Ahnung von Python hättest: dir ist klar, welche Bedeutung das `self` bei Python hat? Wenn nein -> das ist der eigentliche Fehler.

Gruß, noisefloor
Sirius3
User
Beiträge: 18265
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: Python kennt keine Macros. Du beschreibst wieder, wie Du etwas machen willst, aber nicht, was! Die Klassen machen keinen Sinn, SelfCommunication, ist eine Klasse, die nichts macht, TestProxy macht auch nichts sinnvolles, und Testing ist auch keine Klasse.

Das selbe Programm ohne die ganzen unnötigen Klassen

Code: Alles auswählen

def testing():
    print('PRINT',1,2,3)

testing()
Für alles weitere braucht es einen Grund. Also, warum das ganze?
Benutzeravatar
pillmuncher
User
Beiträge: 1530
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Alfons Mittelmeyer hat geschrieben:[...] kay error [...]
Nein, ich mach mich nicht lustig über dich, weil du dich vertippt hast, das passiert mir selber oft genug.

Jedoch: Der größte Kay Error war IMO OOP - besser gesagt, das, was die Leute daraus gemacht haben.
In specifications, Murphy's Law supersedes Ohm's.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

[quote="Sirius3"]@Alfons Mittelmeyer: Die Klassen machen keinen Sinn, SelfCommunication, ist eine Klasse, die nichts macht,[/code]
Natürlich macht die Klasse SelfCommunication einen Sinn. Sie leitet eine Information zum Proxy weiter und sie verbirgt vor dem Anwender die Methoden des Proxies, die ihn nichts angehen.

Code: Alles auswählen

#import proxy

class SelfCommunication:

    def __init__(self,proxy=None):
        # self.__proxy = proxy if proxy else proxy.Proxy()
        self.__proxy = proxy

    def send(self,*args):
        self.__proxy.send(*args)

class TestProxy:

    def send(self,*args):
        print(*args)

    def configuration(self,*args,**kwargs):
        print("Geht nur dem Administrator etwas an")


communication = SelfCommunication(TestProxy())

# Das geht =======================
communication.send("PRINT",1,2,3)

# Und das geht nicht, und genau das ist der Sinn
'''
communication.__proxy.configuration()
communication.configuration()
'''
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

noisefloor hat geschrieben:Gegenfrage, weil du in anderen Threads eigentlich immer den Eindruck erweckst, dass du eine leise Ahnung von Python hättest: dir ist klar, welche Bedeutung das `self` bei Python hat? Wenn nein -> das ist der eigentliche Fehler.Gruß, noisefloor
Weißt denn Du, wie man den Fehler beseitigt? Ich habe zwei Lösungen gefunden, die aber nicht ganz zufriedenstellend sind.

Fällt Dir vielleicht eine bessere ein?

Code: Alles auswählen

#import proxy
from functools import partial

class SelfCommunication:

    def __init__(self,proxy=None):
        # self.__proxy = proxy if proxy else proxy.Proxy()
        self.__proxy = proxy

    def send(self,*args):
        self.__proxy.send(*args)

def send(*args):
    return locals

# zwei nicht ganz zufriedenstellende Moeglichkeiten =====================
compile_send = compile("self.communication.send",'<string>', 'eval')
send1 = partial(eval,compile_send)

send2 = lambda *args: partial(eval,"self.communication.send"+repr(args))
# =======================================================================


# for testing ===========================

class TestProxy:

    def send(self,*args):
        print(*args)

class Testing:
    def __init__(self):
        self.communication = SelfCommunication(TestProxy())

        # testing
        # also das geht:

        self.communication.send('PRINT',1,2,3)

        # ich moechte es aber so haben und ohne globale Variable
        # send('PRINT',1,2,3)

        # etwas habe ich gefunden, aber noch nicht ganz zufriedenstellend      
        # jetz ist noch () zuviel, wie bekomme ich das noch weg???
        send1()('PRINT',4,5,6)
        send2('PRINT',7,8,8)()

Testing()
Benutzeravatar
noisefloor
User
Beiträge: 4187
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
Weißt denn Du, wie man den Fehler beseitigt?
Ja - das `self`muss weg, weil es an der Stelle grundlegend falsch ist.
Fällt Dir vielleicht eine bessere ein?
Wahrscheinlich, ja. Allerdings müsstest du mal die oben gestellte Frage beantworten _was_ du vorhast.

Das aktuelle `compile` und `eval` und `partial` ist jedenfalls ziemlich wüst und in der Form ziemlich sicher nicht notwendig.

Die Klasse `SelfCommunication` ist so trivial, dass du das auch mit einer Funktion erledigen kannst, zumal du das Attribute `__proxy` auch nur in der Klasse brauchst. Die Klasse `TestProxy` ist noch trivial, hier reicht erst recht eine Funktion. Zu `Testing` hat Sirius3 schon was geschrieben.

Zeig' doch mal dein _wirkliches_ Problem mit _echtem_ Code, d.h. dem Code, den du tatsächlich einsetzt. Alles andere bringt nicht. Außerdem ist das vernünftige Erklären eines Problems der 1. Schritt zur Lösung. Wenn du dein eigenes Problem nicht erklären kannst, dann wirst du auch nie eine Lösung finden bzw. verstehen.

Und was sicherlich auch stark helfen würde ist, wenn du dir nochmal ganz in Ruhe im Python-Tutorial die Abschnitte zu Objektorientierung, Klasse und deren Instanzen zu Gemüte führen würdest.

Gruß, noisefloor
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

noisefloor hat geschrieben:Die Klasse `SelfCommunication` ist so trivial, dass du das auch mit einer Funktion erledigen kannst, zumal du das Attribute `__proxy` auch nur in der Klasse brauchst.
In diesem Beispiel benutze ich die echte Proxy Klasse anstatt anstatt SelfCommunication. Der normale Anwender soll im echten Proxy nichts im Zusammenhang mit externen Queues konfigurieren dürfen. Zeig mal, wie ich das mit einer einfachen Funktion erledigen kann.

Code: Alles auswählen

import tkinter as tk

# ================ die echte Proxy Klasse ======================================

import threading
import queue

class Proxy:
 
    def __init__(self,extern_proxy=None):

        # if there is an extern proxy, multiple threads may communicate
        # via this extern proxy without knowing each other
        if extern_proxy == None: self.__extern_proxy = self
        else: self.__extern_proxy = extern_proxy
 
        self.reset()

    def __del__(self):
        # unregister extern registered callbacks
        if self.__extern_proxy != self: self.__extern_proxy.undo_receiveAll_extern(self)

    # extern trigger for communication between threads
    # extern trigger before start of event loop
    # - before there is an event loop, extern triggers by events will not have an effect
    # - for tkinter the extern trigger may be used or polling
    def __noop(self): pass

    def reset(self):

        # Configuration for triggers at start, which may be changed by the user
        self.trigger = self.do_work # intern trigger at start is direct call of 'do_work'.
        self.extern_trigger = self.__noop # extern trigger at start is set to do nothing. feel free to change this in your application

        self.__Dictionary = {} # contains registered message ids and registered callback functions for these message ids
        self.__owners = {} # contains owners and their callback functions:
                         # callback functions may be registerd for an owner (or for None in case of existence of the callbacks until end)
                         # if the owner becomes deleted it should unregister all it's callback functions
                         # because of this dictionary this may be done at once for all callback functions via undo_receiveAll or undo_receiveAll_extern

        self.__Queue = queue.Queue() # Queue for sending messages
        self.__Queue_HighPrio = queue.Queue() # Queue for register and unregister callback functions
        self.__register("execute_function",lambda msg: msg()) # very useful callback function: executes messages, which are lambda expressions
        self.__running = False # regulates calls of method 'do_work' - start value False enables call of method 'work'
        self.__looping = False # regulates ending event loop. After 'loop' is called, the loop may be ended by calling 'exit_loop'

    # process a message in a queue ==========
    def work(self,*args):

        # get message from Queue
        if not self.__Queue_HighPrio.empty(): data = self.__Queue_HighPrio.get()
        elif not self.__Queue.empty(): data = self.__Queue.get()
        else: return False
        self.send_immediate(*data)
        return True

    def send_immediate(self,msgid,msgdata):
        # look up message id and registered callbacks for it and call callback
        if msgid in self.__Dictionary:
            receivers = self.__Dictionary[msgid].items() # items contain callback function and optional_parameter
            for callback,optional_parameter in receivers:
                if type(optional_parameter) is bool:
                    if optional_parameter: callback((msgid,msgdata))
                    else: callback(msgdata)
                else: callback((optional_parameter,(msgid,msgdata)))

    # process all messages in the queues ========
    def do_work(self,*args):
        if self.__running: return
        self.__running = True
        while self.work(): pass
        self.__running = False

    # set both triggers at once, if it's the same ========
    def set_trigger(self,trigger):
        self.trigger = trigger
        self.extern_trigger = trigger

    # loop for event driven threads - don't use this in a GUI event loop (mainloop) - it will block the GUI
    def loop(self,do_with_loop = None):
        self.event = threading.Event()
        self.set_trigger(self.event.set)
        self.event.set()
        self.__looping = True
        if do_with_loop != None: do_with_loop()
        while self.__looping:
            self.event.wait()
            self.event.clear()
            self.do_work()

    # exit event loop 
    def exit_loop(self,*args):
        self.__looping = False
        self.trigger()

    # send functions invoke registered callbacks for the message id =======

    # send function within same thread
    def send(self,msgid,msgdata=None):
        self.__Queue.put((msgid,msgdata))
        self.trigger()

    # send function called by another thread - use it, if you directly want to access the proxy of another thread
    def send_extern(self,msgid,msgdata=None):
        self.__Queue.put((msgid,msgdata))
        self.extern_trigger()

    # send function called by another thread - you may use it also use it, if you directly want to access the proxy of another thread
    # this send function is used also in connection with communication via external proxy
    def receive_extern(self,message):
        self.__Queue.put(message)
        self.extern_trigger()

    # high prio should only be used for registrations ==
    def send_highprio(self,msgid,msgdata=None):
        self.__Queue_HighPrio.put((msgid,msgdata))
        self.trigger()

    # high prio should only be used for registrations ==
    def send_extern_highprio(self,msgid,msgdata=None):
        self.__Queue_HighPrio.put((msgid,msgdata))
        self.extern_trigger()

    # extern send and receive callbacks connected with the external proxy =======

    # registering a list of message ids, which shall be received via the external proxy
    def do_receive_extern(self,message_ids,owner=None):
        if owner == None: owner = self
        for mid in message_ids: self.__extern_proxy.do_receive_extern_one(owner,mid,self.receive_extern,True)

    # registering a list of message ids, which shall be sent to other tasks via the external proxy
    def do_send_extern(self,message_ids,owner=None):
        if owner == None: owner = self
        for mid in message_ids: self.do_receive(owner,mid,self.send_intern_extern,True)

    # callback for do_send_extern
    def send_intern_extern(self,message): self.__extern_proxy.receive_extern(message)

    # register callbacks ================================================

    # normal case - we use the queue - different reasons
    def do_receive(self,owner,msgid,callback,optional_parameter=False):
        self.__Queue_HighPrio.put(("execute_function",lambda: self.__do_receive(owner,msgid,callback,optional_parameter)))
        self.trigger()

    # normally not used - if you want to receive messages from another thread without using the external proxy
    # or if another thread wants to receive messages from this thread without the external proxy
    def do_receive_extern_one(self,owner,msgid,callback,optional_parameter=False):
        self.__Queue_HighPrio.put(("execute_function",lambda: self.__do_receive(owner,msgid,callback,optional_parameter)))
        self.extern_trigger()

    # register callback for the owner, so that it may be unregistered by undo_receive_All
    # and call register callback
    def __do_receive(self,owner,msgid,callback,optional_parameter):
        if owner != None:
            if not owner in self.__owners: self.__owners[owner] = {}
            self.__owners[owner][callback]=msgid
        self.__register(msgid,callback,optional_parameter)

    # register message id, callback and optional_parameter in the message id dictionary
    def __register(self,msgid,callback,optional_parameter=False):
        if msgid not in self.__Dictionary: self.__Dictionary[msgid] = {}
        self.__Dictionary[msgid][callback] = optional_parameter


    # normal case - we use the queue - different reasons
    def do_receive_option(self,owner,msgid,callback,optional_parameter=False):
        self.__Queue_HighPrio.put(("execute_function",lambda: self.__do_receive_option(owner,msgid,callback,optional_parameter)))
        self.trigger()

    # register callback for the owner, so that it may be unregistered by undo_receive_All
    # and call register callback
    def __do_receive_option(self,owner,msgid,callback,optional_parameter):
        if owner != None:
            if not owner in self.__owners: self.__owners[owner] = {}
            self.__owners[owner][callback]=msgid
        self.__register_option(msgid,callback,optional_parameter)
        
    # register message id, callback and optional_parameter in the message id dictionary
    def __register_option(self,msgid,callback,optional_parameter=False):
        if msgid not in self.__Dictionary: self.__Dictionary[msgid] = {}
        if callback not in self.__Dictionary[msgid]: self.__Dictionary[msgid][callback] = {}
        self.__Dictionary[msgid][callback][optional_parameter] = None

    # unregister callback ================================================

    def unregister_msgid(self,msgid):
        self.__Dictionary.pop(msgid,None)

    # normal case - we use the queue - different reasons
    def undo_receive(self,owner,msgid,callback):
        self.__Queue_HighPrio.put(("execute_function",lambda: self.__undo_receive(owner,msgid,callback)))
        self.trigger()
 
    # normally not used - only interesting for callbacks in connection with other threads without using the external proxy
    def undo_receive_extern(self,owner,msgid,callback):
        self.__Queue_HighPrio.put(("execute_function",lambda: self.__undo_receive(owner,msgid,callback)))
        self.extern_trigger()

    # unregister callback for the owner
    # and call unregister register callback
    def __undo_receive(self,owner,msgid,callback):
        if owner != None:
            if owner in self.__owners:
                if callback in self.__owners[owner]: del self.__owners[owner][callback]
        self.__unregister1(msgid,callback)

    # unregister message id, callback (and optional_parameter) by removing the entry from the dictionary
    def __unregister1(self,msgid,callback):
        if msgid in self.__Dictionary:
            receivers = self.__Dictionary[msgid]
            if callback in receivers:
                del receivers[callback]
                if len(receivers) == 0: del self.__Dictionary[msgid]


    # normal case - we use the queue - different reasons
    def undo_receive_option(self,owner_and_option,msgid,callback):
        self.__Queue_HighPrio.put(("execute_function",lambda: self.__undo_receive_option(owner_and_option,msgid,callback)))
        self.trigger()


    # unregister callback for the owner
    # and call unregister register callback
    def __undo_receive_option(self,owner_and_option,msgid,callback):
        if owner_and_option != None:
            if owner_and_option in self.__owners:
                if callback in self.__owners[owner_and_option]: del self.__owners[owner_and_option][callback]
        self.__unregister1_option(msgid,owner_and_option,callback)

    # unregister message id, callback (and optional_parameter) by removing the entry from the dictionary
    def __unregister1_option(self,owner_and_option,msgid,callback):
        if msgid in self.__Dictionary:
            receivers = self.__Dictionary[msgid]
            if callback in receivers:
                if owner_and_option in receivers[callback]:
                    del receivers[callback][owner_and_option]
                    if len(receivers[callback]) == 0:
                        del receivers[callback]
                        if len(receivers) == 0: del self.__Dictionary[msgid]

    # unregister Owner ================================================

    # unregister all callbacks of an owner
    def undo_receiveAll(self,owner):
        self.__Queue_HighPrio.put(("execute_function",lambda: self.__undo_receiveAll(owner)))
        self.trigger()

    # unregister all callbacks of an owner, which are registered by the proxy of another thread
    def undo_receiveAll_extern(self,owner):
        self.__Queue_HighPrio.put(("execute_function",lambda: self.__undo_receiveAll(owner)))
        self.extern_trigger()

    # remove owner_and_option from the owners dictionary and unregister its callbacks ============
    # undo_receiveAll_option should be called, if an owner_and_option is destroyed and the callbacks would become not valid after this
    def __undo_receiveAll(self,owner):
        if owner in self.__owners:
            messages = self.__owners[owner]
            del self.__owners[owner]
            for callback,msgid in messages.items(): self.__unregister1(msgid,callback)

    # unregister all callbacks of an owner_and_option
    def undo_receiveAll_option(self,owner_and_option):
        print("UndoAll")
        self.__Queue_HighPrio.put(("execute_function",lambda: self.__undo_receiveAll_option(owner_and_option)))
        self.trigger()

    # remove owner_and_option_and_option from the owners dictionary and unregister its options maybe the callbacks ============
    def __undo_receiveAll_option(self,owner_and_option):
        if owner_and_otion in self.__owners:
            messages = self.__owners[owner_and_option]
            del self.__owners[owner_and_option]
            for callback,msgid in messages.items():
                print("unregister",msgid)
                self.__unregister1_option(owner_and_option,msgid,callback)
            
    def wait_unroute(self,unroute):
        self.__extern_proxy.do_receive_extern_one(self,"UNROUTED",self.exit_loop)
        self.loop(unroute)

# === Anwendung ============================================================================

class Application(tk.Tk):

    def __init__(self,communication,**kwargs):
        tk.Tk.__init__(self,**kwargs)
        self.communication=communication

        self.labelFrame = Labelframe(self,text='Some Frame Somewhere in the GUI')
        self.toggle_status_label = tk.Label(self,bg='white', pady='7')
        self.toggle_status_label.pack(fill='x', pady=2)
        self.labelFrame.pack()
        self.toggle_status = False
        self.toggle_self()

        # Message "TOGGLE" Empfang registrierung mit callback definition
        self.communication.do_receive(self,"TOGGLE",self.toggle_self)

    def toggle_self(self,*args):
        self.toggle_status = not self.toggle_status
        self.toggle_status_label['text'] = "TOGGLE EIN" if self.toggle_status else "TOGGLE AUS"
    

class Labelframe(tk.LabelFrame):

    def __init__(self,master,**kwargs):
        tk.LabelFrame.__init__(self,master,**kwargs)
        self.communication = self.master.communication

        self.toggle = tk.Button(self,command=self.send_toggle,text='Toggle Satus', pady='3m', bd='3')
        self.toggle.pack(pady=20)

    # sendet die Message "TOGGLE" 
    def send_toggle(self):
        self.communication.send("TOGGLE")
        

Application(Proxy()).mainloop()
noisefloor hat geschrieben:Die Klasse `TestProxy` ist noch trivial, hier reicht erst recht eine Funktion.
Testproxy war nur zum Testen ob etwas geht. In Wirklichkeit handelt es sich um ein Proxyobjekt, das vom Administrator mit einem Externen Proxy verküpft sein kann. Und der wieder mit anderen. Wenn Du eine Message mit Dateinamen schickts, könnte auch eine MP4 Datei vom PC über WLAN auf das Handy kopiert werden. Wie machst Du das mit einer einfachen Funktion?

Was natürlich kein richtiges Problem ist, self.communication.send statt nur send zu schreiben. Das müßte ich dann nur an etwa hundert Stellen tun. Falls es mir später aber einfallen sollte, das wieder anders zu schreiben, wäre das Aufwand. Mit so einem send Makro bräuchte ich nichts zu ändern, wenn ich dann statt einer globalen Variablen self.communication benutze
Sirius3
User
Beiträge: 18265
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: so funktioniert halt Python; es gibt keine Macros, die an der Stelle, an der sie aufgerufen werden, expandiert werden und dort auf lokale Variablen zugreifen können. Das ist ja auch maximal Undurchsichtig. Ich habe mich jetzt nicht durch die ganzen Klassen durchgearbeitet, nur soviel, es gibt zu viele doppelte Unterstriche. Und ich habe immer noch das Gefühl, Du willst da was erzwingen, was keiner Kontrolle bedarf. Bei Programmen gibt es keinen Administrator. Der Programmierer hat die absolute Macht und niemand kann durch noch so komplizierte Konstrukte einem Programmierer etwas verbieten. Auf der anderen Seite, was soll das jetzt mit dem Verschicken von Dateien zu tun haben? Ich befürchte, Du vermischst da Dinge, die nichts miteinander zu tun haben.

Also, statt hunderte Zeilen Code zu posten, beschriebe in so wenigen Worten wie möglich, was für ein Problem Du versuchst zu lösen.
Benutzeravatar
noisefloor
User
Beiträge: 4187
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
beschriebe in so wenigen Worten wie möglich, was für ein Problem Du versuchst zu lösen.
+1. Mir ist auch absolut nicht klar, wer mit wem kommunizieren soll und warum. Oder geht es tatsächlich "nur" darum, einen Text in Tk zu ändern?

Erzeugst du mit Absicht nirgendwo eine Instanz von Proxy? Und den Quelltext hat zu wenig Zeilenumbrüche, um wirklich lesbar zu sein. Nur weil der Python-Interpreter das versteht heißt das ja nicht, dass man das als Mensch auch so versteht / Lesen kann.

Gruß, noisefloor
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:@Alfons Mittelmeyer: so funktioniert halt Python; es gibt keine Macros, die an der Stelle, an der sie aufgerufen werden, expandiert werden und dort auf lokale Variablen zugreifen können. Das ist ja auch maximal Undurchsichtig
Es gibt ja fast so ein macro, nämlich functools.partial, Leider kann dabei nicht bestimmen, dass partial nur auf den ersten Parameter angewendet wird, und die folgenden Parameter nicht an die function im ersten Parameter übergeben werden, sondern an die durch partial erzeugte Funktion. Beispiele für partial in python Dokumentationen, wie partial etwa arbeiten würde, sind leider nicht zu gebrauchen. Damit kann man keine ähnliche Funktion mit echten partial Eigenschaften implementieren.
Sirius3 hat geschrieben:Ich habe mich jetzt nicht durch die ganzen Klassen durchgearbeitet, nur soviel, s gibt zu viele doppelte Unterstriche. Und ich habe immer noch das Gefühl, Du willst da was erzwingen, was keiner Kontrolle bedarf.
Die doppelte Unterstriche dienen nicht der Kontrolle, sondern markieren die Interna, dann weiß man wenigstens was Schnittstellen sind und was keine sind.
Sirius3 hat geschrieben:Bei Programmen gibt es keinen Administrator. Der Programmierer hat die absolute Macht und niemand kann durch noch so komplizierte Konstrukte einem Programmierer etwas verbieten. Auf der anderen Seite, was soll das jetzt mit dem Verschicken von Dateien zu tun haben? Ich befürchte, Du vermischst da Dinge, die nichts miteinander zu tun haben.
Der Programmierer hat nur die Macht, über das Teilsystem, für das er verantwortlich ist. Wenn er für die GUI verantwortlich ist, dann kann er sie nach seinen Vorstellungen implementieren. Wenn es aber darum geht, eine Datei auf das Handy zu übertragen, dann bekommt er die Schnittstelle und die hat er einzuhalten. Vielleicht heißt die ja so:

send('MP4_DATEI_TO_HANDY',dateiname)

Und wie die message heißt muß zwischen den betroffenen Verantwortlichen abgeklärt werden. Dann muß der Admistrator die Schnittstelle konfigurieren, nämlich, daß diese Message nach außen geht zur zentralen Queue, die nicht global bekant ist, sondern als extern_proxy an das vom Adminitrator erzeugte Proxobjekt übergeben wird, welcher dann das Proxyobjekt, so konfiguriert, dass es diese Message nach außen zur zentralen Queue weiterleitet. Ebenso wird das extern_proxy Objekt so konfiguriert, daß es bestimmte Messages von außen an das zum Teilsystem gehörige Proxyobjekt übergibt. Das Teilsystem erzeugt in diesem Falle dieses Proxyobjekt nicht selber, sondern bekommt es übergeben. An dieser nach Außen Konfiguration hat das Teilsystem nichts mehr zu verändern. Unkontrollierte Messages nach außen könnten mit gleichnamigen Messages anderer kollidieren. Wenn einer das ganze Programm schreibt, dann hat er natürlich auch die Kontrolle über das ganze System. Aber es ist sinnvoll, Messages nach und von Außen zuerst zu konfigurieren und nicht dann, wenn einer in der GUI einen Button drückt.
Sirius3 hat geschrieben:Also, statt hunderte Zeilen Code zu posten, beschriebe in so wenigen Worten wie möglich, was für ein Problem Du versuchst zu lösen.
Es geht einfach darum, ob es die Möglichkeit in Python gibt, einen send Ausdruck so zu definieren, daß er wie eine Funktion beim Aufruf aussieht, aber auf ein nur lokal bekanntes Objekt zugreift. Mit partial geht das, aber nicht richtig, denn eval ist keine gute Lösung und die zusätzlichen Klammern sind auch keine Lösung.
Sirius3
User
Beiträge: 18265
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: niemand wird Schnittstellen definieren, in der Form "greife mal auf dieses globale Objekt zu". Sondern, beim Erzeugen einer Instanz wird eine wie auch immer geartete Instanz eines Kommunikations-Typs übergeben. Da ist das einzige, was der Nutzer wissen muß, dass es eine Methode send gibt. Und wenn send die einzige öffentliche Methode ist, kann man diese gleich der benutzenden Klasse übergeben. Da braucht man auch kein dazischengeschaltetes Proxy-Objekt. Und was das ganze mit irgendwelchen Konfigurationen, die ein Administrator macht, zu tun hat, ...
BlackJack

Also `partial()` hat schon echte Partial-Eigenschaften und es ist halt kein Makro weil Python keine Makros kennt. Wenn Du so etwas haben willst, dann musst Du halt eine Sprache verwenden die so etwas als Sprachmittel bietet. Hy beispielsweise, wenn man sich nicht allzu weit von Python entfernen möchte, und damit leben kann, das es eine Sprache ist, die noch recht jung und „im Fluss“ ist. Oder eben ein Lisp oder Scheme das schon ausgereifter und gesetzter ist. Oder eine andere Sprache die nicht aus der Lisp-Familie stammt, aber Makros zur Übersetzungszeit bietet.

In *der* Python-Dokumentation steht bei `partial()` eine in Python geschriebene Beispielimplementierung wie `partial()` grob funktioniert. Wenn Du das nicht zu gebrauchen findest, dann liegt das vielleicht nicht an Dir sondern an noch fehlendem Verständnis wie Python funktioniert. Tatsächlich ist es in CPython in C implementiert und da CPython quelloffen ist, auch einsehbar. Das heisst man kann sich wirklich *exakt* anschauen wie es funktioniert.

Zum markieren von Interna verwendet man *einen* führenden Unterstrich. Zwei haben eine andere Bedeutung und auch einen Effekt, sind also nicht nur reine Markierung.

Der Programmierer hat nicht nur die Macht über das Teilssystem, sondern kann auch an alles andere heran kommen. Wenn er mit anderen zusammenarbeitet und er sich auf sein Teilssystem beschränken soll, dann tut er das aus freien Stücken, oder sozialem Druck, oder sonstwas, aber nicht weil er es technisch nicht könnte.

Ich habe übrigens auch bis jetzt immer noch nicht verstanden was Du da eigentlich lösen willst. Weder wenn ich mir den Quelltext anschaue, noch aus den Beschreibungen.

Hilft es vielleicht sich noch einmal klar zu machen das auch Methoden Objekte sind und/oder das selbst geschriebene Datentypen auch den Aufruf über `__call__()` überschreiben können?
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:Da braucht man auch kein dazischengeschaltetes Proxy-Objekt. Und was das ganze mit irgendwelchen Konfigurationen, die ein Administrator macht, zu tun hat, ...
Das braucht man aus verschiedenen Gründen. Wenn Nachrichten nur jeweils an einen einzigen Empfänger im Teilsystem geht, braucht man den Proxy nicht. Es könnten aber auch mehre Empfänger erwarten, benachrichtigt zu werden. Will man dann aus einer Nachricht, etwa fünf machen und will man nochmal Hand anlegen, wenn später noch etwas Weiteres auch benachrichtigt werden will?

Außerdem braucht man es ebenfalls, wenn es darum geht Nachrichten, an andere Teilsystem zu übergeben. Die haben ihre eigene Queue, die nur im Teilsystem selber lokal bekannt ist. Und das Teilsystem ist auch nicht global vorhanden. Warum soll sich der Programmierer darum kümmern, welches Teilsystem benachrichtigt werden muß, das zudem global nicht vorhanden ist, ebenso wenig wie die App bei App(proxy).mainloop()

Im übergebenen Proxy steht drin, welche Nachrichten auch nach außen gehen, und im Zentralen Proxy steht drin, welche Nachrichten von außen er etwa and Queue (im Proxy) der GUI App übergibt. Außerdem kennt der eigene Proxy dann den zentralen Proxy und dieser wiederum den eigenen. Ansonsten wären das alles nur Unbekannte.

Welche Nachrichten von welchen Teilsystemen an welche Teilsysteme gehen, das wird konfiguriert und das macht der Adminitrator und übergibt den Teilsystemen ihren entsprechend konfigurierten und dann nur mehr lokal vorhandenen Proxy.

Das wären etwa die Methoden des Proxy die eine GUI App haben sollte, wenn auch für eine statische GUI die ersten beiden genügen:

# was man für eine GUI App haben sollte
send # senden über Queue
do_receive # einen callback registrieren
undo_receive # einen callback deregistrieren

# alle callbacks für einen Eigentümer deregistrieren,
# etwa wenn man einen Frame löscht,
# für den mehrere callback Funktionen registriert sind
undo_receive_all

# direkter callback Aufruf,
# nur in wirklichen Ausnahmefällen evtl. erforderlich,
# etwa während destroy eines GUI Elementes
# mit Benachrichtigung an andere, wenn dabei die eigene Referenz mit übergeben wird
send_immediate

do_work # zum Triggern mit after für von extern (anderer Thread) übergebene Nachrichten
BlackJack

@Alfons Mittelmeyer: Die Frage ist ja was diese ganze Komplexität mit dem Problem zu tun hat das Du lösen willst, beziehungsweise steht ja immer noch die Frage im Raum was dieses Problem denn überhaupt ist.

Das die Namen alle reichtlich irreführend sind, hatten wir glaube ich anderer Stelle schon mal, das ist ja nicht das erste mal das dieser ”Proxy” von Dir gezeigt wird. Unter einem Proxy versteht man üblicherweise etwas anderes. Das was Du da machst nennt man üblicherweise Dispatcher, Channel, MessageBroker, Event, oder irgend etwas in der Art. Bei `do_receive()` würde man einen Handler erwarten der etwas empfängt. Und bei `undo_receive()` eine Methode die etwas Empfangenes rückgängig macht, was dann gleich ein Fragezeichen in den Raum stellt, weil das unmöglich oder zumindest komisch klingt.
Benutzeravatar
noisefloor
User
Beiträge: 4187
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

was mir immer noch nicht klar ist:

* Du sprichst von Nachrichten, hast aber auch das Beispiel der MP4-Datei. Warum geht's denn jetzt? Nachrichten (=Strings)? Oder Dateiobjekte? Oder beides?
* Du sprichst Teilsystemen, Empfängern etc.? Wo "leben" die denn? Alles auf dem gleichen Rechner? Im gleichen Netzwerk? Im Internet?
* Was machen die Tk-Frames, von denen du sprichst, genau? Und wie wird wann unter welche Umständen ein Frame gelöscht?
* Wo besteht der Zusammenhang zwischen Frame und Nachricht?

Außerdem beschleicht mich so das Gefühl, dass du evtl. "Signal and Slots" von Qt nachbauen willst oder gerade selber einen Message Broker baust. Letztere gibt es reichlich, auch in ausgereifter Form und OpenSource. Wie z.B. RabbitMQ, ZeroMQ oder das PubSub System von Redis.

Gruß, noisefloor
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

noisefloor hat geschrieben:Hallo,

was mir immer noch nicht klar ist:
* Du sprichst von Nachrichten, hast aber auch das Beispiel der MP4-Datei. Warum geht's denn jetzt? Nachrichten (=Strings)? Oder Dateiobjekte? Oder beides?
Eine Nachricht besteht aus einer ID und übermittelten Daten. Die ID kann zwecks guter Lesbarkeit ein String sein, mann kann natürlich auch andere Werte benützen. Bei den Daten können natürlich auch Dateinamen übergeben werden, es könnte aber auch die Bytefolge einer MP3 Datei sein.

noisefloor hat geschrieben: * Du sprichst Teilsystemen, Empfängern etc.? Wo "leben" die denn? Alles auf dem gleichen Rechner? Im gleichen Netzwerk? Im Internet?
Im einfachsten Fall iist es ein callback im eigenen Thread. Im zweiteinfachsten Fall sind es Teilsysteme in anderen Threads. Und der dritte Fall ist, daß die Nachrichten etwa über TCPIP an andere Prozesse auf demselben Rechner oder auf andere Rechner etwa über Intranet oder Internet weitergeleitet werden

noisefloor hat geschrieben: * Was machen die Tk-Frames, von denen du sprichst, genau? Und wie wird wann unter welche Umständen ein Frame gelöscht?
Tk-Frames sind tkinter GUI Elemente, also Widgets. Man benutzt sie um eine GUI Anwendung zu strukturieren, anstatt alles in das Hauptfenster zu klatschen. Sie sind eine rechteckige Fläche, auf der man andere Widgets anordnet. Die meisten Anwendungen sind statische Anwendungen und da löscht man normalerweise keine Widgets. Allerdings baue ich etwa in meinem GuiDesigner Inhalte auch dynamisch auf und zeige Informationen etwa durch Frames, die einen Label mit dem Namen einer Option und die Option selber zum Verändern in einem Edit Feld präsentieren. Diese Liste an Optionen ist nicht immer gleich für verschiedene Widget Typen, daher lösche ich bei Auswahl von etwas anderem alle Widgets dieser Anzeige und baue die Anzeige dann wieder neu auf unter Neuerzeugung der erforderlichen Widgets für die Anzeige. Es kommt also durchaus vor, daß man Widgets löscht, wenn auch nicht im gewohnten Normalfall

noisefloor hat geschrieben: * Wo besteht der Zusammenhang zwischen Frame und Nachricht?
Wenn man irgendo einen Button drückt, wird normalerweise eine Aktion ausgelöst, die oft auch das Erscheinen anderer GUI Elemente verändert. Für dieses andere GUI Element gibt es dann eine Funktion oder Methode, die während der callback Funktion des Buttons aufgerufen wird. Was aber geschieht, wenn dieses andere GUI Element nicht mehr existiert? Dann kommt es zum Crash, wenn man die Nachrichtemfang nicht deregistriert.

Was haben jetzt Nachrichten mit Frames zu tun? Es geht dabei um Methoden, die aufgerufen werden für die Widgets in diesem Frame. Normalerweise würde man die aufzurufenden Methoden für die Widgets der Containerklasse definieren, also etwa im Frame. Die Nachricht geht dann bei Verwendung des Proxys an eine Callback Methode im Frame. Damit ist der Frame sozusagen der Eigentümer der callback Methode somit auch der Eigentümer der registrierten zu empfangenen Nachricht. Daher übergibt man ihn bei "do_receive" als Eigentümer. Das self des Frames ist da die passende ID, gewiß ist die nur einmal vorhanden. Wenn eine callback Funktion nicht mehr aufgerufen werden soll, weil etwa die betreffenden Widgets nicht mehr da sind, kann man durch "undo_receive" den Nachrichtenempfang für die betreffende callback Methode deregistrieren.

Wenn nun der ganze Frame gelöscht wird, kann man durch "undo_receive_all' den Empfang aller Nachrichten für den Eigentümer - man benützt am Beste dessen self, mit einem einzigen Methodenaufruf deregistrieren. Das ist der Grund, wehalb man bei do_receive und undo_receive den Frame als Eigentümer mit angibt, anstatt sich eine sonstige ID auszudenken. Wenn man aber von diese Methode "undo_receive_all' nicht verwenden will, ist es egal was man bei "do_receive" und "undo_receive" als owner angibt, kann auch False oder irgend etwas sein.
noisefloor hat geschrieben:Außerdem beschleicht mich so das Gefühl, dass du evtl. "Signal and Slots" von Qt nachbauen willst oder gerade selber einen Message Broker baust. Letztere gibt es reichlich, auch in ausgereifter Form und OpenSource. Wie z.B. RabbitMQ, ZeroMQ oder das PubSub System von Redis.
Den Message Broker baue ich nicht gerade, sondern der steht seit 2015. Und Nachbau von Slots ist da gar, es ist nur eine allgemein übliche Methode, wie auch in einem Fahrzeug, Nachrichten über diverse Busse, wie CAN Bus, MOST Bus usw. zu versenden. Die sind dann noch weiter unterteilt, nämlich nicht nur eine ID, sondern zwei, den Funktionsblock und die Message ID. Man könnte das auch hier und statt etwa "LINKE_VORDERTÜR" schreiben "TUEREN_FENSTER_KLAPPEN.LINKE_VORDERTÜR". Das hat den Vorteil dass man dann, wenn man sich nach außen für "TUEREN_FENSTER_KLAPPEN" registriert, alle dazu gehörigen Messages bekommt. Das erleichert den Verwaltungsaufwand bei so einer enormen Menge an Nachrichten in einem Fahrzeug. Allerdings habe ich jetzt so eine Aufteilung nicht implementiert. Ja wenn man hundert verschiedene nachrichten von außen bräuchte und dann nur fünf Nachrichtenblöcke registrieren bräuchte, würde das Konfiguration sehr erleichtern. Es ginge aber auch hier. Dazu nimmt man als Message ID "TUEREN_FENSTER_KLAPPEN" und als erste Paremeter "LINKE_VORDERTÜR" und dann implementiert man einen Message Controller, welcher die "TUEREN_FENSTER_KLAPPEN" Messages in einen eigenen Proxy für "TUEREN_FENSTER_KLAPPEN" umschaufelt mit dem ersten Parameter als Message ID. Oder wenn die Message ID die ID für das Subsystem ist, an welches man senden will, erleichtert sich die Proxy und Weiterleitungs Konfiguration.

Gruß, Alfons
Antworten