2017-08-26 30 views
25

मैं एक Connection उद्देश्य यह है कि पढ़ने के लिए होते हैं और asyncio कनेक्शन की धाराओं लिखने के लिए प्रयोग किया जाता है है:स्ट्रीम से उपज करने का सही तरीका क्या है?

class Connection(object): 

    def __init__(self, stream_in, stream_out): 
     object.__init__(self) 

     self.__in = stream_in 
     self.__out = stream_out 

    def read(self, n_bytes : int = -1): 
     return self.__in.read(n_bytes) 

    def write(self, bytes_ : bytes): 
     self.__out.write(bytes_) 
     yield from self.__out.drain() 

सर्वर साइड पर connected एक Connection वस्तु हर बार एक ग्राहक को जोड़ता है बनाता है, फिर 4 बाइट पढ़ता है।

@asyncio.coroutine 
def new_conection(stream_in, stream_out): 
    conn = Connection(stream_in, stream_out) 
    data = yield from conn.read(4) 
    print(data) 

और ग्राहक पक्ष पर, 4 बाइट लिखे गए हैं।

@asyncio.coroutine 
def client(loop): 
    ... 
    conn = Connection(stream_in, stream_out) 
    yield from conn.write(b'test') 

यह लगभग रूप में की उम्मीद काम करता है, लेकिन मैं yield from हर read और write कॉल करने के लिए है। मैं yield from की कोशिश की है Connection अंदर से ing:

def read(self, n_bytes : int = -1): 
    data = yield from self.__in.read(n_bytes) 
    return data 

लेकिन बजाय डेटा प्राप्त करने, मैं

<generator object StreamReader.read at 0x1109983b8> 

अगर मैं read और write फोन कई स्थानों से की तरह एक आउटपुट मिलता है, मैं नहीं करना पसंद करेंगे प्रत्येक बार yield from एस दोहराएं; बल्कि उन्हें Connection के अंदर रखते हुए। मेरे अंतिम लक्ष्य यह करने के लिए मेरे new_conection समारोह में कटौती कर रहा है:

@asyncio.coroutine 
def new_conection(stream_in, stream_out): 
    conn = Connection(stream_in, stream_out) 
    print(conn.read(4)) 
+0

आपको क्यों पैदा करना है? यदि आप conn.read (4) से उपज नहीं करते हैं, तो ऐसा लगता है कि यह बस बाइट ऑब्जेक्ट देता है। क्या आप यहां क्या खोज रहे हैं? – RageCage

+0

@RageCage: 'उपज' से उपज के बिना, 'conn.read (4)' अभी भी जनरेटर लौटाता है: '<जनरेटर ऑब्जेक्ट कनेक्शन 0x1019262b0 पर पढ़ें> –

+0

क्षमा करें मुझे स्पष्ट करना चाहिए था; यदि आप conn.read() (एकल पंक्ति संस्करण) के पहले पुनरावृत्ति से उपज नहीं करते हैं तो परिणाम क्या है? – RageCage

उत्तर

6

क्योंकि StreamReader.read is a coroutine, यह फोन करने के लिए अपने ही विकल्प हैं) एक Task या Future में यह लपेटकर और चल रहा है कि एक घटना पाश के माध्यम से, ख) await यह async def के साथ परिभाषित coroutine से ing, या ग) से इसके साथ yield from का उपयोग कर @asyncio.coroutine के साथ सजाए गए फ़ंक्शन के रूप में परिभाषित एक कोरआउटिन। event loops can't be started while they're already running:

Connection.read के बाद से एक घटना पाश से कहा जाता है (coroutine new_connection के माध्यम से) तो आपको उस घटना पाश एक Task या FutureStreamReader.read के लिए चलाने के लिए पुन: उपयोग नहीं कर सकते। आपको या तो stop the event loop (विनाशकारी और शायद सही तरीके से करने के लिए संभव नहीं है) या create a new event loop (गड़बड़ी और कोरआउट का उपयोग करने के उद्देश्य को हरा देना) होगा। उनमें से कोई भी वांछनीय नहीं है, इसलिए Connection.read को कोरआउट या async फ़ंक्शन होना चाहिए।

अन्य दो विकल्पों (एक async def coroutine में await या yield from एक @asyncio.coroutine -decorated समारोह में) ज्यादातर बराबर हैं। फर्क सिर्फ इतना है कि async def and await were added in Python 3.5 है, 3.4, yield from और @asyncio.coroutine उपयोग करने के लिए इतना ही एकमात्र विकल्प है (coroutines और asyncio 3.4 से पहले अस्तित्व में नहीं था, इसलिए अन्य संस्करणों अप्रासंगिक हैं)। निजी तौर पर, मैं async def और await का उपयोग करना पसंद करता हूं, क्योंकि async def के साथ कोरआउट को परिभाषित करना सजावटी के मुकाबले क्लीनर और स्पष्ट है।

संक्षेप में: Connection.read और new_connection coroutines (या तो डेकोरेटर या async कीवर्ड का उपयोग) है, और का उपयोग किया है await (या yield from) जब अन्य coroutines (new_connection में await conn.read(4), और await self.__in.read(n_bytes)Connection.read में) कॉल।

+1

आह, बहुत अच्छा जवाब Mego! यह स्पष्ट रूप से किसी ऐसे व्यक्ति द्वारा लिखा जाता है जो जानता है कि उनके बारे में क्या बात है। मैंने इसे पढ़ने से बहुत कुछ सीखा। +1 –

1

मैं लाइन पर StreamReader source code का एक हिस्सा मिला 620 वास्तव में समारोह के उपयोग का एक आदर्श उदाहरण है।

मेरे पिछले उत्तर में, मैंने इस तथ्य को अनदेखा किया कि self.__in.read(n_bytes) न केवल एक कोरआउट है (जिसे मुझे यह माना जाना चाहिए कि यह asyncio मॉड्यूल एक्सडी से था) लेकिन यह लाइन पर परिणाम उत्पन्न करता है। तो यह वास्तव में जनरेटर है, और आपको इससे उपज करने की आवश्यकता होगी।

स्रोत कोड से इस पाश उधार, अपने पढ़ने समारोह कुछ इस तरह दिखना चाहिए:

def read(self, n_bytes : int = -1): 
    data = bytearray() #or whatever object you are looking for 
    while 1: 
     block = yield from self.__in.read(n_bytes) 
     if not block: 
      break 
     data += block 
    return data 

क्योंकि self.__in.read(n_bytes) एक जनरेटर है, तो आप जब तक यह संकेत करने के लिए एक खाली परिणाम पैदावार से उपज के लिए जारी रखने के लिए है पढ़ने का अंत। अब आपके पढ़ने वाले फ़ंक्शन को जनरेटर के बजाय डेटा वापस करना चाहिए। आपको conn.read() के इस संस्करण से उपज नहीं करना पड़ेगा। एक

+0

फ़ंक्शन का उपयोग ठीक उसी तरह करते हुए, जैसा कि आपने इसे प्रदान किया है, मुझे अभी भी जेनरेटर ऑब्जेक्ट ('कनेक्शन.read') प्राप्त हो रहा है। –

+0

क्या आप अभी भी conn.read कॉल से उपज रहे हैं? डेटा प्रिंट करने का प्रयास करें और रीडिंग फ़ंक्शन में टाइप करें (डेटा) यह देखने के लिए कि यह लौटने से पहले क्या है। – RageCage

+0

नहीं, मैंने इसे हटा दिया और इसके बजाय 'data = conn.read (4)' कोशिश की। यह जनरेटर है। –

संबंधित मुद्दे