2009-03-25 8 views
10

Ich habe ein Python-Programm mit 2 Threads (nennen wir sie 'source' und 'Ziel'). Quellenthread posten manchmal eine Nachricht an Ziel Thread mit einigen Argumenten. Als Ziel Thread wählt eine Nachricht es muss eine entsprechende Funktion mit Argumenten in der Nachricht gespeichert aufrufen.Wie eine Funktion und Argumente in Python-Warteschlange?

Diese Aufgabe kann auf mehrere Arten gelöst werden. Die einfache ist eine große 'if ... if..if' im Nachrichten-Pick-Zyklus des Ziel-Thread und Anruf Funktion nach empfangenen Nachrichtentyp und gespeicherten Argumente. Aber diese wird in riesigen amounf Code (oder große Nachschlagetabelle) führen und das Hinzufügen neuer Nachrichten/Handler-Funktion wird zusätzlichen Schritt entwickeln, um Code in Nachrichtenauswahlzyklus zu schreiben.

Da Python behandelt Funktionen als First-Class-Objekte und haben Tupel, ich will eine Funktion und argumens innerhalb einer Nachricht setzen, so als Ziel Thread eine Nachricht nimmt es nennen nur eine functon innerhalb einer Nachricht ohne gespeichert Wissen, welche Funktion es ist.

I einen Code für einen Funktion mit bestimmten Anzahl von Argumenten schreiben:

from Queue import * 
from thread import * 
from time import * 

q = Queue() 

def HandleMsg(arg1, arg2) : 
    print arg1, arg2 

def HandleAnotherMsg(arg1, arg2, arg3) : 
    print arg1, arg2, arg3 

def DestinationThread(a) : 
    while True : 
    (f, a, b) = q.get() 
    f(a, b) 

start_new_thread(DestinationThread, (0,)) 
print "start" 
sleep(1) 
q.put((HandleMsg, 1, 2)) 
sleep(1) 
print "stop" 

Die Frage ist, wie ein Code so modifizieren, kann ich eine Funktion mit in Warteschlange beliebige Anzahl von Argumenten put() ? zum Beispiel HandleAnotherMsg()? Mit q.put ((HandleAnotherMsg, 1, 2, 3)) einen Übersetzungsfehler steigen :(

+0

Können Sie den Fehler posten, den Sie bekommen? –

Antwort

24

So einfach:?

def DestinationThread(a) : 
    while True : 
    items = q.get() 
    func = items[0] 
    args = items[1:] 
    func(*args) 
-2

Warum nicht Queue Unterklasse?


class MyQueue(Queue): 
    # by using *args, you can have a variable number of arguments 
    def put(self,*args): 
    for arg in args: 
     Queue.put(self,arg) 

oder, warum Sie nicht setzen eine Liste


list = [function_obj] 
for arg in function_args: 
    list.append(arg) 
queue.put(list) 
+0

Würde wer diesen Gedanken posten warum? –

+0

Es beantwortet die Frage nicht. Jedes Element in der Warteschlange muss eine Funktion und eine unbekannte Anzahl von Argumenten codieren. Es ist nicht hilfreich, jedes Argument als separaten Wert in die Warteschlange zu schieben. – cthulahoops

+0

Eigentlich ist das zweite Code-Snippet relevant - die obigen sind jedoch nützlicher. – cthulahoops

8
from Queue import * 
from thread import * 
from time import * 

q = Queue() 

def HandleMsg(arg1, arg2) : 
    print arg1, arg2 

def HandleAnotherMsg(arg1, arg2, arg3) : 
    print arg1, arg2, arg3 

def DestinationThread() : 
    while True : 
    f, args = q.get() 
    f(*args) 

start_new_thread(DestinationThread, tuple()) 
print "start" 
sleep(1) 
q.put((HandleMsg, [1, 2])) 
sleep(1) 
q.put((HandleAnotherMsg, [1, 2, 3])) 
sleep(1) 
print "stop" 
0

Es klingt wie Sie den apply() intrinsischen oder seinen Nachfolger verwenden mögen:

def f(x. y): 
    print x+y 

args = (1, 2) 

apply(f, args) # old way 

f(*args)  # new way 
2

ich verwendet habe ein ähnliches Konstrukt vor:

class Call: 
    def __init__(self, fn, *args, **kwargs): 
     self.fn = fn 
     self.args = args 
     self.kwargs = kwargs 

    def __call__(self): 
     return self.fn(*self.args, **self.kwargs) 


x = Call(zip, [0,1], [2,3], [4,5]) 

Sie sollten dann x zu Ihrem anderen Thread übergeben werden können, und von dort aus rufen:

x() # returns the same as zip([0,1], [2,3], [4,5]) 
0

Sie können eine abstrakte Nachrichtenklasse mit einer run-Methode erstellen. Dann für jede Funktion, die über die Warteschlange übertragen werden muss, Unterklasse und implementieren Sie die Funktion als Run-Methode. Der sendende Thread erstellt eine Instanz der richtigen Unterklasse und speichert sie in der Warteschlange. Der empfangende Thread erhält ein Objekt aus der Warteschlange und führt blind die run-Methode aus.

Dies wird normalerweise als das Befehlsmuster bezeichnet (Gamma et al.)

Beispiel:

class Message (object): 
    """abstract message class""" 
    def __init__(self, **kwargs): 
     self.kwargs = kwargs 

    def run(self): 
     pass 


class MessageOne (Message): 
    """one message class""" 
    def run(self): 
     # perform this emssage's action using the kwargs 

Der Absender wird instanziiert und eine Nachricht senden:

queue.put(MessageOne(one='Eins', two='Deux')) 

Der Empfänger einfach ein Nachrichtenobjekt bekommt und ausführen Methode ausgeführt (ohne it..else mit .. durch die verfügbaren Arten von Nachrichten):

msg = queue.get() 
msg.run() 
11

Eine weitere interessante Option ist simpl y, um Lambda zu übergeben.

q.put(lambda: HandleMsg(1,2)) 
q.put(lambda: HandleAnother(8, "hello", extra="foo")) 

def DestinationThread() : 
    while True : 
     f = q.get() 
     f() 
Verwandte Themen