প্রথমে একটি জিনিস বের করা যাক। যে ব্যাখ্যাটির yield from gসমতুল্য for v in g: yield v তা এমনকি যা ন্যায়বিচার তা yield fromকরা শুরু করে না । কারণ, আসুন আমরা এটির মুখোমুখি হই, যদি সমস্ত yield fromকিছু forলুপকে প্রসারিত করে , তবে এটি yield fromভাষায় যুক্ত হওয়ার নিশ্চয়তা দেয় না এবং পাইথন ২.x এ প্রয়োগ করা থেকে পুরো নতুন বৈশিষ্ট্যগুলিকে পুরোপুরি আটকাতে পারে।
এটি কী yield fromকরে এটি কলার এবং সাব-জেনারেটরের মধ্যে স্বচ্ছ দ্বিদ্বি সংযোগ স্থাপন করে :
সংযোগটি "স্বচ্ছ" এই অর্থে যে এটি সমস্ত কিছু সঠিকভাবে প্রচার করবে, কেবলমাত্র উপাদান তৈরি হচ্ছে না (যেমন ব্যতিক্রমগুলি প্রচারিত হয়)।
সংযোগ "দ্বিমুখী", এই অর্থে ডাটা উভয় পাঠানো যেতে পারে যে থেকে এবং থেকে জেনারেটরের।
( যদি আমরা টিসিপি নিয়ে কথা বলছিলাম, এর yield from gঅর্থ হতে পারে "এখন আমার ক্লায়েন্টের সকেট অস্থায়ীভাবে সংযোগ বিচ্ছিন্ন করুন এবং এটিকে অন্য সার্ভার সকেটে পুনরায় সংযুক্ত করুন" ))
BTW, যদি আপনি নিশ্চিত না কি জেনারেটরের থেকে ডেটা প্রেরণ , আপনি সবকিছু ড্রপ এবং প্রায় পড়া প্রয়োজন এমনকি উপায়ে coroutines প্রথম they're খুব দরকারী (তাদের বিপরীতে সাবরুটিনের ), কিন্তু দুর্ভাগ্যবশত পাইথন মধ্যে স্বল্প-পরিচিত। ডেভ বিজলির কৌতূহল কোর্স কর্টাইনগুলিতে একটি দুর্দান্ত শুরু। দ্রুত প্রাইমারের জন্য স্লাইডগুলি 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যা এতে প্রেরিত ডেটা গ্রহণ করে এবং একটি সকেট, এফডি ইত্যাদিতে লেখেন etc.
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স্বচ্ছভাবে হ্যান্ডলগুলি মান পাঠানো বা উপ-জেনারেটরের মধ্যে মান নিক্ষেপ।
এটি এখনও সমস্ত কোণার কেসটি কভার করে না। বাইরের জেনারেটর বন্ধ থাকলে কী হবে? সাব-জেনারেটর যখন মান দেয় (তবে হ্যাঁ, পাইথন ৩.৩++ তে জেনারেটর মান ফিরিয়ে দিতে পারে) সেই ক্ষেত্রে কী হবে, কীভাবে ফেরতের মান প্রচার করা উচিত? যে yield fromস্বচ্ছভাবে সমস্ত কোণার কেস পরিচালনা করে সত্যই চিত্তাকর্ষক ।yield fromকেবল ম্যাজিকালি কাজ করে এবং এই সমস্ত কেস পরিচালনা করে।
আমি ব্যক্তিগতভাবে মনে করি yield fromএকটি দুর্বল কীওয়ার্ড পছন্দ কারণ এটি দ্বিমুখী প্রকৃতিটিকে দৃশ্যমান করে না। প্রস্তাবিত অন্যান্য কীওয়ার্ড ছিল (যেমন delegateতবে প্রত্যাখ্যান করা হয়েছিল কারণ ভাষাতে একটি নতুন কীওয়ার্ড যুক্ত করা বিদ্যমান শব্দগুলির সংমিশ্রণের চেয়ে অনেক বেশি কঠিন।
সংক্ষেপে, এ yield fromহিসাবে ভাবাই ভালtransparent two way channel কলার এবং সাব-জেনারেটরের মধ্যে ।
তথ্যসূত্র:
- পিইপি 380 - উপ-জেনারেটর (ইউইং) -কে অর্পণ করার জন্য সিনট্যাক্স [v3.3, 2009-02-13]
- পিইপি 342 - বর্ধিত জেনারেটর (জিভিআর, এবি) এর মাধ্যমে কর্টিনগুলি [v2.5, 2005-05-10]