चलिए पहले एक बात समझ लेते हैं। जो व्याख्या yield from gसमतुल्य है, for v in g: yield v वह भी न्याय करने के लिए शुरू नहीं होती है जो yield fromसब के बारे में है। क्योंकि, चलो इसका सामना करते हैं, यदि सभी लूप yield fromका विस्तार करते हैं for, तो यह yield fromभाषा को जोड़ने का वारंट नहीं करता है और पायथन 2.x में कार्यान्वित होने से नई सुविधाओं का एक पूरा गुच्छा तैयार करता है।
यह क्या yield fromकरता है यह कॉलर और उप-जनरेटर के बीच एक पारदर्शी द्विदिश कनेक्शन स्थापित करता है :
कनेक्शन "पारदर्शी" इस अर्थ में है कि यह सब कुछ सही ढंग से भी प्रचारित करेगा, न कि केवल उत्पन्न होने वाले तत्व (जैसे अपवाद प्रचारित हैं)।
कनेक्शन "द्विदिश" अर्थ में डेटा दोनों भेजा जा सकता है वह यह है कि से और करने के लिए एक जनरेटर।
( यदि हम टीसीपी के बारे में बात कर रहे थे, तो yield from gइसका मतलब हो सकता है "अब मेरे ग्राहक के सॉकेट को अस्थायी रूप से डिस्कनेक्ट करें और इसे अन्य सर्वर सॉकेट से फिर से कनेक्ट करें"। )
BTW, यदि आप सुनिश्चित नहीं हैं कि किसी जेनरेटर को डेटा भेजने का क्या मतलब है, तो आपको सबकुछ छोड़ना होगा और पहले कॉरटुइन के बारे में पढ़ना होगा - वे बहुत उपयोगी हैं ( सबरूटीन के विपरीत ), लेकिन दुर्भाग्य से पायथन में कम जाना जाता है। डेव बेज़ले का कूर्टिंस पर क्यूरियस कोर्स एक उत्कृष्ट शुरुआत है। त्वरित प्राइमर के लिए स्लाइड 24-33 पढ़ें ।
से उपज का उपयोग कर एक जनरेटर से डेटा पढ़ना
def reader():
"""A generator that fakes a read from a file, socket, etc."""
for i in range(4):
yield '<< %s' % i
def reader_wrapper(g):
# Manually iterate over data produced by reader
for v in g:
yield v
wrap = reader_wrapper(reader())
for i in wrap:
print(i)
# Result
<< 0
<< 1
<< 2
<< 3
इसके बजाय मैन्युअल रूप से पुनरावृत्ति करने के बजाय reader(), हम yield fromइसे कर सकते हैं।
def reader_wrapper(g):
yield from g
यह काम करता है, और हमने कोड की एक पंक्ति को समाप्त कर दिया। और शायद आशय थोड़ा स्पष्ट है (या नहीं)। लेकिन कुछ भी नहीं जीवन बदल रहा है।
1 से उपज का उपयोग करके एक जनरेटर (कोरटाइन) को डेटा भेजना
अब कुछ और दिलचस्प करते हैं। आइए एक कॉरआउट बनाते हैं, जो writerइसे भेजे गए डेटा को स्वीकार करता है और सॉकेट, एफडी आदि को लिखता है।
def writer():
"""A coroutine that writes data *sent* to it to fd, socket, etc."""
while True:
w = (yield)
print('>> ', w)
अब सवाल यह है कि रैपर फ़ंक्शन को लेखक को डेटा भेजने से कैसे निपटना चाहिए, ताकि रैपर को भेजे जाने वाले किसी भी डेटा को पारदर्शी रूप से भेजा जाए writer()?
def writer_wrapper(coro):
# TBD
pass
w = writer()
wrap = writer_wrapper(w)
wrap.send(None) # "prime" the coroutine
for i in range(4):
wrap.send(i)
# Expected result
>> 0
>> 1
>> 2
>> 3
आवरण को उस डेटा को स्वीकार करने की आवश्यकता होती है जो उसे (जाहिर है) भेजा जाता है और StopIterationजब लूप समाप्त हो जाता है तो उसे भी संभालना चाहिए । जाहिर है बस कर for x in coro: yield xनहीं होगा। यहां एक संस्करण है जो काम करता है।
def writer_wrapper(coro):
coro.send(None) # prime the coro
while True:
try:
x = (yield) # Capture the value that's sent
coro.send(x) # and pass it to the writer
except StopIteration:
pass
या, हम ऐसा कर सकते थे।
def writer_wrapper(coro):
yield from coro
यह कोड की 6 लाइनों को बचाता है, इसे बहुत अधिक पठनीय बनाता है और यह सिर्फ काम करता है। जादू!
जनरेटर उपज से डेटा भेजना - भाग 2 - अपवाद हैंडलिंग
इसे और जटिल बनाते हैं। क्या होगा यदि हमारे लेखक को अपवादों को संभालने की आवश्यकता है? मान लीजिए कि writerहैंडल एक है SpamExceptionऔर यह प्रिंट करता है ***अगर यह एक का सामना करता है।
class SpamException(Exception):
pass
def writer():
while True:
try:
w = (yield)
except SpamException:
print('***')
else:
print('>> ', w)
अगर हम नहीं बदले तो क्या होगा writer_wrapper? क्या यह काम करता है? कोशिश करते हैं
# writer_wrapper same as above
w = writer()
wrap = writer_wrapper(w)
wrap.send(None) # "prime" the coroutine
for i in [0, 1, 2, 'spam', 4]:
if i == 'spam':
wrap.throw(SpamException)
else:
wrap.send(i)
# Expected Result
>> 0
>> 1
>> 2
***
>> 4
# Actual Result
>> 0
>> 1
>> 2
Traceback (most recent call last):
... redacted ...
File ... in writer_wrapper
x = (yield)
__main__.SpamException
उम, यह काम नहीं कर रहा है क्योंकि x = (yield)सिर्फ अपवाद उठाता है और सब कुछ एक दुर्घटनाग्रस्त पड़ाव पर आता है। चलो इसे काम करते हैं, लेकिन मैन्युअल रूप से अपवादों को संभालना और उन्हें भेजना या उन्हें उप-जनरेटर में फेंकना ( writer)
def writer_wrapper(coro):
"""Works. Manually catches exceptions and throws them"""
coro.send(None) # prime the coro
while True:
try:
try:
x = (yield)
except Exception as e: # This catches the SpamException
coro.throw(e)
else:
coro.send(x)
except StopIteration:
pass
यह काम।
# Result
>> 0
>> 1
>> 2
***
>> 4
लेकिन ऐसा करता है!
def writer_wrapper(coro):
yield from coro
yield fromपारदर्शी रूप से हैंडल मान भेज रहा है या उप जनरेटर में मूल्यों फेंक।
यह अभी भी सभी कोने के मामलों को कवर नहीं करता है। बाहरी जनरेटर बंद होने पर क्या होता है? उस मामले के बारे में जब उप-जनरेटर एक मान लौटाता है (हाँ, पायथन 3.3+ में, जनरेटर मान लौटा सकता है), रिटर्न मान का प्रचार कैसे किया जाना चाहिए? यह yield fromपारदर्शी रूप से सभी कोने के मामलों को वास्तव में प्रभावशाली बनाता है ।yield fromबस जादुई काम करता है और उन सभी मामलों को संभालता है।
मुझे व्यक्तिगत रूप से लगता yield fromहै कि यह एक खराब कीवर्ड विकल्प है क्योंकि यह दो-तरफा प्रकृति को स्पष्ट नहीं करता है । अन्य खोजशब्द प्रस्तावित थे (जैसे delegateकि अस्वीकार कर दिए गए थे क्योंकि नए कीवर्ड को भाषा में जोड़ना मौजूदा लोगों के संयोजन की तुलना में बहुत अधिक कठिन है।
संक्षेप में, कॉल करने वाले और उप-जनरेटर के बीच के yield fromरूप में सोचना सबसे अच्छा है transparent two way channel।
संदर्भ:
- पीईपी 380 - एक उप-जनरेटर (ईविंग) [v3.3, 2009-02-13] को सौंपने के लिए सिंटैक्स
- PEP 342 - संवर्धित जनरेटरों (GvR, Eby) के माध्यम से कोरआउट्स [v2.5, 2005-05-10]