অজগর জেনারেটর "প্রেরণ" ফাংশন উদ্দেশ্য?


163

পাইথন জেনারেটর ফাংশনের সাথে যুক্ত "প্রেরণ" ফাংশন কেন বিদ্যমান তার কোনও উদাহরণ আমাকে দিতে পারেন? আমি পুরোপুরি ফলন কার্যকারিতা বুঝতে পারি। তবে পাঠানোর কাজটি আমার কাছে বিভ্রান্ত করছে conf এই পদ্ধতির ডকুমেন্টেশন গুলিয়ে দেয়:

generator.send(value)

এক্সিকিউশনটি পুনরায় শুরু করে এবং জেনারেটর ফাংশনে একটি মান "প্রেরণ" করে। মান আর্গুমেন্ট বর্তমান ফলন প্রকাশের ফলাফল হয়ে যায়। প্রেরণ () পদ্ধতিটি জেনারেটরের দ্বারা উত্পাদিত পরবর্তী মানটি প্রদান করে বা জেনারেটর যদি অন্য কোনও মান না দিয়ে প্রস্থান করে তবে স্টপআইটারেশন উত্থাপন করে।

ওটার মানে কি? আমি ভেবেছিলাম মানটি কি ফাংশনের ইনপুট? "প্রেরণ () পদ্ধতিটি জেনারেটরের দ্বারা উত্পাদিত পরবর্তী মূল্য প্রদান করে" এই বাক্যটি ফলন কার্যকারিতার সঠিক উদ্দেশ্য বলে মনে হয়; উত্পাদক জেনারেটর দ্বারা উত্পাদিত পরবর্তী মান প্রদান করে ...

কেউ কি আমাকে এমন কোনও জেনারেটর প্রেরণ ব্যবহারের একটি উদাহরণ দিতে পারে যা এমন কিছু ফলন অর্জন করতে পারে না যা পূরণ করতে পারে?



3
অন্য বাস্তব জীবনে উদাহরণঃ (এফটিপি থেকে পড়া) যোগ করা হয়েছে যখন callbacks জেনারেটরের মধ্যে প্রমাণিত হয় ভিতরে থেকে ব্যবহার করা
জানুয়ারী Vlcinsky

2
এটি উল্লেখযোগ্য যে "যখন send()জেনারেটরটি শুরু করার জন্য বলা হয়, তখন এটি অবশ্যই Noneযুক্তি হিসাবে ডেকে আনতে হবে, কারণ কোনও ফলন প্রকাশ নেই যা মূল্য পেতে পারে" ", সরকারী ডক থেকে উদ্ধৃত এবং প্রশ্নটির উদ্ধৃতিটি হ'ল অনুপস্থিত।
রিক

উত্তর:


146

এটি কেবল উত্পাদিত কোনও জেনারেটরে মান প্রেরণে ব্যবহৃত হয়। এখানে একটি কৃত্রিম (অ-দরকারী) ব্যাখ্যামূলক উদাহরণ রয়েছে:

>>> def double_inputs():
...     while True:
...         x = yield
...         yield x * 2
...
>>> gen = double_inputs()
>>> next(gen)       # run up to the first yield
>>> gen.send(10)    # goes into 'x' variable
20
>>> next(gen)       # run up to the next yield
>>> gen.send(6)     # goes into 'x' again
12
>>> next(gen)       # run up to the next yield
>>> gen.send(94.3)  # goes into 'x' again
188.5999999999999

আপনি কেবল এটি দিয়ে করতে পারবেন না yield

কেন এটি কার্যকর, আপনি যে সর্বোত্তম ব্যবহারের ঘটনা দেখেছি তা হ'ল টুইস্টেড @defer.inlineCallbacks। মূলত এটি আপনাকে এই জাতীয় ক্রিয়া লিখতে দেয়:

@defer.inlineCallbacks
def doStuff():
    result = yield takesTwoSeconds()
    nextResult = yield takesTenSeconds(result * 10)
    defer.returnValue(nextResult / 10)

কি হবে যে takesTwoSeconds()আয় একটি Deferred, যা একটি মান প্রতিশ্রুতি একটি মান পরে নির্ণিত করা হবে না। ট্যুইস্ট করা অন্য থ্রেডে গণনা চালাতে পারে। গণনাটি সম্পন্ন হয়ে গেলে, এটি এটিকে পিছিয়ে যায় এবং এর পরে মানটি doStuff()ফাংশনে ফিরে আসে । এটি doStuff()সাধারণ প্রক্রিয়াজাতীয় ফাংশনটির মতো কম-বেশি দেখতে শেষ করতে পারে, তা বাদে এটি সমস্ত ধরণের গণনা এবং কলব্যাক ইত্যাদি করা যায় না this

def doStuff():
    returnDeferred = defer.Deferred()
    def gotNextResult(nextResult):
        returnDeferred.callback(nextResult / 10)
    def gotResult(result):
        takesTenSeconds(result * 10).addCallback(gotNextResult)
    takesTwoSeconds().addCallback(gotResult)
    return returnDeferred

এটি অনেক বেশি সংশ্লেষিত এবং অস্বাস্থ্যকর।


2
আপনি কি ব্যাখ্যা করতে পারবেন এর উদ্দেশ্য কী? কেন এটি ডাবল ইনপুট (শুরুর সংখ্যা) এবং ফলন দিয়ে পুনরায় তৈরি করা যায় না?
টমি

@ টমি: ওহ কারণ আপনি যে মানগুলি পেয়েছেন তার আগের কোনওটির সাথে কোনও সম্পর্ক নেই। আমাকে উদাহরণটি পরিবর্তন করতে দিন
ক্লডিউ

আপনি কেন তখন এটি এমন কোনও সাধারণ ফাংশন ব্যবহার করবেন যা এর ইনপুট দ্বিগুণ করবে ??
টমি

4
@ টমি: আপনি না। প্রথম উদাহরণটি কেবল এটি কী করে তা ব্যাখ্যা করার জন্য। দ্বিতীয় উদাহরণটি আসলে কার্যকর ব্যবহারের ক্ষেত্রে।
ক্লদিউ

1
@ টমি: আমি যদি বলি আপনি যদি সত্যিই এই উপস্থাপনাটি পরীক্ষা করে দেখতে চান এবং এটির মাধ্যমে কাজ করতে চান। একটি সংক্ষিপ্ত উত্তর যথেষ্ট হবে না কারণ আপনি কেবল "" তবে আমি কি এটির মতো এটি করতে পারি না? " ইত্যাদি
ক্লাদিউ

96

এই ফাংশনটি কর্টাইন লিখতে হয়

def coroutine():
    for i in range(1, 10):
        print("From generator {}".format((yield i)))
c = coroutine()
c.send(None)
try:
    while True:
        print("From user {}".format(c.send(1)))
except StopIteration: pass

কপি করে প্রিন্ট

From generator 1
From user 2
From generator 1
From user 3
From generator 1
From user 4
...

দেখুন কীভাবে নিয়ন্ত্রণটি পিছন পিছন পাশ করা হচ্ছে? এগুলি হল কর্টিনস। এগুলি সমস্ত ধরণের শীতল জিনিসের মতো ব্যবহার করা যেতে পারে যেমন অ্যাসিঞ্চ আইও এবং অনুরূপ।

জেনারেটর এবং কোনও প্রেরণ সহ এটিকে এভাবে ভাবুন, এটি একতরফা রাস্তা

==========       yield      ========
Generator |   ------------> | User |
==========                  ========

তবে পাঠানোর সাথে সাথে এটি দ্বিপথের রাস্তায় পরিণত হয়

==========       yield       ========
Generator |   ------------>  | User |
==========    <------------  ========
                  send

যা ফ্লাইতে জেনারেটরদের আচরণ এবং ব্যবহারকারীকে জেনারেটর জেনারেটর ব্যবহার করে কাস্টমাইজ করে ব্যবহারকারীর দ্বার উন্মুক্ত করে ।


3
তবে একটি জেনারেটরের ফাংশন প্যারামিটার নিতে পারে। "প্রেরণ" কীভাবে জেনারেটরে একটি পরামিতি প্রেরণের বাইরে চলে যায়?
টমি

13
@ টমি কারণ আপনি জেনারেটরটি চালুর সাথে সাথে পরামিতিগুলি পরিবর্তন করতে পারবেন না। আপনি এটি পরামিতি দিন, এটি সঞ্চালিত, সম্পন্ন। প্রেরণের সাথে সাথে, আপনি এটি প্যারামিটারগুলি দেন, এটি কিছুক্ষণের জন্য চলতে থাকে, আপনি এটির একটি মূল্য পাঠান এবং এটি কিছু আলাদা করে, পুনরাবৃত্তি করুন
ড্যানিয়েল গ্রেটজার

2
@ টমি এটি জেনারেটরটি পুনরায় চালু করবে, যার ফলে আপনি প্রচুর কাজ আবার করতে পারবেন
ড্যানিয়েল গ্রেটজার

5
আপনি কি দয়া করে সব কিছুর আগে কোনও পাঠানোর উদ্দেশ্য ব্যাখ্যা করতে পারেন?
শুভম আগরওয়াল

2
@ শুভগম আগরওয়াল জেনারেটরটি 'স্টার্ট' করার জন্য করা হয়। এটি করা দরকার এমন কিছু। আপনি যখন প্রথমবার send()জেনারেটরকে কল করেন তখন থেকেই আপনি কীওয়ার্ডটিতে পৌঁছেছেন না সে সম্পর্কে আপনি কিছু ভাবছেন yield
মাইকেল 3

50

এটি কারও সাহায্য করতে পারে। এখানে এমন একটি জেনারেটর রয়েছে যা প্রেরণ ফাংশন দ্বারা প্রভাবিত হয় না। এটি ইনস্ট্যান্টেশনে সংখ্যার প্যারামিটার গ্রহণ করে এবং প্রেরণ দ্বারা প্রভাবিত হয় না:

>>> def double_number(number):
...     while True:
...         number *=2 
...         yield number
... 
>>> c = double_number(4)
>>> c.send(None)
8
>>> c.next()
16
>>> c.next()
32
>>> c.send(8)
64
>>> c.send(8)
128
>>> c.send(8)
256

এখন আপনি এখানে সেন্ড ব্যবহার করে একই ধরণের ফাংশনটি করবেন কীভাবে প্রতিটি পুনরাবৃত্তির উপর আপনি সংখ্যার মান পরিবর্তন করতে পারেন:

def double_number(number):
    while True:
        number *= 2
        number = yield number

এখানে দেখতে দেখতে দেখতে যেমনটি দেখা যাচ্ছে, আপনি দেখতে পাচ্ছেন যে সংখ্যার জন্য একটি নতুন মান পাঠানো ফলাফল পরিবর্তন করে:

>>> def double_number(number):
...     while True:
...         number *= 2
...         number = yield number
...
>>> c = double_number(4)
>>> 
>>> c.send(None)
8
>>> c.send(5) #10
10
>>> c.send(1500) #3000
3000
>>> c.send(3) #6
6

আপনি এটিকে এটির মতো লুপের জন্য রাখতে পারেন:

for x in range(10):
    n = c.send(n)
    print n

আরও সহায়তার জন্য এই দুর্দান্ত টিউটোরিয়ালটি দেখুন


12
কোনও ফাংশনের মধ্যে এই তুলনা যা প্রেরণ দ্বারা প্রভাবিত হয় না () যা সত্যিই সাহায্য করে। ধন্যবাদ!
মানস বাজাজ

এটি কীভাবে উদ্দেশ্যটির উদাহরণস্বরূপ উদাহরণ হতে পারে send? একটি সরল lambda x: x * 2একই জিনিসটি অনেক কম সংশ্লেষিত উপায়ে করে।
ব্যবহারকারী 209974

এটি কি প্রেরণ ব্যবহার করে? যান এবং আপনার উত্তর যুক্ত করুন।
রডটেক

17

কিছু জেনারেটর এবং জন্য ব্যবহার ক্ষেত্রে send()

send()অনুমতি সহ জেনারেটর :

  • মৃত্যুদন্ড কার্যকর করার অভ্যন্তরীণ অবস্থা মনে করে
    • আমরা কি পদক্ষেপে আছি
    • আমাদের তথ্য বর্তমান অবস্থা কি
  • মানগুলির ক্রম ফিরিয়ে দেওয়া
  • ইনপুট ক্রম প্রাপ্ত

এখানে কিছু ব্যবহারের মামলা রয়েছে:

একটি রেসিপি অনুসরণ করার চেষ্টা দেখেছি

আসুন আমাদের একটি রেসিপি রয়েছে, যা কিছু ক্রমে ইনপুটগুলির পূর্বনির্ধারিত সেটটি আশা করে।

আমরা হতেপারি:

  • watched_attemptরেসিপি থেকে একটি উদাহরণ তৈরি করুন
  • এটি কিছু ইনপুট পেতে দিন
  • পাত্রটিতে বর্তমানে কী রয়েছে সে সম্পর্কে প্রতিটি ইনপুট ফেরত তথ্য সহ
  • প্রতিটি ইনপুট চেক সহ, যে ইনপুটটি প্রত্যাশিত এক (এবং এটি যদি না হয় তবে ব্যর্থ হয়)

    def recipe():
        pot = []
        action = yield pot
        assert action == ("add", "water")
        pot.append(action[1])
    
        action = yield pot
        assert action == ("add", "salt")
        pot.append(action[1])
    
        action = yield pot
        assert action == ("boil", "water")
    
        action = yield pot
        assert action == ("add", "pasta")
        pot.append(action[1])
    
        action = yield pot
        assert action == ("decant", "water")
        pot.remove("water")
    
        action = yield pot
        assert action == ("serve")
        pot = []
        yield pot

এটি ব্যবহার করতে, প্রথমে watched_attemptউদাহরণটি তৈরি করুন :

>>> watched_attempt = recipe()                                                                         
>>> watched_attempt.next()                                                                                     
[]                                                                                                     

.next()জেনারেটরের কার্য সম্পাদন শুরু করার জন্য কলটি প্রয়োজনীয়।

ফিরে আসা মান শো, আমাদের পাত্রটি বর্তমানে খালি।

রেসিপিটি প্রত্যাশা করে যা অনুসরণ করে এখন কয়েকটি পদক্ষেপ করুন:

>>> watched_attempt.send(("add", "water"))                                                                     
['water']                                                                                              
>>> watched_attempt.send(("add", "salt"))                                                                      
['water', 'salt']                                                                                      
>>> watched_attempt.send(("boil", "water"))                                                                    
['water', 'salt']                                                                                      
>>> watched_attempt.send(("add", "pasta"))                                                                     
['water', 'salt', 'pasta']                                                                             
>>> watched_attempt.send(("decant", "water"))                                                                  
['salt', 'pasta']                                                                                      
>>> watched_attempt.send(("serve"))                                                                            
[] 

যেমনটি আমরা দেখছি, পাত্রটি শেষ পর্যন্ত শূন্য।

ক্ষেত্রে, কেউ রেসিপিটি অনুসরণ করে না, এটি ব্যর্থ হয় (কোনও কিছু রান্না করার চেষ্টার পর্যবেক্ষণের কাঙ্ক্ষিত ফলাফল কী হতে পারে - কেবল শিখতে হবে যখন নির্দেশনা দেওয়া হয় তখন আমরা যথেষ্ট মনোযোগ দিই না।

>>> watched_attempt = running.recipe()                                                                         
>>> watched_attempt.next()                                                                                     
[]                                                                                                     
>>> watched_attempt.send(("add", "water"))                                                                     
['water']                                                                                              
>>> watched_attempt.send(("add", "pasta")) 

---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-21-facdf014fe8e> in <module>()
----> 1 watched_attempt.send(("add", "pasta"))

/home/javl/sandbox/stack/send/running.py in recipe()
     29
     30     action = yield pot
---> 31     assert action == ("add", "salt")
     32     pot.append(action[1])
     33

AssertionError:

লক্ষ্য করুন:

  • প্রত্যাশিত পদক্ষেপের লিনিয়ার ক্রম রয়েছে
  • পদক্ষেপগুলি পৃথক হতে পারে (কিছু অপসারণ করছে, কেউ পাত্রটিতে যোগ করছে)
  • আমরা কোনও ফাংশন / জেনারেটর দ্বারা সেগুলি পরিচালনা করি - জটিল শ্রেণি বা অনুরূপ স্ট্র্যাচার ব্যবহার করার দরকার নেই।

মোট চলমান

আমরা জেনারেটরটি এতে পাঠানো মোট মানগুলির চলমান ট্র্যাক রাখতে ব্যবহার করতে পারি।

যে কোনও সময় আমরা সংখ্যার যোগ করি, ইনপুটগুলির গণনা এবং মোট যোগফল ফেরত দেওয়া হয় (পূর্ববর্তী ইনপুট এতে প্রেরণের মুহুর্তের জন্য বৈধ)।

from collections import namedtuple

RunningTotal = namedtuple("RunningTotal", ["n", "total"])


def runningtotals(n=0, total=0):
    while True:
        delta = yield RunningTotal(n, total)
        if delta:
            n += 1
            total += delta


if __name__ == "__main__":
    nums = [9, 8, None, 3, 4, 2, 1]

    bookeeper = runningtotals()
    print bookeeper.next()
    for num in nums:
        print num, bookeeper.send(num)

আউটপুট দেখতে হবে:

RunningTotal(n=0, total=0)
9 RunningTotal(n=1, total=9)
8 RunningTotal(n=2, total=17)
None RunningTotal(n=2, total=17)
3 RunningTotal(n=3, total=20)
4 RunningTotal(n=4, total=24)
2 RunningTotal(n=5, total=26)
1 RunningTotal(n=6, total=27)

3
আমি আপনার উদাহরণটি চালিয়েছি এবং অজগর 3 এ দেখেছি যে দেখেছি_আটটেম্পট.নেক্সট () এর পরবর্তী (দেখানো_অটেম্পট) দ্বারা প্রতিস্থাপন করতে হবে।
Thanos.a

15

send()পদ্ধতি নিয়ন্ত্রণ ফলন অভিব্যক্তি বাঁদিকে মান হতে হবে।

ফলন কীভাবে পৃথক হয় এবং কী মান রাখে তা বোঝার জন্য প্রথমে পাইথন কোডটি ক্রমের ক্রমটি রিফ্রেশ করুন।

বিভাগ 6.15 মূল্যায়ন আদেশ

পাইথন বাম থেকে ডানে এক্সপ্রেশন মূল্যায়ণ করে। লক্ষ্য করুন যে কোনও অ্যাসাইনমেন্ট মূল্যায়ন করার সময়, ডান-হাতটি বাম-হাতের আগে মূল্যায়ন করা হয়।

সুতরাং a = bপ্রথমে ডান হাতের অভিব্যক্তিটি মূল্যায়ন করা হয়।

যেমন নীচের a[p('left')] = p('right')দিক থেকে বোঝা যাচ্ছে যে ডান হাতটি প্রথমে মূল্যায়ন করা হয়।

>>> def p(side):
...     print(side)
...     return 0
... 
>>> a[p('left')] = p('right')
right
left
>>> 
>>> 
>>> [p('left'), p('right')]
left
right
[0, 0]

ফলনটি কী করে ?, ফলন দেয়, ফাংশনটির কার্য সম্পাদন স্থগিত করে এবং কলারের কাছে ফিরে আসে এবং স্থগিতকরণের আগে যে জায়গায় রেখেছিল তা একই স্থানে পুনরায় কার্যকর করা শুরু করে।

মৃত্যুদন্ড কার্যকর করা হয় কোথায়? আপনি ইতিমধ্যে এটি অনুমান করতে পারেন ... ফলন প্রকাশের ডান এবং বাম পাশের মধ্যে কার্যকর করা স্থগিত করা হয়। সুতরাং সম্পাদনাটি new_val = yield old_valচিহ্নটিতে থামিয়ে দেওয়া হয় =এবং ডানদিকে মান (যা স্থগিতের আগে, এবং যাঁকে ফোনকারীকে ফেরত দেওয়া মানও হয়) অন্যরকম কিছু হতে পারে তারপরে বাম দিকের মান (যা আবার শুরু হওয়ার পরে নির্ধারিত মান is মৃত্যুদন্ড)।

yield 2 টি মান দেয়, একটি ডানে এবং অন্যটি বামে।

আপনি ফলন প্রকাশের বাম পাশে মানটি কীভাবে নিয়ন্ত্রণ করবেন? .send()পদ্ধতি মাধ্যমে ।

6.2.9। ফলন এক্সপ্রেশন

পুনরায় শুরু করার পরে ফলন প্রকাশের মান সেই পদ্ধতির উপর নির্ভর করে যা কার্যকর করা পুনরায় শুরু করে। যদি __next__()ব্যবহৃত হয় (সাধারণত একটি বা next()বিল্টিনের মাধ্যমে) তবে ফলাফলটি কোনওটিই নয়। অন্যথায়, যদি send()ব্যবহৃত হয়, তবে ফলাফলটি সেই পদ্ধতিতে পাস করা মান হবে be


13

sendপদ্ধতি কার্যকরী coroutines

যদি আপনি কাউরোটাইনগুলির মুখোমুখি না হন তবে তারা আপনার মাথাটি চারপাশে মুড়িয়ে ফেলার জন্য কৌশলযুক্ত কারণ তারা কোনও প্রোগ্রাম প্রবাহিত করার উপায় পরিবর্তন করে। আপনি আরও বিশদ জন্য একটি ভাল টিউটোরিয়াল পড়তে পারেন ।


6

"ফলন" শব্দের দুটি অর্থ রয়েছে: কিছু উত্পাদন করা (উদাহরণস্বরূপ, ভুট্টা ফলন করা) এবং অন্য কাউকে / জিনিস চালিয়ে যেতে দেওয়া (যেমন, পথচারীদের জন্য ফলনকারী গাড়ি) থামানো। উভয় সংজ্ঞা পাইথনের ক্ষেত্রে প্রযোজ্যyield কীওয়ার্ডে ; যা জেনারেটর ফাংশনগুলিকে বিশেষ করে তোলে তা হ'ল নিয়মিত ফাংশনগুলির বিপরীতে, জেনারেটরের ক্রিয়াকলাপটি কেবল বিরতি দেওয়ার পরে, বন্ধ না করে, কলারে "ফিরে" দেওয়া যেতে পারে।

একটি "বাম" প্রান্ত এবং একটি "ডান" প্রান্তযুক্ত দ্বি নির্দেশমূলক পাইপের এক প্রান্ত হিসাবে কোনও জেনারেটর কল্পনা করা সবচেয়ে সহজ; এই পাইপটি এমন এক মাধ্যম যা মান নিজেই জেনারেটর এবং জেনারেটরের ক্রিয়াকলাপের শরীরের মধ্যে প্রেরণ করা হয়। পাইপের প্রতিটি প্রান্তে দুটি ক্রিয়াকলাপ রয়েছে: pushযা পাইপের অন্য প্রান্তটি মান টান না দেয় এবং কোনও কিছু না ফেরানো পর্যন্ত একটি মান এবং ব্লক প্রেরণ করে; এবংpull, যা পাইপের অন্য প্রান্ত পর্যন্ত কোনও মানকে ঠেলে না দেওয়া অবধি ব্লক করে এবং পুশ করা মানটি ফেরত দেয়। রানটাইমের সময়, এক্সিকিউশনটি পাইপের উভয় পাশের প্রসঙ্গগুলির মধ্যে পিছনে পিছনে বাউন্স করে - প্রতিটি পক্ষ চালিত হয় যতক্ষণ না এটি অন্য দিকে কোনও মান প্রেরণ করে, যে স্থানে এটি থামায়, অন্য পক্ষকে চালিত হতে দেয় এবং মানটির জন্য অপেক্ষা করে ফিরে আসুন, অন্যদিকে থামার সময় এটি আবার শুরু হয়। অন্য কথায়, পাইপের প্রতিটি প্রান্তটি যে মুহুর্তটি একটি মূল্য পাঠায় সেই মুহুর্তের সাথে এটি একটি মান গ্রহণ করে runs

পাইপ বৈশিষ্ট্যগুলি প্রতিসম, কিন্তু - কনভেনশন আমি এই উত্তর সংজ্ঞা করছি দ্বারা - বাম শেষ উত্পাদক ফাংশন লাশ ভিতরে শুধুমাত্র পাওয়া যায় এবং মাধ্যমে অ্যাক্সেসযোগ্য yieldযখন ডান শেষ, শব্দ হয় জেনারেটরের এবং মাধ্যমে অ্যাক্সেসযোগ্য জেনারেটরের sendফাংশন পাইপটির নিজ নিজ প্রান্তে একক ইন্টারফেস হিসাবে yieldএবং sendডাবল ডিউটি ​​করে: তারা উভয়ই পাইপের তাদের প্রান্তে / থেকে মানগুলি yieldটানিয়ে sendদেয় এবং বিপরীত অবস্থায় ডানদিকে ঠেলে এবং বাম দিকে টান দেয়। এই ডাবল ডিউটি ​​হল বিবৃতিগুলির শব্দার্থবিজ্ঞানকে ঘিরে বিভ্রান্তির শিরা x = yield y। দুটি স্পষ্ট ধাক্কা / টানা পদক্ষেপগুলিকে ভাঙ্গা yieldএবং sendনীচে নামানো তাদের শব্দার্থকগুলিকে আরও স্পষ্ট করে তুলবে:

  1. মনে করুন gজেনারেটর। g.sendপাইপের ডান প্রান্ত দিয়ে একটি মান বাম দিকে ঠেলে দেয়।
  2. gবিরতি প্রসঙ্গে জেনারেটর ফাংশন এর শরীর চালানোর অনুমতি প্রসঙ্গে বাস্তবায়ন ।
  3. দ্বারা ধাক্কা দেওয়া মানটি g.sendবাম দিকে টেনে yieldনিয়ে যায় এবং পাইপের বাম প্রান্তে প্রাপ্ত হয়। ইন x = yield y, xটানা মান নির্ধারিত হয়।
  4. জেনারেটরের ক্রিয়াকলাপের মূল অংশের মধ্যে পরবর্তী লাইনটি উপস্থিত না হওয়া পর্যন্ত সম্পাদন অব্যাহত থাকে yield
  5. yieldপাইপের বাম প্রান্তটি দিয়ে পিছনে পিছনে একটি মান ডানদিকে ঠেলে দেয় g.send। ইন x = yield y, yপাইপ মাধ্যমে ডানদিকে ধাক্কা হয়।
  6. জেনারেটর ফাংশনটির দেহের মধ্যে সম্পাদন বিরতি দেয়, বাহ্যিক স্কোপটি যেখানে ছেড়ে যায় সেখানে চালিয়ে যেতে দেয়।
  7. g.send পুনরায় শুরু করে এবং মানটি টান দেয় এবং ব্যবহারকারীর কাছে ফেরত দেয়।
  8. g.sendএরপরে যখন ডাকা হবে তখন পদক্ষেপ 1 এ ফিরে যান।

চক্রাকারে, এই পদ্ধতির একটি শুরু আছে: কখন g.send(None)- যা next(g)স্বল্প - এটি প্রথম বলা হয় ( Noneপ্রথম sendকল ব্যতীত অন্য কিছু পাস করা অবৈধ )। এবং এর শেষ হতে পারে: যখন yieldজেনারেটর ফাংশনটির শরীরে আর কোনও বিবৃতি পৌঁছানো যায় না।

আপনি কী দেখতে পাচ্ছেন যে yieldবিবৃতিটি (বা আরও সঠিকভাবে, জেনারেটর) এত বিশেষ করে তোলে ? পরিমাপকৃত returnকীওয়ার্ডের বিপরীতে , yieldতার কলারের কাছে মানগুলি সঞ্চার করতে সক্ষম এবং এতে বাসকারী ফাংশনটি শেষ না করেই তার কলারের কাছ থেকে মানগুলি গ্রহণ করতে সক্ষম! (অবশ্যই, আপনি যদি কোনও ফাংশন - বা একটি জেনারেটর বন্ধ করতে চান return) তবে কীওয়ার্ডটিও রাখা সহজ ।) যখন কোনও yieldবিবৃতি আসে তখন জেনারেটর ফাংশনটি কেবল বিরতি দেয় এবং তারপরে যেখানে বাম দিকে ডানদিকে উঠে যায় p অন্য মান প্রেরণ করা বন্ধ। এবং sendএটি বাইরে থেকে একটি জেনারেটরের ফাংশনের অভ্যন্তরের সাথে যোগাযোগের জন্য কেবল ইন্টারফেস।

আমরা সত্যিই নিচে যতটা আমরা করতে পারেন হিসাবে এই ধাক্কা / পুল / পাইপ উপমা বিরতি চান, আমরা নিম্নলিখিত pseudocode হয় যে সত্যিই বাড়িতে ড্রাইভ দিয়ে শেষ যে সরাইয়া পদক্ষেপ 1-5, থেকে yieldএবং sendএকই দুই পক্ষের হয় মুদ্রা নল:

  1. right_end.push(None) # the first half of g.send; sending None is what starts a generator
  2. right_end.pause()
  3. left_end.start()
  4. initial_value = left_end.pull()
  5. if initial_value is not None: raise TypeError("can't send non-None value to a just-started generator")
  6. left_end.do_stuff()
  7. left_end.push(y) # the first half of yield
  8. left_end.pause()
  9. right_end.resume()
  10. value1 = right_end.pull() # the second half of g.send
  11. right_end.do_stuff()
  12. right_end.push(value2) # the first half of g.send (again, but with a different value)
  13. right_end.pause()
  14. left_end.resume()
  15. x = left_end.pull() # the second half of yield
  16. 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:

  1. আমরা প্রয়োগটিকে একটি নেস্টেড ফাংশনে স্থানান্তরিত করেছি।
  2. আমরা একটি দ্বিপাক্ষিক পাইপ তৈরি করেছি যার left_endনেস্ট ফাংশন দ্বারা অ্যাক্সেস করা right_endহবে এবং যার ফিরিয়ে দেওয়া হবে এবং বাইরের স্কোপ দ্বারা অ্যাক্সেস করা হবে - right_endএটি আমরা জেনারেটর অবজেক্ট হিসাবে জানি।
  3. নেস্টেড ফাংশন মধ্যে, প্রথম জিনিস আমরা চেক করে left_end.pull()হয় None, গ্রাসকারী প্রক্রিয়ায় ধাক্কা মান।
  4. নেস্টেড ক্রিয়াকলাপের মধ্যে, বিবৃতিটি x = yield yদুটি লাইন দ্বারা প্রতিস্থাপন করা হয়েছে: left_end.push(y)এবং x = left_end.pull()
  5. আমরা 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উত্থাপিত হয়।


"G = f (1) এর 1 টি আর্গুমেন্টটি সাধারণত ধরা পড়েছে এবং এফ এর শরীরে y এর জন্য বরাদ্দ করা হয়েছে, তবে সত্যটি এখনও শুরু হয়নি।" কেন না? পাইথন যেমন কোড না পাওয়া পর্যন্ত এই কোডটি চালানোর চেষ্টা করবে না কেন yield?
জোশ

@ জোশ কার্সার প্রথম কল না হওয়া পর্যন্ত উন্নত হয় না send; send(None)প্রথম yieldবিবৃতিতে কার্সারটি সরিয়ে নিতে এটির একটি কল লাগে এবং তারপরেই পরবর্তী sendকলগুলি আসলে একটি "আসল" মান প্রেরণ করে yield
বলপয়েন্টবেইন

ধন্যবাদ - এটি আকর্ষণীয়, সুতরাং দোভাষী জানেন যে ফাংশনটি কোনও এক সময় f ঘটবে yield , এবং এভাবে sendকলারের কাছ থেকে এটি না পাওয়া পর্যন্ত অপেক্ষা করুন ? একটি সাধারণ ক্রিয়াকলাপের সাহায্যে দোভাষী কেবল এখনই চালানো শুরু করবেন f, ডান? সর্বোপরি পাইথনে কোনও ধরণের কোনও এওটি সংকলন নেই। আপনি কি নিশ্চিত যে ঘটনাটি? (আপনি কী বলছেন তা নিয়ে প্রশ্ন নেই, আপনি এখানে যা লিখেছেন তা দ্বারা আমি সত্যিই হতবাক হয়েছি)। পাইথন কীভাবে জানে যে এটি বাকি ফাংশনটি সম্পাদন শুরু করার আগে অপেক্ষা করা দরকার তা আমি আরও কোথায় পড়তে পারি?
জোশ

@ জোশ আমি পাইথনের অভ্যন্তরীণ কোনও ধারণা ছাড়াই খেলনা জেনারেটরগুলি কীভাবে কাজ করে তা পর্যবেক্ষণ করে এই মানসিক মডেলটি তৈরি করেছি। তবে, জেনারেটরে প্রেরণ না করে প্রারম্ভিকভাবে send(None)উপযুক্ত মান (যেমন, 1) পাওয়া যায় তা বোঝায় যে প্রথম কলটি একটি বিশেষ ক্ষেত্রে। এটি ডিজাইনের জন্য একটি জটিল ইন্টারফেস; যদি আপনি প্রথমে একটি নির্বিচার মান প্রেরণ করতে দেন, তবে ফলিত মান এবং প্রেরিত মানের ক্রম বর্তমানে এটির তুলনায় একের দ্বারা বন্ধ হয়ে যাবে। Nonesendsend
বলপয়েন্টবেইন

ধন্যবাদ বলপয়েন্টবেন। খুব আকর্ষণীয়, আমি কেন এখানে কেস দেখতে এখানে একটি প্রশ্ন রেখেছি ।
জোশ

2

এগুলি আমাকেও বিভ্রান্ত করেছে। আমি এমন একটি জেনারেটর স্থাপনের চেষ্টা করার সময় তৈরি করেছি যা বিকল্প ক্রমে (ফলন, গ্রহণ, ফলন, স্বীকৃতি) সিগন্যাল দেয় এবং গ্রহণ করে ...

def echo_sound():

    thing_to_say = '<Sound of wind on cliffs>'
    while True:
        thing_to_say = (yield thing_to_say)
        thing_to_say = '...'.join([thing_to_say]+[thing_to_say[-6:]]*2)
        yield None  # This is the return value of send.

gen = echo_sound()

print 'You are lost in the wilderness, calling for help.'

print '------'
in_message = gen.next()
print 'You hear: "{}"'.format(in_message)
out_message = 'Hello!'
print 'You yell "{}"'.format(out_message)
gen.send(out_message)

print '------'
in_message = gen.next()
print 'You hear: "{}"'.format(in_message)
out_message = 'Is anybody out there?'
print 'You yell "{}"'.format(out_message)
gen.send(out_message)

print '------'
in_message = gen.next()
print 'You hear: "{}"'.format(in_message)
out_message = 'Help!'
print 'You yell "{}"'.format(out_message)
gen.send(out_message)

আউটপুটটি হ'ল:

You are lost in the wilderness, calling for help.
------
You hear: "<Sound of wind on cliffs>"
You yell "Hello!"
------
You hear: "Hello!...Hello!...Hello!"
You yell "Is anybody out there?"
------
You hear: "Is anybody out there?...there?...there?"
You yell "Help!"
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.