Как объединить сопроводительные файлы sender- и приемника?

Насколько я понял концепцию coroutine в Python, у вас могут быть два разных способа передачи данных (извините, я не мог найти или найти лучшие условия для них):

  1. На основе отправителя: каждая сопрограмма потребляет данные "снаружи" и отправляет их потребителю, например

    def coro(consumer):
     while True:
     item = yield
     consumer.send(process(item))

    Чтобы построить трубопроводы, можно было бы производить от внешней сопрограммы до внутренней:

    producer(filter(sink()))
  2. Основанный на приемнике: каждый сопроцессор потребляет данные из своего аргумента и дает его потребителю, например

    def coro(producer):
     while True:
     item = next(producer)
     yield process(item)

    Чтобы построить трубопроводы, можно было бы производить от внутренней к внешней сопрограмме, которая в конце концов выглядит больше, чего люди ожидают от функций:

    sink(filter(producer()))

Оба подхода имеют свои преимущества. С сопроводительными сообщениями, переданными на отправителя, я могу транслировать многие потребители

def broadcast(consumers):
 while True:
 item = yield
 for consumer in consumers:
 consumer.send(item)

Однако сопрограммы на основе отправителя всегда ограничены одним "вводным" сопрограммой, потому что они не могут отличить, кто их отправил (ну, на самом деле да, но это было бы противно). Это, с другой стороны, тривиально с сопрограммами на основе приемника:

def adder(producer1, producer2):
 while True:
 x = next(producer1)
 y = next(producer2)
 yield x + y

Теперь мой вопрос: есть ли разумный и простой способ унифицировать оба подхода? Например, передавая результат сумматора?

1 ответ

Я предполагаю, что можно сделать что-то вроде этого:

def adder(producer1, producer2, consumers):
 while True:
 x = next(producer1)
 y = next(producer2)
 for consumer in consumers:
 consumer.send(x+y)

а затем просто вызовите adder(x_producer, y_producer, consumers). Как видно из диаграммы:

сумматор не может быть помещен в середине трубопровода, и причина в том, что он должен иметь все ссылки на потребителей и производителей, что возможно, если мы будем называть сумматор на самом высоком уровне.

ОБНОВЛЕНИЕ: вот еще один подход, который делает сумматор генератором:

class AdderWithBroadcast(object):

 consumers = []

 def __init__(self, x_prod, y_prod):
 self.x_prod = x_prod
 self.y_prod = y_prod

 def __iter__(self):
 return self

 def next(self):
 x = next(self.x_prod)
 y = next(self.y_prod)

 for consumer in self.consumers:
 consumer.send(x+y)

 return x+y

 def consumer():
 while True:
 a = (yield)
 print a, ' in consumer'


k = iter(range(10))
adder = AdderWithBroadcast(k, k)
cons = consumer()
cons.send(None)
adder.consumers.append(cons)
for i in adder: 
 # I won't include the actual result here, you can try in no your own
 print i

Оформление декоратора:

class Broadcaster(object):

 consumers = []

 def __init__(self, gen):
 self.gen = gen

 def __iter__(self):
 return self

 def __call__(self, *args, **kwargs):
 self.gen = self.gen(*args, **kwargs)

 def next(self):
 yielded = next(self.gen)

 for consumer in self.consumers:
 consumer.send(yielded)

 return yielded


@Broadcaster
def adder(producer1, producer2):
 while True:
 x = next(producer1)
 y = next(producer2)
 yield x + y
# result is the same as in previous solution

licensed under cc by-sa 3.0 with attribution.