как поместить функцию и аргументы в очередь Python? - PullRequest
12 голосов
/ 26 марта 2009

У меня есть программа на Python с двумя потоками (назовем их 'source' и 'место назначения' ). Исходная ветка иногда публикует сообщение по назначению нить с некоторыми аргументами. Чем ветка назначения выбирает сообщение это должен вызвать соответствующую функцию с сохраненными в сообщении арументами.

Эта задача может быть решена несколькими способами. Самый простой - это поставить большой 'if ... if..if' в цикле выбора сообщений целевого потока и вызова функция в соответствии с типом полученного сообщения и сохраненными аргументами. Но это приведет к огромному количеству кода (или большой справочной таблицы) и добавлению нового Функция сообщения / обработчика будет развивать дополнительный шаг для написания кода в цикл выбора сообщений.

Поскольку python рассматривает функции как первоклассные объекты и имеет кортежи, я хочу поместить функцию и аргументы в сообщение, а не в целевую ветку выбирает сообщение, которое просто вызывает функцию, сохраненную в сообщении без знание, что это за функция.

Я могу написать код для функций с указанным количеством аргументов:

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)) вызовет ошибку компиляции: (

Ответы [ 7 ]

27 голосов
/ 26 марта 2009

Так просто:

def DestinationThread( a ) :
  while True :
    items = q.get()
    func = items[0]
    args = items[1:]
    func(*args)
12 голосов
/ 26 марта 2009

Еще один интересный вариант - просто перейти в лямбду.

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

def DestinationThread() :
   while True :
      f = q.get()
      f()
9 голосов
/ 26 марта 2009
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"
2 голосов
/ 26 марта 2009

Я использовал подобную конструкцию раньше:

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])
0 голосов
/ 26 марта 2009

Вы можете создать абстрактный класс сообщения с помощью метода run. Затем для каждой функции, которую необходимо передать через очередь, создайте подкласс и реализуйте функцию как метод run. Отправляющий поток создаст экземпляр соответствующего подкласса и поместит его в очередь. Принимающий поток получит объект из очереди и вслепую выполнит метод run.

Обычно это называется шаблоном Command (Gamma et al.)

Пример:

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

Отправитель создаст экземпляр и отправит сообщение:

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

Получатель просто получает объект сообщения и выполняет его метод run (без необходимости ... еще ... через доступные типы сообщений):

msg = queue.get()
msg.run()
0 голосов
/ 26 марта 2009

Звучит так, как будто вы хотите использовать apply() или его преемник:

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

args = ( 1, 2 )

apply(f, args)   # old way

f(*args)        # new way
0 голосов
/ 26 марта 2009

Почему бы вам не создать подкласс 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...