У меня есть программа python с двумя потоками (давайте назовите их "источник" и
'место назначения'). Исходная нить иногда отправляет сообщение в пункт назначения
поток с некоторыми аргументами. Чем поток назначения выбирает сообщение
должен вызвать соответствующую функцию с данными, сохраненными в сообщении.
Эта задача может быть решена несколькими способами. Легкий - это
'if... if..if' в цикле выбора сообщения потока назначения и вызова
функции в соответствии с принятым типом сообщения и сохраненными аргументами. Но это
приведет к огромному количеству кода (или большой таблицы поиска) и добавлению новых
функция сообщений/обработчиков будет генерировать дополнительный шаг для написания кода в
цикл выбора сообщения.
Так как python рассматривает функции как объекты первого класса и имеет кортежи, я хочу
поставить функцию и argumens внутри сообщения, а не поток назначения
выбирает сообщение, которое он просто вызывает functon, сохраненный в сообщении без каких-либо
знание, какая функция.
Я могу написать код для функций с указанным числом аргументов:
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"
Вопрос в том, как изменить код, чтобы я мог поставить() функцию с
любое количество аргументов в очереди? например HandleAnotherMsg()?
Использование q.put((HandleAnotherMsg, 1, 2, 3)) приведет к ошибке компиляции: (
Ответ 1
Так просто:
def DestinationThread( a ) :
while True :
items = q.get()
func = items[0]
args = items[1:]
func(*args)
Ответ 2
Еще один интересный вариант - просто передать в лямбда.
q.put(lambda: HandleMsg(1,2))
q.put(lambda: HandleAnother(8, "hello", extra="foo"))
def DestinationThread() :
while True :
f = q.get()
f()
Ответ 3
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"
Ответ 4
Я использовал аналогичную конструкцию раньше:
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])
Затем вы можете передать x в другой поток и называть его оттуда:
x() # returns the same as zip([0,1], [2,3], [4,5])
Ответ 5
Похоже, вы хотите использовать встроенный apply()
или его преемник:
def f(x. y):
print x+y
args = ( 1, 2 )
apply(f, args) # old way
f(*args) # new way
Ответ 6
Вы можете создать абстрактный класс сообщений с помощью метода run. Затем для каждой функции, которая должна быть передана через очередь, подкласс и реализовать функцию как метод запуска.
Посылающий поток создаст экземпляр соответствующего подкласса и поместит его в очередь. Получающий поток получит объект из очереди и слепо выполнит метод выполнения.
Обычно это называется шаблоном Command (Gamma и др.)
Пример:
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 action using the kwargs
Отправитель будет создавать экземпляр и отправлять сообщение:
queue.put(MessageOne(one='Eins', two='Deux'))
Получатель просто получает объект сообщения и выполняет его метод запуска (без необходимости... через доступные типы сообщений):
msg = queue.get()
msg.run()
Ответ 7
Почему вы не подклассы Queue?
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)
или, почему бы вам не поместить список?
list = [function_obj]
for arg in function_args:
list.append(arg)
queue.put(list)