Как поместить функцию и аргументы в очередь python?

У меня есть программа 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)