2014-02-19 62 views
4

(Python 3)Python - 我怎樣才能更簡潔地使用生成器?

我正在使用Python生成器從隊列中讀取消息。

消費者讀取隊列消息後,它需要能夠告訴生成器在成功處理隊列消息時刪除隊列消息。爲了將.send()發送到Python生成器,似乎我必須先將.send(None)發送給生成器。這使我的代碼比我認爲的應該更胖。

任何人都可以提出一種方式來使用qconsumer.py驅動發生器,使用更少的代碼行嗎?我已經確定了我希望消除的以下哪些行。

總之,如何讓下面的代碼更加緊湊,我如何刪除行的建議?下面

代碼爲qconsumer.py:下面

from qserver import Qserver 

myqserver = Qserver() 

myproducer = myqserver.producer() # trying to eliminate this line 
# first send to a generator must be None 
myproducer.send(None) # trying to eliminate this line 
for msg in myproducer: 
    # do something with message 
    print(msg) 
    if messageprocessok: 
     myproducer.send('delete') 

代碼爲qserver.py:

# -*- coding: utf-8 -*- 
import boto 
from boto.sqs.connection import SQSConnection 
from boto.sqs.message import Message 

QNAME = 'qinbound' 
SQSREGION = 'us-west-1' 

class Qserver(): 
    """A simple Q server.""" 

    def __init__(self, qname=None, sqsregion=None): 
     self.qname = qname or QNAME 
     self.sqsregion = sqsregion or SQSREGION 
     self.sqsconn = boto.sqs.connect_to_region(self.sqsregion) 
     self.q_in = self.sqsconn.get_queue(self.qname) 

    def producer(self): 
     while True: 
      qmessage = self.q_in.read(wait_time_seconds=20) 
      if qmessage is None: 
       continue 
      action = (yield qmessage.get_body()) 
      if action == 'delete': 
       # if processing completed ok, clear message from this queue 
       self.q_in.delete_message(qmessage) 
+3

您確定需要它嗎? 「無」是您可以發送給非啓動發電機的唯一東西,但您當然不需要。對'qconsumer.py'代碼(帶有更簡單的虛擬生成器)的簡單測試可以在不調用'myproducer.send(None)'的情況下正常運行。但是,'generator.send()'_returns_生成器的下一個值;你在'for'循環中迭代生成器並使用'myproducer.send'明確地拉取下一個值 - 你應該只使用其中一個。 – lanzz

+0

@lanzz嗯....聽起來像我的做法是錯誤的。 –

回答

1

已經明白你想要做什麼,我想我會避免與混合send迭代。具有myqserver類是迭代器本身似乎更有意義,對我說:

# -*- coding: utf-8 -*- 
import boto 
from boto.sqs.connection import SQSConnection 
from boto.sqs.message import Message 

QNAME = 'qinbound' 
SQSREGION = 'us-west-1' 

class Qserver(): 
    """A simple Q server.""" 
    _current_message = None 

    def __init__(self, qname=None, sqsregion=None): 
     self.qname = qname or QNAME 
     self.sqsregion = sqsregion or SQSREGION 
     self.sqsconn = boto.sqs.connect_to_region(self.sqsregion) 
     self.q_in = self.sqsconn.get_queue(self.qname) 

    def __iter__(self): 
     return self 

    def __next__(self): 
     while True: 
      qmessage = self.q_in.read(wait_time_seconds=20) 
      if qmessage is not None: 
       self._current_message = qmessage 
       return qmessage 

    next = __next__ 

    def delete_current(self): 
     if self._current_message is not None: 
      self.q_in.delete_message(self._current_message) 

和使用將是這樣的:

from qserver import Qserver 

myqserver = Qserver() 
for msg in myqserver: 
    # do something with message 
    print(msg) 
    if messageprocessok: 
     myqserver.delete_current() 
+0

看起來很有趣。你能否在推薦避免混合發送迭代時解釋你的想法?這樣做有什麼問題? –

+0

主要是因爲它讓事情更簡單。只需要使用數據(通過「發送」)或僅產生數據(通過產生)的生成器就會更容易理解和調試。如果你同時使用,你需要小心,因爲正如上面提到的那樣,'send'會導致生成器恢復執行,直到遇到下一個yield。如果下一個'yield'是一個語句而不是一個表達式,那麼調用'send'就會返回結果。如果你不小心,你會失去返回的價值。 – Sahand

+0

另請參見http://www.dabeaz.com/coroutines/Coroutines.pdf – Sahand

2

您當前的消費者扔掉消息,因爲每個send調用返回一個。你應該這樣做,而不是:

myqserver = Qserver() 
myproducer = myqserver.producer() 
messageprocessok = False 
while True: 
    msg = myproducer.send('delete' if messageprocessok else None) 
    # do something with message 
    print(msg) 

或者:

myqserver = Qserver() 
myproducer = myqserver.producer() 
msg = next(myproducer) 
while True: 
    # do something with message 
    print(msg) 
    msg = myproducer.send('delete' if messageprocessok else None) 

,你需要單獨調用Qserver()myqserver.producer()事實很簡單,因爲你做prouducer一個類的方法。或者,您可以使用獨立功能,或者創建一個簡單返回Qserver().producer()的包裝函數。這是獨立版本:

def producer(qname=None, sqsregion=None): 
    qname = qname or QNAME 
    sqsregion = sqsregion or SQSREGION 
    sqsconn = boto.sqs.connect_to_region(sqsregion) 
    q_in = sqsconn.get_queue(qname) 
    while True: 
     qmessage = q_in.read(wait_time_seconds=20) 
     if qmessage is None: 
      continue 
     action = (yield qmessage.get_body()) 
     if action == 'delete': 
      # if processing completed ok, clear message from this queue 
      q_in.delete_message(qmessage) 
+0

嗨@Janne。直到消息處理完成,我們纔會知道messageprocessok。這是否符合您的解決方案? –

+0

@DukeDougal第一個調用需要是'myproducer.send(None)'或者'next(myproducer)'。我正在初始化'messageprocessok = False'來完成這個任務。在第一次調用時,發生器從開始一直運行到產生第一條消息的時間點。 –

+0

寶貴的信息。我將另一個標記爲解決方案,因爲這就是我將要使用的方法,但您的方法似乎也不錯。 StackOverflow說只能有一個解決方案...... –