"ফলন" শব্দের দুটি অর্থ রয়েছে: কিছু উত্পাদন করা (উদাহরণস্বরূপ, ভুট্টা ফলন করা) এবং অন্য কাউকে / জিনিস চালিয়ে যেতে দেওয়া (যেমন, পথচারীদের জন্য ফলনকারী গাড়ি) থামানো। উভয় সংজ্ঞা পাইথনের ক্ষেত্রে প্রযোজ্যyield কীওয়ার্ডে ; যা জেনারেটর ফাংশনগুলিকে বিশেষ করে তোলে তা হ'ল নিয়মিত ফাংশনগুলির বিপরীতে, জেনারেটরের ক্রিয়াকলাপটি কেবল বিরতি দেওয়ার পরে, বন্ধ না করে, কলারে "ফিরে" দেওয়া যেতে পারে।
একটি "বাম" প্রান্ত এবং একটি "ডান" প্রান্তযুক্ত দ্বি নির্দেশমূলক পাইপের এক প্রান্ত হিসাবে কোনও জেনারেটর কল্পনা করা সবচেয়ে সহজ; এই পাইপটি এমন এক মাধ্যম যা মান নিজেই জেনারেটর এবং জেনারেটরের ক্রিয়াকলাপের শরীরের মধ্যে প্রেরণ করা হয়। পাইপের প্রতিটি প্রান্তে দুটি ক্রিয়াকলাপ রয়েছে: pushযা পাইপের অন্য প্রান্তটি মান টান না দেয় এবং কোনও কিছু না ফেরানো পর্যন্ত একটি মান এবং ব্লক প্রেরণ করে; এবংpull, যা পাইপের অন্য প্রান্ত পর্যন্ত কোনও মানকে ঠেলে না দেওয়া অবধি ব্লক করে এবং পুশ করা মানটি ফেরত দেয়। রানটাইমের সময়, এক্সিকিউশনটি পাইপের উভয় পাশের প্রসঙ্গগুলির মধ্যে পিছনে পিছনে বাউন্স করে - প্রতিটি পক্ষ চালিত হয় যতক্ষণ না এটি অন্য দিকে কোনও মান প্রেরণ করে, যে স্থানে এটি থামায়, অন্য পক্ষকে চালিত হতে দেয় এবং মানটির জন্য অপেক্ষা করে ফিরে আসুন, অন্যদিকে থামার সময় এটি আবার শুরু হয়। অন্য কথায়, পাইপের প্রতিটি প্রান্তটি যে মুহুর্তটি একটি মূল্য পাঠায় সেই মুহুর্তের সাথে এটি একটি মান গ্রহণ করে runs
পাইপ বৈশিষ্ট্যগুলি প্রতিসম, কিন্তু - কনভেনশন আমি এই উত্তর সংজ্ঞা করছি দ্বারা - বাম শেষ উত্পাদক ফাংশন লাশ ভিতরে শুধুমাত্র পাওয়া যায় এবং মাধ্যমে অ্যাক্সেসযোগ্য yieldযখন ডান শেষ, শব্দ হয় জেনারেটরের এবং মাধ্যমে অ্যাক্সেসযোগ্য জেনারেটরের sendফাংশন পাইপটির নিজ নিজ প্রান্তে একক ইন্টারফেস হিসাবে yieldএবং sendডাবল ডিউটি করে: তারা উভয়ই পাইপের তাদের প্রান্তে / থেকে মানগুলি yieldটানিয়ে sendদেয় এবং বিপরীত অবস্থায় ডানদিকে ঠেলে এবং বাম দিকে টান দেয়। এই ডাবল ডিউটি হল বিবৃতিগুলির শব্দার্থবিজ্ঞানকে ঘিরে বিভ্রান্তির শিরা x = yield y। দুটি স্পষ্ট ধাক্কা / টানা পদক্ষেপগুলিকে ভাঙ্গা yieldএবং sendনীচে নামানো তাদের শব্দার্থকগুলিকে আরও স্পষ্ট করে তুলবে:
- মনে করুন
gজেনারেটর। g.sendপাইপের ডান প্রান্ত দিয়ে একটি মান বাম দিকে ঠেলে দেয়।
gবিরতি প্রসঙ্গে জেনারেটর ফাংশন এর শরীর চালানোর অনুমতি প্রসঙ্গে বাস্তবায়ন ।
- দ্বারা ধাক্কা দেওয়া মানটি
g.sendবাম দিকে টেনে yieldনিয়ে যায় এবং পাইপের বাম প্রান্তে প্রাপ্ত হয়। ইন x = yield y, xটানা মান নির্ধারিত হয়।
- জেনারেটরের ক্রিয়াকলাপের মূল অংশের মধ্যে পরবর্তী লাইনটি উপস্থিত না হওয়া পর্যন্ত সম্পাদন অব্যাহত থাকে
yield।
yieldপাইপের বাম প্রান্তটি দিয়ে পিছনে পিছনে একটি মান ডানদিকে ঠেলে দেয় g.send। ইন x = yield y, yপাইপ মাধ্যমে ডানদিকে ধাক্কা হয়।
- জেনারেটর ফাংশনটির দেহের মধ্যে সম্পাদন বিরতি দেয়, বাহ্যিক স্কোপটি যেখানে ছেড়ে যায় সেখানে চালিয়ে যেতে দেয়।
g.send পুনরায় শুরু করে এবং মানটি টান দেয় এবং ব্যবহারকারীর কাছে ফেরত দেয়।
g.sendএরপরে যখন ডাকা হবে তখন পদক্ষেপ 1 এ ফিরে যান।
চক্রাকারে, এই পদ্ধতির একটি শুরু আছে: কখন g.send(None)- যা next(g)স্বল্প - এটি প্রথম বলা হয় ( Noneপ্রথম sendকল ব্যতীত অন্য কিছু পাস করা অবৈধ )। এবং এর শেষ হতে পারে: যখন yieldজেনারেটর ফাংশনটির শরীরে আর কোনও বিবৃতি পৌঁছানো যায় না।
আপনি কী দেখতে পাচ্ছেন যে yieldবিবৃতিটি (বা আরও সঠিকভাবে, জেনারেটর) এত বিশেষ করে তোলে ? পরিমাপকৃত returnকীওয়ার্ডের বিপরীতে , yieldতার কলারের কাছে মানগুলি সঞ্চার করতে সক্ষম এবং এতে বাসকারী ফাংশনটি শেষ না করেই তার কলারের কাছ থেকে মানগুলি গ্রহণ করতে সক্ষম! (অবশ্যই, আপনি যদি কোনও ফাংশন - বা একটি জেনারেটর বন্ধ করতে চান return) তবে কীওয়ার্ডটিও রাখা সহজ ।) যখন কোনও yieldবিবৃতি আসে তখন জেনারেটর ফাংশনটি কেবল বিরতি দেয় এবং তারপরে যেখানে বাম দিকে ডানদিকে উঠে যায় p অন্য মান প্রেরণ করা বন্ধ। এবং sendএটি বাইরে থেকে একটি জেনারেটরের ফাংশনের অভ্যন্তরের সাথে যোগাযোগের জন্য কেবল ইন্টারফেস।
আমরা সত্যিই নিচে যতটা আমরা করতে পারেন হিসাবে এই ধাক্কা / পুল / পাইপ উপমা বিরতি চান, আমরা নিম্নলিখিত pseudocode হয় যে সত্যিই বাড়িতে ড্রাইভ দিয়ে শেষ যে সরাইয়া পদক্ষেপ 1-5, থেকে yieldএবং sendএকই দুই পক্ষের হয় মুদ্রা নল:
right_end.push(None) # the first half of g.send; sending None is what starts a generator
right_end.pause()
left_end.start()
initial_value = left_end.pull()
if initial_value is not None: raise TypeError("can't send non-None value to a just-started generator")
left_end.do_stuff()
left_end.push(y) # the first half of yield
left_end.pause()
right_end.resume()
value1 = right_end.pull() # the second half of g.send
right_end.do_stuff()
right_end.push(value2) # the first half of g.send (again, but with a different value)
right_end.pause()
left_end.resume()
x = left_end.pull() # the second half of yield
goto 6
কী রূপান্তর যে আমরা বিভক্ত হয় x = yield yএবং value1 = g.send(value2)দুই বিবৃতি প্রতিটি: left_end.push(y)এবং x = left_end.pull(); এবং value1 = right_end.pull()এবং right_end.push(value2)। yieldকীওয়ার্ডের দুটি বিশেষ ক্ষেত্রে রয়েছে : x = yieldএবং yield y। এগুলি যথাক্রমে সিনট্যাকটিক চিনি x = yield Noneএবং এর জন্য _ = yield y # discarding value।
পাইপের মাধ্যমে মানগুলি প্রেরণ করা হয় সেই নির্দিষ্ট ক্রম সম্পর্কিত সুনির্দিষ্ট বিশদের জন্য নীচে দেখুন।
নিম্নলিখিতটি নীচেরটির পরিবর্তে দীর্ঘ কংক্রিটের মডেল। প্রথমত, এটি প্রথমে লক্ষ করা উচিত যে কোনও জেনারেটরের জন্য g, next(g)এটি ঠিক সমান g.send(None)। এটি মাথায় রেখে আমরা কীভাবে sendকাজ করে কেবল জেনারেটরকে এগিয়ে নিয়ে যাওয়ার বিষয়ে আলোচনা করতে পারি send।
ধরুন আমাদের আছে
def f(y): # This is the "generator function" referenced above
while True:
x = yield y
y = x
g = f(1)
g.send(None) # yields 1
g.send(2) # yields 2
এখন, fনিম্নলিখিত সাধারণ (নন-জেনারেটর) ফাংশনের পক্ষে মোটামুটি দেশুগারের সংজ্ঞা :
def f(y):
bidirectional_pipe = BidirectionalPipe()
left_end = bidirectional_pipe.left_end
right_end = bidirectional_pipe.right_end
def impl():
initial_value = left_end.pull()
if initial_value is not None:
raise TypeError(
"can't send non-None value to a just-started generator"
)
while True:
left_end.push(y)
x = left_end.pull()
y = x
def send(value):
right_end.push(value)
return right_end.pull()
right_end.send = send
# This isn't real Python; normally, returning exits the function. But
# pretend that it's possible to return a value from a function and then
# continue execution -- this is exactly the problem that generators were
# designed to solve!
return right_end
impl()
নিম্নলিখিত এই রূপান্তর ঘটেছে f:
- আমরা প্রয়োগটিকে একটি নেস্টেড ফাংশনে স্থানান্তরিত করেছি।
- আমরা একটি দ্বিপাক্ষিক পাইপ তৈরি করেছি যার
left_endনেস্ট ফাংশন দ্বারা অ্যাক্সেস করা right_endহবে এবং যার ফিরিয়ে দেওয়া হবে এবং বাইরের স্কোপ দ্বারা অ্যাক্সেস করা হবে - right_endএটি আমরা জেনারেটর অবজেক্ট হিসাবে জানি।
- নেস্টেড ফাংশন মধ্যে, প্রথম জিনিস আমরা চেক করে
left_end.pull()হয় None, গ্রাসকারী প্রক্রিয়ায় ধাক্কা মান।
- নেস্টেড ক্রিয়াকলাপের মধ্যে, বিবৃতিটি
x = yield yদুটি লাইন দ্বারা প্রতিস্থাপন করা হয়েছে: left_end.push(y)এবং x = left_end.pull()।
- আমরা
sendফাংশনটি সংজ্ঞায়িত করেছি right_end, যা x = yield yপূর্ববর্তী পদক্ষেপের সাথে আমরা বিবৃতিটি প্রতিস্থাপন করেছি দুটি রেখার প্রতিরূপ ।
এই কল্পনার জগতে যেখানে ফাংশনগুলি ফিরে আসার পরেও চালিয়ে যেতে পারে, সেট gকরা হয় right_endএবং তারপরে impl()তাকে ডাকা হয়। সুতরাং উপরোক্ত আমাদের উদাহরণে, আমরা যদি ফাঁকে ফাঁকে ফাঁকে ফাঁকে ফাঁকে ফাঁকে ফাঁকে ফাঁকে ফাঁকে ফাঁকে ফেলা হয়, তবে কি ঘটবে তা মোটামুটি নিম্নরূপ:
left_end = bidirectional_pipe.left_end
right_end = bidirectional_pipe.right_end
y = 1 # from g = f(1)
# None pushed by first half of g.send(None)
right_end.push(None)
# The above push blocks, so the outer scope halts and lets `f` run until
# *it* blocks
# Receive the pushed value, None
initial_value = left_end.pull()
if initial_value is not None: # ok, `g` sent None
raise TypeError(
"can't send non-None value to a just-started generator"
)
left_end.push(y)
# The above line blocks, so `f` pauses and g.send picks up where it left off
# y, aka 1, is pulled by right_end and returned by `g.send(None)`
right_end.pull()
# Rinse and repeat
# 2 pushed by first half of g.send(2)
right_end.push(2)
# Once again the above blocks, so g.send (the outer scope) halts and `f` resumes
# Receive the pushed value, 2
x = left_end.pull()
y = x # y == x == 2
left_end.push(y)
# The above line blocks, so `f` pauses and g.send(2) picks up where it left off
# y, aka 2, is pulled by right_end and returned to the outer scope
right_end.pull()
x = left_end.pull()
# blocks until the next call to g.send
এই উপরের 16-পদক্ষেপ সিউডোকোড ঠিক এই মানচিত্র।
অন্যান্য কিছু বিবরণ রয়েছে, যেমন ত্রুটিগুলি কীভাবে প্রচার করা হয় এবং আপনি জেনারেটরের শেষের দিকে পৌঁছলে কী হয় (পাইপটি বন্ধ রয়েছে), তবে এটি কীভাবে বুনিয়াদি নিয়ন্ত্রণ প্রবাহ sendব্যবহৃত হয় তা কীভাবে কাজ করবে তা স্পষ্ট করে দেওয়া উচিত ।
এই একই বিলোপকারী বিধিগুলি ব্যবহার করে আসুন দুটি বিশেষ কেস দেখি:
def f1(x):
while True:
x = yield x
def f2(): # No parameter
while True:
x = yield x
বেশিরভাগ অংশে তারা একইভাবে দেশুগার করে f, কেবলমাত্র পার্থক্যগুলি কীভাবে yieldবিবৃতিতে রূপান্তরিত হয়:
def f1(x):
# ... set up pipe
def impl():
# ... check that initial sent value is None
while True:
left_end.push(x)
x = left_end.pull()
# ... set up right_end
def f2():
# ... set up pipe
def impl():
# ... check that initial sent value is None
while True:
left_end.push(x)
x = left_end.pull()
# ... set up right_end
প্রথমটিতে, পাস করা মান f1প্রাথমিকভাবে ধাক্কা দেওয়া হয় (ফলন দেওয়া হয়), এবং তারপরে সমস্ত পাঠানো (প্রেরিত) ডানদিকে ধাক্কা দেওয়া হয় (ফলিত)। দ্বিতীয়টিতে xএর কোনও মান (এখনও) নেই যখন এটি প্রথমবার আসে push, সুতরাং একটি UnboundLocalErrorউত্থাপিত হয়।