"ফলন" কীওয়ার্ডটি কী করে?


10180

yieldপাইথনে কীওয়ার্ডটির ব্যবহার কী এবং এটি কী করে?

উদাহরণস্বরূপ, আমি এই কোডটি 1 টি বোঝার চেষ্টা করছি :

def _get_child_candidates(self, distance, min_dist, max_dist):
    if self._leftchild and distance - max_dist < self._median:
        yield self._leftchild
    if self._rightchild and distance + max_dist >= self._median:
        yield self._rightchild  

এবং এই আহ্বানকারী:

result, candidates = [], [self]
while candidates:
    node = candidates.pop()
    distance = node._get_dist(obj)
    if distance <= max_dist and distance >= min_dist:
        result.extend(node._values)
    candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
return result

পদ্ধতিটি _get_child_candidatesবলা হলে কী হয় ? একটি তালিকা ফিরে আছে? একক উপাদান? এটি আবার বলা হয়? পরবর্তী কলগুলি কখন বন্ধ হবে?


১. এই কোডের টুকরাটি জোচেন শুলজ (জার্সচুল্জ) লিখেছিলেন, যিনি মেট্রিক স্পেসের জন্য একটি দুর্দান্ত পাইথন গ্রন্থাগার তৈরি করেছিলেন। এটি সম্পূর্ণ উত্সের লিঙ্ক: মডিউল এমস্পেস

উত্তর:


14635

কী yieldকরে তা বুঝতে , আপনাকে অবশ্যই জেনারেটরগুলি বুঝতে হবে । এবং জেনারেটরগুলি বোঝার আগে আপনাকে অবশ্যই পুনরাবৃত্তিগুলি বুঝতে হবে ।

Iterables

আপনি যখন একটি তালিকা তৈরি করেন, আপনি এর আইটেমগুলি একে একে পড়তে পারেন। একে একে এর আইটেমগুলি পড়তে বলা হয় পুনরাবৃত্তি:

>>> mylist = [1, 2, 3]
>>> for i in mylist:
...    print(i)
1
2
3

mylistএকটি পুনরাবৃত্তিযোগ্য । আপনি যখন কোনও তালিকা উপলব্ধি ব্যবহার করেন, আপনি একটি তালিকা তৈরি করেন এবং তাই একটি পুনরাবৃত্তযোগ্য:

>>> mylist = [x*x for x in range(3)]
>>> for i in mylist:
...    print(i)
0
1
4

আপনি " for... in..." ব্যবহার করতে পারেন এমন সমস্ত কিছুই পুনরাবৃত্তিযোগ্য; lists, stringsফাইলগুলি ...

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

জেনারেটর

জেনারেটরগুলি পুনরাবৃত্তকারী, এক ধরণের পুনরাবৃত্তিযোগ্য আপনি কেবল একবারে পুনরাবৃত্তি করতে পারেন । জেনারেটর সমস্ত মান স্মৃতিতে সঞ্চয় করে না, তারা উড়ে মানগুলি উত্পন্ন করে :

>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator:
...    print(i)
0
1
4

আপনি ()পরিবর্তে ব্যবহার ব্যতীত এটি ঠিক একই []। কিন্তু তুমিfor i in mygenerator দ্বিতীয়বার সঞ্চালন পারবেন না কারণ জেনারেটরগুলি কেবল একবার ব্যবহার করা যেতে পারে: তারা 0 গণনা করে, তারপর এটি ভুলে গিয়ে 1 গণনা করে এবং একে একে একে 4 গণনা শেষ করে।

উত্পাদ

yieldএকটি কীওয়ার্ড যা ব্যবহার করা হয় যেমন returnফাংশন ব্যতীত কোনও জেনারেটর ফিরে আসবে।

>>> def createGenerator():
...    mylist = range(3)
...    for i in mylist:
...        yield i*i
...
>>> mygenerator = createGenerator() # create a generator
>>> print(mygenerator) # mygenerator is an object!
<generator object createGenerator at 0xb7555c34>
>>> for i in mygenerator:
...     print(i)
0
1
4

এখানে এটি একটি অকেজো উদাহরণ, তবে এটি কার্যকর যখন আপনি জানেন যে আপনার ফাংশনটি একটি বিশাল মানের মান প্রদান করবে যা আপনাকে কেবল একবার পড়তে হবে।

মাস্টার করার জন্য yield, আপনাকে অবশ্যই বুঝতে হবে যে আপনি যখন ফাংশনটি কল করবেন তখন ফাংশন বডিটিতে আপনার লেখা কোডটি চলবে না। ফাংশনটি কেবল জেনারেটরের বস্তুটি দেয়, এটি কিছুটা জটিল :-)

তারপরে, আপনার কোডটি সেখান থেকে চালিয়ে যাবে যেখানে এটি প্রতিবারই forজেনারেটরটি ব্যবহার করে।

এখন শক্ত অংশ:

প্রথমবার forকলগুলি যখন আপনার ফাংশন থেকে জেনারেটর অবজেক্ট তৈরি হয়েছিল, এটি শুরু থেকে আপনার ফাংশনটিতে কোডটি চালাবে যতক্ষণ না এটি আঘাত করে yield, তখন এটি লুপের প্রথম মানটি ফিরিয়ে দেবে। তারপরে, প্রতিটি পরবর্তী কলটি আপনি ফাংশনে লিখেছেন সেই লুপের আরেকটি পুনরাবৃত্তি চালাবে এবং পরবর্তী মানটি ফেরত দেবে। এই পর্যন্ত জেনারেটরের খালি বিবেচনা করা হয় যা ঘটবে ফাংশন আঘাত ছাড়া সঞ্চালিত হয় যখন চালিয়ে যাবে, yield। এটি হতে পারে কারণ লুপটি শেষ হয়ে গেছে বা আপনি আর কোনওটিকে সন্তুষ্ট করবেন না "if/else"


আপনার কোড ব্যাখ্যা করা হয়েছে

জেনারেটর:

# Here you create the method of the node object that will return the generator
def _get_child_candidates(self, distance, min_dist, max_dist):

    # Here is the code that will be called each time you use the generator object:

    # If there is still a child of the node object on its left
    # AND if the distance is ok, return the next child
    if self._leftchild and distance - max_dist < self._median:
        yield self._leftchild

    # If there is still a child of the node object on its right
    # AND if the distance is ok, return the next child
    if self._rightchild and distance + max_dist >= self._median:
        yield self._rightchild

    # If the function arrives here, the generator will be considered empty
    # there is no more than two values: the left and the right children

কলার:

# Create an empty list and a list with the current object reference
result, candidates = list(), [self]

# Loop on candidates (they contain only one element at the beginning)
while candidates:

    # Get the last candidate and remove it from the list
    node = candidates.pop()

    # Get the distance between obj and the candidate
    distance = node._get_dist(obj)

    # If distance is ok, then you can fill the result
    if distance <= max_dist and distance >= min_dist:
        result.extend(node._values)

    # Add the children of the candidate in the candidate's list
    # so the loop will keep running until it will have looked
    # at all the children of the children of the children, etc. of the candidate
    candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))

return result

এই কোডটিতে বেশ কয়েকটি স্মার্ট পার্ট রয়েছে:

  • লুপটি তালিকায় পুনরাবৃত্তি করে, তবে লুপটি পুনরাবৃত্ত হওয়ার সময় তালিকাটি প্রসারিত হয় :-) আপনি যদি অসীম লুপটি শেষ করতে পারেন তবে কিছুটা বিপজ্জনক হলেও এই সমস্ত নেস্টেড ডেটা দিয়ে যাওয়ার একটি সংক্ষিপ্ত উপায়। এই ক্ষেত্রে, candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))জেনারেটরের সমস্ত whileমান সরিয়ে ফেলুন , তবে নতুন জেনারেটর অবজেক্ট তৈরি করতে থাকুন যা একই নোডে প্রয়োগ না হওয়ায় পূর্ববর্তীগুলির থেকে বিভিন্ন মান তৈরি করে।

  • extend()পদ্ধতি একটি তালিকা বস্তুর পদ্ধতি যে একটি iterable আশা এবং তালিকা তার মান যোগ করে।

সাধারণত আমরা এটিতে একটি তালিকা পাস করি:

>>> a = [1, 2]
>>> b = [3, 4]
>>> a.extend(b)
>>> print(a)
[1, 2, 3, 4]

তবে আপনার কোডে এটি একটি জেনারেটর পায় যা ভাল কারণ:

  1. আপনার দুটিবার মান পড়ার দরকার নেই।
  2. আপনার অনেক বাচ্চা থাকতে পারে এবং আপনি তাদের সমস্ত স্মৃতিতে সঞ্চিত রাখতে চান না।

এবং এটি কাজ করে কারণ পাইথন কোনও পদ্ধতির আর্গুমেন্টের তালিকা কিনা তা বিবেচনা করে না। পাইথন পুনরাবৃত্তির প্রত্যাশা করে তাই এটি স্ট্রিং, তালিকা, টিপলস এবং জেনারেটরের সাথে কাজ করবে! এটিকে হাঁসের টাইপিং বলা হয় এবং পাইথন এত শীতল হওয়ার অন্যতম কারণ। তবে এটি আরও একটি গল্প, অন্য প্রশ্নের জন্য ...

জেনারেটরের উন্নত ব্যবহার দেখতে আপনি এখানে থামতে পারেন বা কিছুটা পড়তে পারেন:

জেনারেটরের ক্লান্তি নিয়ন্ত্রণ করছে

>>> class Bank(): # Let's create a bank, building ATMs
...    crisis = False
...    def create_atm(self):
...        while not self.crisis:
...            yield "$100"
>>> hsbc = Bank() # When everything's ok the ATM gives you as much as you want
>>> corner_street_atm = hsbc.create_atm()
>>> print(corner_street_atm.next())
$100
>>> print(corner_street_atm.next())
$100
>>> print([corner_street_atm.next() for cash in range(5)])
['$100', '$100', '$100', '$100', '$100']
>>> hsbc.crisis = True # Crisis is coming, no more money!
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> wall_street_atm = hsbc.create_atm() # It's even true for new ATMs
>>> print(wall_street_atm.next())
<type 'exceptions.StopIteration'>
>>> hsbc.crisis = False # The trouble is, even post-crisis the ATM remains empty
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> brand_new_atm = hsbc.create_atm() # Build a new one to get back in business
>>> for cash in brand_new_atm:
...    print cash
$100
$100
$100
$100
$100
$100
$100
$100
$100
...

দ্রষ্টব্য: পাইথন 3 এর জন্য, ব্যবহার করুন print(corner_street_atm.__next__())বাprint(next(corner_street_atm))

এটি কোনও সম্পদে অ্যাক্সেস নিয়ন্ত্রণ করার মতো বিভিন্ন জিনিসের জন্য কার্যকর হতে পারে।

Itertools, আপনার সেরা বন্ধু

ইটারটুলস মডিউলটিতে পুনরাবৃত্তিগুলি ম্যানিপুলেট করতে বিশেষ ফাংশন রয়েছে। কখনও জেনারেটরের নকল করতে চান? চেইন দুটি জেনারেটর? ওয়ান-লাইনারের সাথে নেস্টেড তালিকায় গ্রুপ মানগুলি? Map / Zipঅন্য তালিকা তৈরি না করেই?

তারপর ঠিক import itertools

একটি উদাহরণ? আসুন চার ঘোড়ার দৌড়ের জন্য আগমনের সম্ভাব্য আদেশগুলি দেখুন:

>>> horses = [1, 2, 3, 4]
>>> races = itertools.permutations(horses)
>>> print(races)
<itertools.permutations object at 0xb754f1dc>
>>> print(list(itertools.permutations(horses)))
[(1, 2, 3, 4),
 (1, 2, 4, 3),
 (1, 3, 2, 4),
 (1, 3, 4, 2),
 (1, 4, 2, 3),
 (1, 4, 3, 2),
 (2, 1, 3, 4),
 (2, 1, 4, 3),
 (2, 3, 1, 4),
 (2, 3, 4, 1),
 (2, 4, 1, 3),
 (2, 4, 3, 1),
 (3, 1, 2, 4),
 (3, 1, 4, 2),
 (3, 2, 1, 4),
 (3, 2, 4, 1),
 (3, 4, 1, 2),
 (3, 4, 2, 1),
 (4, 1, 2, 3),
 (4, 1, 3, 2),
 (4, 2, 1, 3),
 (4, 2, 3, 1),
 (4, 3, 1, 2),
 (4, 3, 2, 1)]

পুনরাবৃত্তির অভ্যন্তরীণ প্রক্রিয়াগুলি বোঝা

Iteration একটি প্রক্রিয়া যা পুনরাবৃত্তি ( __iter__()পদ্ধতি বাস্তবায়ন ) এবং পুনরাবৃত্তকারীগুলি ( পদ্ধতিটি বাস্তবায়ন করে ) বোঝায় __next__()। Iteabless হ'ল কোনও বস্তু যা থেকে আপনি পুনরুক্তি পেতে পারেন। আইট্রেটারগুলি এমন বস্তু যা আপনাকে পুনরাবৃত্তিতে পুনরাবৃত্তি করতে দেয়।

লুপগুলি কীভাবে forকাজ করে সে সম্পর্কে এই নিবন্ধে আরও রয়েছে ।


353
yieldএই জবাবটি যা জাদুকর তা নয়। আপনি যখন কোনও ফাংশনটিতে কল করেন yieldযেখানে কোনও বিবৃতি রয়েছে , আপনি একটি জেনারেটর অবজেক্ট পাবেন তবে কোনও কোড চলবে না। তারপরে প্রতিবার আপনি জেনারেটর থেকে কোনও বস্তুটি বের করেন, পাইথন কোনও yieldবিবৃতি না আসা পর্যন্ত ফাংশনে কোডটি কার্যকর করে , তারপরে অজানাটি থামিয়ে দেয় এবং বিতরণ করে। আপনি যখন অন্য কোনও বস্তুটি বের করেন, পাইথন ঠিক এর পরে আবার শুরু হয় yieldএবং এটি অন্যটিতে পৌঁছানো অবধি চলতে থাকে yield(প্রায়শই একই, তবে একটি পুনরাবৃত্তি পরে)। ফাংশনটি শেষ না হওয়া পর্যন্ত এটি অব্যাহত থাকে, যার জেনারেটরটিকে ক্লান্ত মনে করা হয়।
ম্যাথিয়াস ফ্রিপ

28
"এই পুনরাবৃত্তিমূলকগুলি কার্যকর ... তবে আপনি সমস্ত মানগুলিকে স্মৃতিতে সঞ্চয় করেন এবং এটি সর্বদা আপনি যা চান তা নয়" হয় হয় ভুল বা বিভ্রান্তিকর। একটি পুনরাবৃত্তযোগ্য পুনরাবৃত্তকারীকে ইটার () কল করার পরে একটি পুনরাবৃত্তিকে ফেরত দেয় এবং একটি পুনরাবৃত্তকারী সবসময় তার মানগুলিকে মেমরিতে সংরক্ষণ করতে হয় না , এটি ইটার পদ্ধতির বাস্তবায়নের উপর নির্ভর করে , এটি চাহিদা অনুসারে মানগুলিও তৈরি করতে পারে।
পিকমেট 涅

এই দুর্দান্ত উত্তরের সাথে যুক্ত করা ভাল লাগবে কেন আপনি ()পরিবর্তে ব্যবহৃত ব্যতীত[] কেন এটি একই রকম , বিশেষত যা আছে ()তা (একটি টিউপলের সাথে বিভ্রান্তি থাকতে পারে)।
ওউজে

আমি ভুল হতে পারি, তবে একটি জেনারেটর একটি পুনরুক্তিকারী নয়, "জেনারেটর নামে পরিচিত" একটি পুনরুক্তিকারী।
অ্যাডারচক্স

2006

বোঝার শর্টকাট yield

আপনি যখন yieldবিবৃতি সহ কোনও ফাংশন দেখেন, তখন কী হবে তা বোঝার জন্য এই সহজ কৌশলটি প্রয়োগ করুন:

  1. result = []ফাংশনের শুরুতে একটি লাইন In োকান।
  2. yield exprসঙ্গে প্রতিটি প্রতিস্থাপন result.append(expr)
  3. return resultফাংশনের নীচে একটি লাইন .োকান।
  4. হ্যাঁ - আর yieldবিবৃতি নেই! কোডটি পড়ুন এবং চিত্রিত করুন।
  5. মূল সংজ্ঞা সাথে ফাংশনটির তুলনা করুন।

এই কৌশলটি আপনাকে ফাংশনের পিছনে যুক্তি সম্পর্কে ধারণা দিতে পারে, তবে আসলে yieldযা ঘটে তা তালিকার ভিত্তিতে হওয়া পদ্ধতির চেয়ে উল্লেখযোগ্যভাবে আলাদা। অনেক ক্ষেত্রে, ফলন পদ্ধতির পরিমাণ অনেক বেশি মেমরির দক্ষ এবং দ্রুত হবে। অন্যান্য ক্ষেত্রে, এই কৌশলটি আপনাকে অসীম লুপে আটকে দেবে, যদিও মূল ফাংশনটি ঠিক কাজ করে fine আরো জানতে পড়ুন...

আপনার Iteabless, Iterators, এবং জেনারেটর গুলিয়ে ফেলবেন না

প্রথমে পুনরাবৃত্তকারী প্রোটোকল - আপনি যখন লিখবেন

for x in mylist:
    ...loop body...

পাইথন নিম্নলিখিত দুটি পদক্ষেপ সম্পাদন করে:

  1. এর জন্য একটি পুনরাবৃত্তি পায় mylist:

    কল করুন iter(mylist)-> এটি কোনও next()পদ্ধতির (বা __next__()পাইথন 3 এ) দিয়ে কোনও বস্তু ফেরত দেয় ।

    [বেশিরভাগ লোকেরা আপনাকে জানাতে ভুলে যাওয়া এই পদক্ষেপ]

  2. আইটেমগুলি লুপ করতে পুনরুক্তি ব্যবহার করে:

    next()পদক্ষেপ 1 থেকে ফিরে আসা পুনরাবৃত্তকারীটিতে পদ্ধতিটি কল করতে থাকুন from থেকে ফেরতের মান next()বরাদ্দ করা হয় xএবং লুপের বডিটি কার্যকর করা হয়। যদি কোনও ব্যতিক্রম StopIterationযদি অভ্যন্তরীণ থেকে উত্থাপিত হয় next(), তবে এর অর্থ হ'ল পুনরাবৃত্তিতে কোনও মান নেই এবং লুপটি প্রস্থান করা হয়েছে।

সত্য পাইথন সঞ্চালিত যে কোন সময় দুই ধাপ উপরে এটি চায় উপর লুপ একটি বস্তুর বিষয়বস্তু - তাই এটি লুপ জন্য একটি হতে পারে, কিন্তু এটি মত কোড হতে পারে otherlist.extend(mylist)(যেখানে otherlistএকটি পাইথন তালিকা)।

এখানে mylistএকটি পুনরাবৃত্তযোগ্য কারণ এটি পুনরায় প্রোটোকল প্রয়োগ করে। কোনও ব্যবহারকারী-সংজ্ঞায়িত শ্রেণিতে, আপনি __iter__()আপনার শ্রেণীর পুনরাবৃত্তিযোগ্য করার পদ্ধতিটি প্রয়োগ করতে পারেন । এই পদ্ধতিতে একটি পুনরাবৃত্তির ফিরিয়ে দেওয়া উচিত । একটি পুনরাবৃত্তি একটি next()পদ্ধতি সহ একটি অবজেক্ট । এটি উভয় বাস্তবায়ন করা সম্ভব __iter__()এবং next()একই শ্রেণীতে উপর, এবং __iter__()রিটার্ন self। এটি সাধারণ কেসের জন্য কাজ করবে, তবে যখন আপনি একই আইটেমের উপরে একই সাথে দুটি পুনরাবৃত্তিকে লুপিং করতে চান তখন নয়।

সুতরাং এটি পুনরুদ্ধারকারী প্রোটোকল, অনেকগুলি এই প্রোটোকলটি প্রয়োগ করে:

  1. অন্তর্নির্মিত তালিকা, অভিধান, tuples, সেট, ফাইল।
  2. ব্যবহারকারী-সংজ্ঞায়িত শ্রেণি যা প্রয়োগ করে __iter__()
  3. জেনারেটর।

নোট করুন যে কোনও forলুপটি জানেন না যে এটি কী ধরণের জিনিসটির সাথে কাজ করছে - এটি কেবল পুনরাবৃত্তকারী প্রোটোকল অনুসরণ করে এবং আইটেমটি কল করার সাথে সাথে এটি পেয়ে খুশি হয় next()। অন্তর্নির্মিত তালিকাগুলি তাদের আইটেমগুলি একে একে ফেরত দেয়, অভিধানগুলি কীগুলি একে একে ফেরত দেয়, ফাইলগুলি একে একে লাইনগুলি ফেরত দেয় ইত্যাদি gene এবং জেনারেটরগুলি ফিরে yieldআসে ... ঠিক সেখানেই আসে:

def f123():
    yield 1
    yield 2
    yield 3

for item in f123():
    print item

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

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

জেনারেটর কেন ব্যবহার করবেন?

সাধারণত, আপনি এমন কোড লিখতে পারেন যা জেনারেটর ব্যবহার করে না তবে একই যুক্তি প্রয়োগ করে। একটি বিকল্প হ'ল আমি আগে উল্লিখিত অস্থায়ী তালিকা 'কৌশল' ব্যবহার করা। এটি সমস্ত ক্ষেত্রে কার্যকর হবে না, উদাহরণস্বরূপ, যদি আপনার অসীম লুপ থাকে, বা আপনার যখন খুব দীর্ঘ তালিকা থাকে তখন এটি মেমরির অকার্যকর ব্যবহার করতে পারে। অন্য পদ্ধতিটি হ'ল একটি নতুন পুনরাবৃত্তিযোগ্য ক্লাস সামমথিংয়ের প্রয়োগ করা যা রাষ্ট্রকে উদাহরণস্বরূপ সদস্যদের মধ্যে রাখে এবং এর মধ্যে পরবর্তী যৌক্তিক পদক্ষেপটি সম্পাদন করে next()(বা__next__() পাইথন 3) পদ্ধতিতে । যুক্তি অনুসারে, next()পদ্ধতির অভ্যন্তরীণ কোডটি খুব জটিল দেখায় এবং ত্রুটিযুক্ত হতে পারে। এখানে জেনারেটর একটি পরিষ্কার এবং সহজ সমাধান সরবরাহ করে।


19
"আপনি যখন ফলন বিবৃতি সহ কোনও ফাংশন দেখেন, তখন কী হবে তা বোঝার জন্য এই সহজ কৌশলটি প্রয়োগ করুন" এটি sendজেনারেটরের পয়েন্টের একটি বিশাল অংশ যা আপনি একটি জেনারেটরে পরিণত করতে পারবেন এই বিষয়টি কি পুরোপুরি উপেক্ষা করে না ?
ড্যানিয়েলস্যাঙ্ক

10
"এটি লুপের জন্য হতে পারে তবে এটি কোডের মতোও হতে পারে otherlist.extend(mylist)" -> এটি ভুল। extend()তালিকায় স্থান পরিবর্তন করে এবং পুনরাবৃত্তযোগ্য ফেরত দেয় না। লুপ ওভার চেষ্টা করা otherlist.extend(mylist)ব্যর্থ হবে TypeErrorকারণ extend()স্পষ্টতই ফিরে আসে None, এবং আপনি লুপ করতে পারবেন না None
পেড্রো

4
@pedro আপনি এই বাক্যটিকে ভুল বুঝেছেন। এর অর্থ হ'ল অজগর এক্সিকিউট করার সময় দুটি উল্লিখিত পদক্ষেপ mylist(চালু নয় otherlist) সম্পাদন করে otherlist.extend(mylist)
আজ

555

এই ভাবে চিন্তা করুন:

একটি পুনরুক্তি করা একটি অবজেক্টের জন্য কেবল একটি অভিনব শব্দ যা একটি next()পদ্ধতি রয়েছে। সুতরাং একটি ফলন-এড ফাংশনটি এরকম কিছু হতে শুরু করে:

মূল সংস্করণ:

def some_function():
    for i in xrange(4):
        yield i

for i in some_function():
    print i

মূলত পাইথন দোভাষী উপরের কোডটি দিয়ে যা করেন:

class it:
    def __init__(self):
        # Start at -1 so that we get 0 when we add 1 below.
        self.count = -1

    # The __iter__ method will be called once by the 'for' loop.
    # The rest of the magic happens on the object returned by this method.
    # In this case it is the object itself.
    def __iter__(self):
        return self

    # The next method will be called repeatedly by the 'for' loop
    # until it raises StopIteration.
    def next(self):
        self.count += 1
        if self.count < 4:
            return self.count
        else:
            # A StopIteration exception is raised
            # to signal that the iterator is done.
            # This is caught implicitly by the 'for' loop.
            raise StopIteration

def some_func():
    return it()

for i in some_func():
    print i

পর্দার আড়ালে কী ঘটছে সে সম্পর্কে আরও অন্তর্দৃষ্টি জন্য forলুপটি এটিতে আবার লেখা যেতে পারে:

iterator = some_func()
try:
    while 1:
        print iterator.next()
except StopIteration:
    pass

এটি কি আরও অর্থবোধ করে বা কেবল আপনাকে আরও বিভ্রান্ত করে? :)

আমি মনে রাখতে হবে এই হল অর্থবোধক উদ্দেশ্যে একটি অতিসরলীকরণ। :)


1
__getitem__পরিবর্তে সংজ্ঞায়িত করা যেতে পারে __iter__। উদাহরণস্বরূপ: class it: pass; it.__getitem__ = lambda self, i: i*10 if i < 10 else [][0]; for i in it(): print(i)এটি মুদ্রণ করবে: 0, 10, 20, ..., 90
jfs

16
আমি পাইথন ৩.6 এ এই উদাহরণটি চেষ্টা করেছিলাম এবং যদি আমি তৈরি করি iterator = some_function()তবে ভেরিয়েবলটির আর iteratorকোনও ফাংশন next()নেই, তবে কেবল একটি __next__()ফাংশন রয়েছে। ভেবেছিলাম আমি এটি উল্লেখ করব।
পিটার

কোথায় forলুপ বাস্তবায়ন আপনি লিখেছেন কল __iter__পদ্ধতি iterator, এর উপস্থাপনার ক্ষেত্রে উদাহরণ হিসেবে বলা যায় it?
সিস্টেমেটিকাল

455

yieldশব্দ দুটো সরল সত্যকে কমে হয়:

  1. যদি সংকলক কোনও ফাংশনের ভিতরে কোথাওyield কীওয়ার্ড সনাক্ত করে , তবে সেই ফাংশনটি আর বিবৃতি দিয়ে ফিরে আসে না । পরিবর্তে , এটি অবিলম্বে জেনারেটর নামক একটি অলস "মুলতুবি তালিকা" অবজেক্টটি ফিরিয়ে দেয়return
  2. একটি জেনারেটর পুনরাবৃত্তিযোগ্য। পুনরাবৃত্তিযোগ্য কী ? এটি নির্দিষ্ট বা ক্রমে প্রতিটি উপাদান দেখার জন্য অন্তর্নির্মিত প্রোটোকল সহ এটি listবা setবা rangeবা ডিক-ভিউয়ের মতো কিছু

সংক্ষেপে: একটি জেনারেটর একটি অলস, ক্রমবর্ধমান-মুলতুবিযুক্ত তালিকা এবং yieldবিবৃতিগুলি আপনাকে জেনারেটরের ক্রমবর্ধমানভাবে থুতু হওয়া উচিত তালিকার মানগুলিকে প্রোগ্রাম করার জন্য ফাংশন স্বরলিপি ব্যবহার করতে দেয়

generator = myYieldingFunction(...)
x = list(generator)

   generator
       v
[x[0], ..., ???]

         generator
             v
[x[0], x[1], ..., ???]

               generator
                   v
[x[0], x[1], x[2], ..., ???]

                       StopIteration exception
[x[0], x[1], x[2]]     done

list==[x[0], x[1], x[2]]

উদাহরণ

আসুন makeRangeপাইথনের মতো একটি ক্রিয়াটি সংজ্ঞায়িত করি range। কলিং makeRange(n)একজন জেনারেটরকে রিটার্ন দেয়:

def makeRange(n):
    # return 0,1,2,...,n-1
    i = 0
    while i < n:
        yield i
        i += 1

>>> makeRange(5)
<generator object makeRange at 0x19e4aa0>

জেনারেটরকে তার মুলতুবি থাকা মানগুলি তত্ক্ষণাত্ ফিরিয়ে দিতে বাধ্য করতে আপনি এটিকে পাস করতে পারেন list()(ঠিক যেমন আপনি কোনও পুনরাবৃত্ত হতে পারে):

>>> list(makeRange(5))
[0, 1, 2, 3, 4]

"কেবল একটি তালিকা ফিরিয়ে দেওয়া" এর সাথে উদাহরণের তুলনা করা

উপরের উদাহরণটি কেবলমাত্র এমন একটি তালিকা তৈরি করা হিসাবে ভাবা যেতে পারে যা আপনি সংযুক্ত করে ফিরে যান:

# list-version                   #  # generator-version
def makeRange(n):                #  def makeRange(n):
    """return [0,1,2,...,n-1]""" #~     """return 0,1,2,...,n-1"""
    TO_RETURN = []               #>
    i = 0                        #      i = 0
    while i < n:                 #      while i < n:
        TO_RETURN += [i]         #~         yield i
        i += 1                   #          i += 1  ## indented
    return TO_RETURN             #>

>>> makeRange(5)
[0, 1, 2, 3, 4]

একটি প্রধান পার্থক্য আছে, যদিও; শেষ বিভাগ দেখুন।


আপনি কীভাবে জেনারেটর ব্যবহার করতে পারেন

একটি পুনরাবৃত্তযোগ্য একটি তালিকা বোঝার শেষ অংশ এবং সমস্ত জেনারেটর পুনরাবৃত্ত হয়, তাই তারা প্রায়শই এর মতো ব্যবহৃত হয়:

#                   _ITERABLE_
>>> [x+10 for x in makeRange(5)]
[10, 11, 12, 13, 14]

জেনারেটরগুলির জন্য আরও ভাল অনুভূতি পেতে, আপনি itertoolsমডিউলটি নিয়ে খেলা করতে পারেন ( ওয়্যারেন্টেড chain.from_iterableহওয়ার পরিবর্তে ব্যবহারের বিষয়টি নিশ্চিত হন chain)। উদাহরণস্বরূপ, আপনি এমনকি অসীম-দীর্ঘ অলস তালিকাগুলি কার্যকর করতে জেনারেটর ব্যবহার করতে পারেন itertools.count()। আপনি আপনার নিজের প্রয়োগ def enumerate(iterable): zip(count(), iterable)করতে পারেন, বা বিকল্পভাবে এটি দিয়ে করতে পারেনyield কিছুক্ষণের মধ্যে কীওয়ার্ডটি ।

দয়া করে নোট করুন: জেনারেটরগুলি বাস্তবে কর্টিনগুলি প্রয়োগ করার মতো আরও অনেক কিছুর জন্য ব্যবহার করা যেতে পারে বা অ- প্রোগ্রামিং বা অন্যান্য মার্জিত জিনিস। তবে, আমি এখানে উপস্থিত "অলস তালিকাগুলি" দৃষ্টিকোণটি হ'ল আপনার সর্বাধিক সাধারণ ব্যবহার।


দৃশ্যের অন্তরালে

এভাবেই "পাইথন পুনরাবৃত্তি প্রোটোকল" কাজ করে। তা হচ্ছে, আপনি যখন করছেন তখন কি চলছে list(makeRange(5))। এটি আমি আগে "অলস, বর্ধিত তালিকা" হিসাবে বর্ণনা করি।

>>> x=iter(range(5))
>>> next(x)
0
>>> next(x)
1
>>> next(x)
2
>>> next(x)
3
>>> next(x)
4
>>> next(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

অন্তর্নির্মিত ফাংশনটি next()কেবলমাত্র অবজেক্টগুলিকে .next()ফাংশন বলে, যা "পুনরাবৃত্তি প্রোটোকলের" অংশ এবং সমস্ত পুনরাবৃত্তকারীগুলিতে পাওয়া যায়। next()সাধারণভাবে পাঠযোগ্যতার ব্যয়ে আপনি অভিনব জিনিসগুলি বাস্তবায়নের জন্য ফাংশনটি (এবং পুনরাবৃত্তির প্রোটোকলের অন্যান্য অংশ) ম্যানুয়ালি ব্যবহার করতে পারেন , তাই এটি করা এড়াতে চেষ্টা করুন ...


ক্ষুদ্র অংশ

সাধারণত, বেশিরভাগ লোকেরা নিম্নলিখিত বর্ণগুলি সম্পর্কে চিন্তা করে না এবং সম্ভবত এখানে পড়া বন্ধ করতে চান।

পাইথন কথা বলার, একটি iterable কোন বস্তুর একটি তালিকা মত "একটি লুপ জন্য ধারণা বুঝতে পারে" হয় [1,2,3], এবং একটি পুনরুক্তিকারীর জন্য-লুপ মত অনুরোধ একটি নির্দিষ্ট উদাহরণস্বরূপ হয় [1,2,3].__iter__()। কোনও জেনারেটর যেকোন পুনরুক্তারের মতো হ'ল এটি লেখার পদ্ধতি ব্যতীত (ফাংশন সিনট্যাক্স সহ)।

আপনি যখন একটি তালিকা থেকে একটি পুনরাবৃত্তির জন্য অনুরোধ করেন, এটি একটি নতুন পুনরুক্তি তৈরি করে। যাইহোক, আপনি যখন কোনও পুনরুক্তিদাতা (যা আপনি খুব কমই করতে পারেন) এর কাছ থেকে পুনরাবৃত্তির জন্য অনুরোধ করেন, এটি কেবল আপনাকে নিজের একটি অনুলিপি দেয়।

সুতরাং, আপনি এই জাতীয় কিছু করতে ব্যর্থ হচ্ছেন এমন সম্ভাবনাময় ইভেন্টে ...

> x = myRange(5)
> list(x)
[0, 1, 2, 3, 4]
> list(x)
[]

... তারপরে মনে রাখবেন যে জেনারেটর একটি পুনরুক্তিকারী ; অর্থাৎ এটি এক সময়ের ব্যবহার one আপনি যদি এটি পুনরায় ব্যবহার করতে চান তবে আপনার myRange(...)আবার কল করা উচিত । আপনার যদি দুবার ফল ব্যবহারের প্রয়োজন হয় তবে ফলাফলটিকে একটি তালিকায় রূপান্তর করুন এবং এটি একটি ভেরিয়েবলের মধ্যে সঞ্চয় করুন x = list(myRange(5))। যাদের একেবারে জেনারেটর ক্লোন করা দরকার (উদাহরণস্বরূপ, যারা ভয়ঙ্করভাবে হ্যাপিশ মেটাট্রোগ্রামিং করছে) itertools.teeতারা একেবারে প্রয়োজনে ব্যবহার করতে পারে , যেহেতু অনুলিপিযোগ্য আয়রেটর পাইথন পিইপি মানক প্রস্তাবটি পিছিয়ে দেওয়া হয়েছে।


377

yieldপাইথনে কীওয়ার্ডটি কী করে ?

উত্তরের রূপরেখা / সংক্ষিপ্তসার

  • yieldযখন ডাকা হয় তখন একটি ফাংশন একটি জেনারেটর প্রদান করে
  • জেনারেটরগুলি পুনরাবৃত্তকারী কারণ তারা পুনরায় প্রোটোকল প্রয়োগ করে , তাই আপনি তাদের উপর পুনরাবৃত্তি করতে পারেন।
  • কোনও জেনারেটরকেও তথ্য প্রেরণ করা যেতে পারে , এটি এটিকে ধারণামূলকভাবে একটি কর্টিন বানিয়ে
  • পাইথন 3 এ, আপনি উভয় দিকেই এক জেনারেটর থেকে অন্য জেনারেটরে প্রতিনিধি দিতে পারেন yield from
  • (পরিশিষ্ট শীর্ষ একগুলি সহ কয়েকটি উত্তর সমালোচনা করে এবং returnজেনারেটরের ব্যবহার সম্পর্কে আলোচনা করে ))

জেনারেটর:

yieldএকটি ফাংশন সংজ্ঞা শুধুমাত্র আইনি অভ্যন্তর, এবং একটি ফাংশন সংজ্ঞা অন্তর্ভুক্তি yieldএটি একটি জেনারেটর ফিরে আসে।

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

yieldনিম্নলিখিত দুটি পদ্ধতির দ্বারা সংজ্ঞায়িত পুনরায় প্রোটোকল প্রয়োগের একটি সহজ উপায় সরবরাহ করে: __iter__এবং next(পাইথন 2) বা __next__(পাইথন 3)। এই দুটি পদ্ধতিই কোনও বস্তুকে একটি পুনরুক্তি করে তোলে যা আপনি মডিউল Iteratorথেকে বিমূর্ত বেস শ্রেণীর সাথে টাইপ-চেক করতে পারেন collections

>>> def func():
...     yield 'I am'
...     yield 'a generator!'
... 
>>> type(func)                 # A function with yield is still a function
<type 'function'>
>>> gen = func()
>>> type(gen)                  # but it returns a generator
<type 'generator'>
>>> hasattr(gen, '__iter__')   # that's an iterable
True
>>> hasattr(gen, 'next')       # and with .next (.__next__ in Python 3)
True                           # implements the iterator protocol.

জেনারেটরের ধরণটি একটি উপ-ধরণের পুনরুক্তি:

>>> import collections, types
>>> issubclass(types.GeneratorType, collections.Iterator)
True

এবং প্রয়োজনে আমরা টাইপ-চেক করতে পারি:

>>> isinstance(gen, types.GeneratorType)
True
>>> isinstance(gen, collections.Iterator)
True

এর বৈশিষ্ট্য Iterator হ'ল একবার নিঃশেষ হয়ে গেলে আপনি এটিকে পুনরায় ব্যবহার বা পুনরায় সেট করতে পারবেন না:

>>> list(gen)
['I am', 'a generator!']
>>> list(gen)
[]

আপনি যদি আবার এর কার্যকারিতাটি ব্যবহার করতে চান তবে আপনাকে অন্যটি তৈরি করতে হবে (পাদটীকা 2 দেখুন):

>>> list(func())
['I am', 'a generator!']

একটি প্রোগ্রাম থেকে ডেটা উত্পন্ন করতে পারে, উদাহরণস্বরূপ:

def func(an_iterable):
    for item in an_iterable:
        yield item

উপরের সাধারণ জেনারেটরটি নীচের সাথেও সমান - পাইথন ৩.৩ অনুসারে (এবং পাইথন ২ তে পাওয়া যায় না), আপনি ব্যবহার করতে পারেন yield from:

def func(an_iterable):
    yield from an_iterable

যাহোক, yield from সাবজিনেটরদের প্রতিনিধিদেরও অনুমতি দেয় যা সাব-কার্টাইন সহ সমবায় প্রতিনিধিদের উপরের অংশে ব্যাখ্যা করা হবে।

Coroutines:

yield জেনারেটরে ডেটা প্রেরণে মঞ্জুরি দেয় এমন অভিব্যক্তি তৈরি করে (পাদটীকা দেখুন 3)

এখানে একটি উদাহরণ রয়েছে, receivedভেরিয়েবলটি নোট করুন , যা জেনারেটরে প্রেরণ করা ডেটার দিকে নির্দেশ করবে:

def bank_account(deposited, interest_rate):
    while True:
        calculated_interest = interest_rate * deposited 
        received = yield calculated_interest
        if received:
            deposited += received


>>> my_account = bank_account(1000, .05)

প্রথমত, আমাদের অবশ্যই জেনারেটরটি বিল্টিন ফাংশন দিয়ে সারি করতে হবে next। আপনি যে পাইথনের সংস্করণটি ব্যবহার করছেন তার উপর নির্ভর করে এটি উপযুক্ত nextবা __next__পদ্ধতিটিকে কল করবে :

>>> first_year_interest = next(my_account)
>>> first_year_interest
50.0

এবং এখন আমরা জেনারেটরে ডেটা প্রেরণ করতে পারি। ( পাঠানো Noneকল করার মতোইnext )):

>>> next_year_interest = my_account.send(first_year_interest + 1000)
>>> next_year_interest
102.5

সহ-Coroutine সমবায় প্রতিনিধি yield from

এখন, প্রত্যাহার করুন যা yield fromপাইথন 3 এ উপলভ্য This এটি আমাদেরকে একটি করকোটিনগুলিকে একটি উপকৌরতে প্রতিনিধিত্ব করতে দেয়:

def money_manager(expected_rate):
    under_management = yield     # must receive deposited value
    while True:
        try:
            additional_investment = yield expected_rate * under_management 
            if additional_investment:
                under_management += additional_investment
        except GeneratorExit:
            '''TODO: write function to send unclaimed funds to state'''
        finally:
            '''TODO: write function to mail tax info to client'''


def investment_account(deposited, manager):
    '''very simple model of an investment account that delegates to a manager'''
    next(manager) # must queue up manager
    manager.send(deposited)
    while True:
        try:
            yield from manager
        except GeneratorExit:
            return manager.close()

এবং এখন আমরা উপ-জেনারেটরের কাছে কার্যকারিতাটি অর্পণ করতে পারি এবং এটি উপরে যেমন ঠিক একটি জেনারেটর ব্যবহার করতে পারে:

>>> my_manager = money_manager(.06)
>>> my_account = investment_account(1000, my_manager)
>>> first_year_return = next(my_account)
>>> first_year_return
60.0
>>> next_year_return = my_account.send(first_year_return + 1000)
>>> next_year_return
123.6

আপনি সুনির্দিষ্ট শব্দার্থবিদ্যা সম্বন্ধে আরও পড়তে পারেন yield fromমধ্যে PEP 380।

অন্যান্য পদ্ধতি: বন্ধ এবং নিক্ষেপ

closeপদ্ধতি উত্থাপন GeneratorExitবিন্দু ফাংশন সঞ্চালনের নিথর হয়েছিল। এটির মাধ্যমেও ডাকা হবে __del__যাতে আপনি যে কোনও ক্লিনআপ কোড রাখতে পারেন যেখানে আপনি হ্যান্ডেল করেন GeneratorExit:

>>> my_account.close()

আপনি একটি ব্যতিক্রম নিক্ষেপ করতে পারেন যা জেনারেটরে পরিচালনা করা যেতে পারে বা ব্যবহারকারীর কাছে আবার প্রচার করা যেতে পারে:

>>> import sys
>>> try:
...     raise ValueError
... except:
...     my_manager.throw(*sys.exc_info())
... 
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
  File "<stdin>", line 2, in <module>
ValueError

উপসংহার

আমি বিশ্বাস করি যে আমি নিম্নলিখিত প্রশ্নের সমস্ত দিক কভার করেছি:

yieldপাইথনে কীওয়ার্ডটি কী করে ?

দেখা যাচ্ছে যে yieldঅনেক কিছু করে। আমি নিশ্চিত যে আমি এটি আরও আরও উদাহরণ উদাহরণ যোগ করতে পারে। আপনি যদি আরও চান বা কিছু গঠনমূলক সমালোচনা করতে চান তবে নীচে মন্তব্য করে আমাকে জানান।


পরিশিষ্ট:

শীর্ষ / স্বীকৃত উত্তরের সমালোচনা **

  • এটি উদাহরণস্বরূপ একটি তালিকা ব্যবহার করে কী পুনরাবৃত্তিযোগ্য তা নিয়ে বিভ্রান্ত হয় । উপরে আমার রেফারেন্স দেখ, কিন্তু সংক্ষেপে বলা: একটি iterable একটি হয়েছে __iter__একটি ফেরার পদ্ধতি পুনরুক্তিকারীর । একটি পুনরাবৃত্তকারী একটি .next(পাইথন 2 বা .__next__(পাইথন 3) পদ্ধতি সরবরাহ করে, যা forউত্থাপিতভাবে লুপগুলি বলা হয় যতক্ষণ না এটি উত্থাপিত হয়StopIteration এবং একবার এটি করা হয়ে গেলে এটি এটি চালিয়ে যেতে থাকবে।
  • এরপরে এটি জেনারেটর কী তা বর্ণনা করার জন্য একটি জেনারেটর এক্সপ্রেশন ব্যবহার করে। যেহেতু কোনও জেনারেটর কেবল একটি পুনরুক্তি তৈরির সুবিধার উপায় , এটি কেবল বিষয়টি বিভ্রান্ত করে এবং আমরা এখনও yieldঅংশে পৌঁছতে পারি নি ।
  • ইন জেনারেটরের নিঃশেষিত নিয়ন্ত্রণ তিনি আহ্বান .nextপদ্ধতি, যখন পরিবর্তে তিনি builtin ফাংশন ব্যবহার করা উচিত, next। এটি ইন্ডিয়ারেশনের উপযুক্ত স্তর হবে কারণ তার কোডটি পাইথন 3 এ কাজ করে না।
  • Itertools? এটি আদৌ কী yieldকরে তার সাথে প্রাসঙ্গিক ছিল না ।
  • পাইথন 3 yield নতুন কার্যকারিতা সহ যে পদ্ধতিগুলি সরবরাহ করে সেগুলির কোনও আলোচনা নয় শীর্ষ / স্বীকৃত উত্তরটি একটি খুব অসম্পূর্ণ উত্তর।yield from

yieldজেনারেটর এক্সপ্রেশন বা বোধগম্যতার জন্য পরামর্শের উত্তরের সমালোচনা ।

ব্যাকরণ বর্তমানে একটি তালিকা বোধগম্যতে যে কোনও প্রকাশের অনুমতি দেয়।

expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) |
                     ('=' (yield_expr|testlist_star_expr))*)
...
yield_expr: 'yield' [yield_arg]
yield_arg: 'from' test | testlist

ফলন যেহেতু একটি অভিব্যক্তি, তাই কিছু এটি দ্বারা উপলব্ধি বা জেনারেটর এক্সপ্রেশন ব্যবহার করা আকর্ষণীয় হিসাবে বিবেচনা করেছে - বিশেষত ভাল ব্যবহারের ক্ষেত্রে উল্লেখ না করেও।

সিপিথন কোর বিকাশকারীরা এর ভাতা হ্রাস করার বিষয়ে আলোচনা করছেন । মেলিং তালিকা থেকে একটি সম্পর্কিত পোস্ট এখানে:

30 জানুয়ারী 2017 এ 19:05 এ ব্রেট ক্যানন লিখেছেন:

সান, 29 জানু 2017 এ 16:39 ক্রেগ রডরিগ লিখেছেন:

আমি উভয় পদ্ধতির সাথে ঠিক আছি। পাইথন 3 এ জিনিসগুলি যেমনভাবে রেখে দেওয়া ঠিক তত ভাল নয়, আইএমএইচও।

আমার ভোটটি সিনট্যাক্স এরর যা আপনি সিনট্যাক্স থেকে যা প্রত্যাশা করছেন তা পাচ্ছেন না।

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

সেখানে যাওয়ার শর্তে, আমরা সম্ভবত চাই:

  • সিনট্যাক্স ওয়ার্নিং বা অবমূল্যায়ন ওয়ার্নিং ৩. 3.
  • 2.3.x এ পাই 3 কে সতর্কতা
  • সিনট্যাক্স এরর 3..৮ এ

চিয়ার্স, নিক।

- নিক কোঘলান | ncoghlan at gmail.com | ব্রিসবেন, অস্ট্রেলিয়া

এছাড়াও, এখানে একটি অসামান্য সমস্যা রয়েছে (10544) যা এটি কখনও ভাল ধারণা না হওয়ার দিকে নির্দেশ করছে বলে মনে হচ্ছে (পাইপাই, পাইথনের লিখিত পাইথন বাস্তবায়ন ইতিমধ্যে সিনট্যাক্স সতর্কতা উত্থাপন করছে।)

নীচের লাইন, সিপিথনের বিকাশকারীরা অন্যথায় আমাদের না বলুন: জেনারেটর এক্সপ্রেশন বা বোধগম্যতা স্থাপন করবেন না yield

returnজেনারেটরের মধ্যে বিবৃতি

ইন পাইথন 2 :

একটি জেনারেটর ফাংশনে, returnবিবৃতিটিকে একটি অন্তর্ভুক্ত করার অনুমতি নেই expression_list। সেই প্রসঙ্গে, একটি খালি returnইঙ্গিত দেয় যে জেনারেটরটি সম্পন্ন হয়েছে এবং StopIterationউত্থাপিত হওয়ার কারণ হবে ।

একটি expression_listমূলত কমা দ্বারা পৃথক এক্সপ্রেশন যে কোন সংখ্যার হয় - মূলত, পাইথন 2 আপনার সাথে জেনারেটরের বন্ধ করতে পারবেন return, কিন্তু আপনি একটি মান করতে পারবে না।

ইন পাইথন 3 :

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

পাদটিকা

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

  2. এর অর্থ হ'ল উদাহরণস্বরূপ, এটি xrange( rangeপুনর 3) এ) বস্তুগুলি Iteratorপুনরায় ব্যবহারযোগ্য হতে পারে, যদিও তা পুনরাবৃত্তিযোগ্য can তালিকার মতো, তাদের __iter__পদ্ধতিগুলি পুনরাবৃত্তকারী বস্তুগুলি ফেরত দেয়।

  3. yieldমূলত একটি বিবৃতি হিসাবে পরিচয় করানো হয়েছিল যার অর্থ এটি কোনও কোড ব্লকের একটি লাইনের শুরুতে উপস্থিত হতে পারে। এখন yieldফলন প্রকাশ করে creates https://docs.python.org/2/references/simple_stmts.html#grammar-token-yeld_stmt এই পরিবর্তনটি কোনও ব্যবহারকারীকে যেমন জেনারেটরে ডেটা প্রেরণ করার অনুমতি দেওয়ার প্রস্তাব দেওয়া হয়েছিল । ডেটা প্রেরণের জন্য, একজনকে অবশ্যই এটি কোনও কিছুতে নির্ধারণ করতে সক্ষম হতে হবে এবং তার জন্য, একটি বিবৃতি ঠিক কাজ করবে না।


328

yieldঠিক যেমন return- এটি আপনি যা বলবেন তা ফিরিয়ে দেয় (জেনারেটর হিসাবে)। পার্থক্যটি হ'ল পরের বার আপনি জেনারেটরটি কল করার সময় শেষ কল থেকে yieldবিবৃতিতে কার্যকর করা শুরু হয় । প্রত্যাবর্তনের মতো নয়, যখন ফলন হয় তখন স্ট্যাক ফ্রেমটি পরিষ্কার হয় না, তবে নিয়ন্ত্রণটি কলারের কাছে ফিরে স্থানান্তরিত হয়, সুতরাং পরবর্তী সময়ে ফাংশনটি ডাকা হলে তার রাজ্য পুনরায় শুরু হবে।

আপনার কোডের ক্ষেত্রে, ফাংশনটি get_child_candidatesএকটি পুনরাবৃত্তির মতো কাজ করছে যাতে আপনি যখন আপনার তালিকাটি প্রসারিত করেন, এটি নতুন তালিকায় একবারে একটি উপাদান যুক্ত করে।

list.extendক্লান্ত না হওয়া অবধি কোনও পুনরাবৃত্তিকারীকে কল করে। আপনার পোস্ট করা কোড নমুনার ক্ষেত্রে, কেবলমাত্র একটি টুপল ফিরিয়ে দেওয়া এবং এটি তালিকায় যুক্ত করা আরও পরিষ্কার হবে much


107
এটি কাছাকাছি, তবে সঠিক নয়। আপনি যখনই কোনও ফাংশনটিতে একটি উত্পাদ বিবৃতি দিয়ে কল করবেন, এটি একেবারে নতুন জেনারেটর বস্তুটি ফিরিয়ে দেয়। এটি কেবলমাত্র যখন আপনি সেই জেনারেটরের .next () পদ্ধতিটি কল করেন যা শেষ ফলনের পরে পুনরায় কার্যকর হয়।
কুরোশ

239

এখানে উল্লেখ করার মতো আরও একটি জিনিস রয়েছে: একটি ফাংশন যা ফল দেয় তা আসলে শেষ করতে হয় না। আমি এই জাতীয় কোড লিখেছি:

def fib():
    last, cur = 0, 1
    while True: 
        yield cur
        last, cur = cur, last + cur

তারপরে আমি এটি অন্য কোডে এটি ব্যবহার করতে পারি:

for f in fib():
    if some_condition: break
    coolfuncs(f);

এটি সত্যিই কিছু সমস্যা সরল করতে সহায়তা করে এবং কিছু জিনিসকে কাজ করা সহজ করে তোলে।


233

যারা নূন্যতম কাজের উদাহরণ পছন্দ করেন তাদের জন্য এই ইন্টারেক্টিভ পাইথন সেশনে ধ্যান করুন:

>>> def f():
...   yield 1
...   yield 2
...   yield 3
... 
>>> g = f()
>>> for i in g:
...   print(i)
... 
1
2
3
>>> for i in g:
...   print(i)
... 
>>> # Note that this time nothing was printed

208

টি এল; ডিআর

এর পরিবর্তে:

def square_list(n):
    the_list = []                         # Replace
    for x in range(n):
        y = x * x
        the_list.append(y)                # these
    return the_list                       # lines

এটা কর:

def square_yield(n):
    for x in range(n):
        y = x * x
        yield y                           # with this one.

যখনই আপনি নিজেকে স্ক্র্যাচ থেকে একটি তালিকা তৈরি করতে দেখেন, yieldপরিবর্তে প্রতিটি টুকরো।

ফলন সহ এটি আমার প্রথম "আহা" মুহুর্ত ছিল।


yieldএকটি হল শর্করাবৎ পথ বলতে

স্টাফ একটি সিরিজ নির্মাণ

একই আচরণ:

>>> for square in square_list(4):
...     print(square)
...
0
1
4
9
>>> for square in square_yield(4):
...     print(square)
...
0
1
4
9

বিভিন্ন আচরণ:

ফলন একক-পাস : আপনি কেবল একবারে পুনরাবৃত্তি করতে পারেন। যখন কোনও ফাংশনে এর ফলন হয় তখন আমরা একে জেনারেটর ফাংশন বলি । এবং একটি পুনরাবৃত্তি এটি ফিরে আসে। সেই পদগুলি প্রকাশ করছে। আমরা একটি ধারকটির সুবিধাদি হারাতে চাই, তবে প্রয়োজনীয় হিসাবে গণনা করা এবং নির্বিচারে দীর্ঘায়িত একটি সিরিজের শক্তি অর্জন করি।

ফলন অলস , এটি গণনা বন্ধ করে দেয়। এতে ফলন সহ একটি ফাংশন আপনি যখন কল করবেন তখন আসলে কার্যকর হয় না। এটি একটি পুনরাবৃত্তিকারী বস্তুটি ফেরত দেয় যা এটি কোথায় রেখে গিয়েছিল তা মনে রাখে। প্রতিবার আপনি next()পুনরুক্তিকারীকে কল করুন (এটি একটি লুপে ঘটে) এক্সিকিউশন ইঞ্চি পরবর্তী ফলনের দিকে এগিয়ে। returnস্টপ ইন্টেরেশন উত্থাপন করে এবং সিরিজটি শেষ করে (এটি লুপের প্রাকৃতিক সমাপ্তি)।

ফলন বহুমুখী । ডেটা সব একসাথে সংরক্ষণ করতে হবে না, এটি একবারে একটি উপলভ্য করা যেতে পারে। এটি অসীম হতে পারে।

>>> def squares_all_of_them():
...     x = 0
...     while True:
...         yield x * x
...         x += 1
...
>>> squares = squares_all_of_them()
>>> for _ in range(4):
...     print(next(squares))
...
0
1
4
9

আপনার যদি একাধিক পাসের প্রয়োজন হয় এবং সিরিজটি খুব বেশি দীর্ঘ না list()হয় তবে কেবল এটির জন্য কল করুন:

>>> list(square_yield(4))
[0, 1, 4, 9]

শব্দটির উজ্জ্বল পছন্দ yieldকারণ উভয় অর্থই প্রযোজ্য:

ফলন - উত্পাদন বা সরবরাহ (কৃষিতে হিসাবে)

... সিরিজের পরবর্তী তথ্য সরবরাহ করুন।

ফলন - উপায় দিন বা ত্যাগ (রাজনৈতিক ক্ষমতায় যেমন)

... পুনরাবৃত্তি অগ্রসর হওয়া অবধি সিপিইউ কার্যকর করা ত্যাগ করুন।


194

ফলন আপনাকে একটি জেনারেটর দেয়।

def get_odd_numbers(i):
    return range(1, i, 2)
def yield_odd_numbers(i):
    for x in range(1, i, 2):
       yield x
foo = get_odd_numbers(10)
bar = yield_odd_numbers(10)
foo
[1, 3, 5, 7, 9]
bar
<generator object yield_odd_numbers at 0x1029c6f50>
bar.next()
1
bar.next()
3
bar.next()
5

আপনি দেখতে পাচ্ছেন, প্রথম ক্ষেত্রে fooএকবারে মেমরির পুরো তালিকাটি ধারণ করে। 5 টি উপাদান সহ একটি তালিকার পক্ষে এটি বড় কথা নয়, তবে আপনি যদি 5 মিলিয়ন তালিকা চান? এটি কেবল বিশাল মেমরির খাওয়ারই নয়, ফাংশনটি ডাকা হওয়ার সময় এটি তৈরি করতেও অনেক সময় ব্যয় হয়।

দ্বিতীয় ক্ষেত্রে, barআপনাকে কেবল একটি জেনারেটর দেয়। একটি জেনারেটর একটি পুনরাবৃত্তিযোগ্য - যার অর্থ আপনি এটি একটি forলুপ ইত্যাদিতে ব্যবহার করতে পারেন তবে প্রতিটি মান কেবল একবারে অ্যাক্সেস করা যায়। সমস্ত মান একই সাথে মেমরিতে সঞ্চয় করা হয় না; জেনারেটর অবজেক্টটি "মনে রাখে" যেখানে আপনি এটি শেষবারের মতো ডেকেছিলেন - এইভাবে, আপনি যদি 50 মিলিয়ন হিসাবে গণ্য করতে (যদি) একটি পুনরাবৃত্তি ব্যবহার করেন তবে আপনাকে সমস্ত 50 বিলিয়ন গণনা করতে হবে না একবারে এবং 50 টি বিলিয়ন সংখ্যার মাধ্যমে গণনা করতে সঞ্চয় করুন।

আবার, এটি একটি দুর্দান্ত কনট্রিভাইড উদাহরণ, আপনি যদি সত্যিই 50 বিলিয়নতে গণনা করতে চান তবে আপনি সম্ভবত এটির ব্যবহার করতে পারবেন। :)

এটি জেনারেটরের সবচেয়ে সাধারণ ব্যবহারের কেস। যেমনটি আপনি বলেছিলেন, দক্ষ ফলক লেখার জন্য এটি কল স্ট্যাকের মাধ্যমে কিছু ধরণের স্ট্যাক ভেরিয়েবল ব্যবহার না করে ফলন ব্যবহার করে ব্যবহার করা যেতে পারে। জেনারেটরগুলি বিশেষত গাছের ট্র্যাভারসাল এবং অন্যান্য সমস্ত জিনিসের জন্য ব্যবহার করা যেতে পারে।


পাইথন 3-তে কেবল একটি নোট, rangeতালিকার পরিবর্তে একটি জেনারেটরও ফিরিয়ে দেয়, সুতরাং আপনিও অনুরূপ ধারণাটি দেখতে পাবেন __repr__/ __str__এই ক্ষেত্রে, একটি ভাল ফলাফল দেখাতে ওভাররাইড করা বাদে range(1, 10, 2)
ইটনাটালি

189

এটি একটি জেনারেটর ফিরিয়ে দিচ্ছে। আমি পাইথনের সাথে বিশেষভাবে পরিচিত নই, তবে আমি বিশ্বাস করি যে আপনি যদি সেগুলির সাথে পরিচিত হন তবে এটি সি # এর পুনরাবৃত্তকারী ব্লকগুলির মতো একই জিনিস ।

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


167

জেনারেটরগুলি কীভাবে ব্যবহার করতে হয় তার বর্ণনা দেয় এমন অনেক দুর্দান্ত উত্তরগুলির মধ্যে একটি ধরণের উত্তর এখনও আমার দেওয়া অনুভূত হয় না। এখানে প্রোগ্রামিং ভাষার তত্ত্বের উত্তর:

yieldপাইথন মধ্যে বিবৃতি জেনারেটরের ফেরৎ। পাইথনের একটি জেনারেটর একটি ফাংশন যা ধারাবাহিকতাগুলি ফিরিয়ে দেয় (এবং বিশেষত এক ধরণের কর্টিন, তবে ধারাবাহিকতা আরও কী ঘটছে তা বোঝার জন্য আরও সাধারণ ব্যবস্থার প্রতিনিধিত্ব করে)।

প্রোগ্রামিং ল্যাঙ্গুয়েজ তত্ত্বের ধারাবাহিকতা অনেক বেশি মৌলিক ধরণের গণনা, তবে এগুলি প্রায়শই ব্যবহৃত হয় না, কারণ এগুলি সম্পর্কে তর্ক করা অত্যন্ত কঠিন এবং বাস্তবায়ন করা খুব কঠিন। কিন্তু ধারাবাহিকতা কী তা ধারণাটি সহজ is এটি একটি গণনার অবস্থা যা এখনও শেষ হয়নি। এই অবস্থায় ভেরিয়েবলের বর্তমান মানগুলি, যে ক্রিয়াকলাপগুলি এখনও করা হয়নি এবং সেগুলি সংরক্ষণ করা হয়। তারপরে প্রোগ্রামের কোনও এক পর্যায়ে ধারাবাহিকতাটি আহ্বান করা যেতে পারে, যেমন প্রোগ্রামের ভেরিয়েবলগুলি সেই অবস্থায় পুনরায় সেট করা হয় এবং সেভ করা অপারেশনগুলি সঞ্চালিত হয়।

ধারাবাহিকতা, এই আরও সাধারণ আকারে, দুটি উপায়ে প্রয়োগ করা যেতে পারে। ইন call/ccপথ, প্রোগ্রামের স্ট্যাক আক্ষরিক সংরক্ষিত হয় এবং তারপর যখন ধারাবাহিকতা প্রার্থনা করা হয়, স্ট্যাক পুনরুদ্ধার করা হয়।

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

def save_file(filename):
  def write_file_continuation():
    write_stuff_to_file(filename)

  check_if_file_exists_and_user_wants_to_overwrite(write_file_continuation)

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

এই পোস্টের বাকী অংশগুলি সাধারণতার ক্ষতি ছাড়াই সিপিএস হিসাবে ধারাবাহিকতাটিকে ধারণাগত করে তুলবে, কারণ এটি বোঝা এবং পড়া খুব সহজ কাজ easier


এখন পাইথনে জেনারেটর সম্পর্কে কথা বলা যাক। জেনারেটর ধারাবাহিকতার একটি নির্দিষ্ট উপপ্রকার। যখন ধারাবাহিকতাগুলি সাধারণভাবে একটি গণনার অবস্থা বাঁচাতে সক্ষম হয় (যেমন, প্রোগ্রামটির কল স্ট্যাক), জেনারেটর কেবল একটি পুনরুক্তির উপরে পুনরাবৃত্তির অবস্থা সংরক্ষণ করতে সক্ষম । যদিও, জেনারেটরগুলির নির্দিষ্ট ব্যবহারের ক্ষেত্রে এই সংজ্ঞাটি সামান্য বিভ্রান্তিকর। এই ক্ষেত্রে:

def f():
  while True:
    yield 4

এটি স্পষ্টভাবে একটি যুক্তিসঙ্গত পুনরাবৃত্ত যাঁর আচরণ ভালভাবে সংজ্ঞায়িত - প্রতিটি বার জেনারেটর এটির উপরে পুনরাবৃত্তি করে, এটি 4 ফেরায় (এবং এটি চিরকালের জন্য করে)। তবে এটি সম্ভবত পুনরাবৃত্তির প্রোটোটাইপিকাল টাইপ নয় যা পুনরাবৃত্তকারীদের (যেমন, for x in collection: do_something(x)) চিন্তা করার সময় মনে আসে । এই উদাহরণটি জেনারেটরের শক্তি চিত্রিত করে: যদি কোনও পুনরুক্তিকারী হয় তবে একটি জেনারেটর তার পুনরাবৃত্তির পরিস্থিতি সংরক্ষণ করতে পারে।

পুনরাবৃত্তি করার জন্য: ধারাবাহিকতা কোনও প্রোগ্রামের স্ট্যাকের রাজ্যকে বাঁচাতে পারে এবং জেনারেটর পুনরাবৃত্তির পরিস্থিতি সংরক্ষণ করতে পারে। এর অর্থ জেনারেটরের তুলনায় ধারাবাহিকতা অনেক বেশি শক্তিশালী, তবে জেনারেটরগুলি অনেক অনেক সহজ easier ভাষা ডিজাইনারের জন্য এটি প্রয়োগ করা আরও সহজ, এবং প্রোগ্রামারটি ব্যবহার করা তাদের পক্ষে সহজ (যদি আপনার জ্বলতে কিছু সময় থাকে, ধারাবাহিকতা এবং কল / সিসি সম্পর্কে এই পৃষ্ঠাটি পড়ার এবং বুঝতে চেষ্টা করুন )।

তবে আপনি জেনারেটরগুলি সহজেই চালিয়ে যাওয়ার স্টাইলের একটি সাধারণ, নির্দিষ্ট ক্ষেত্রে হিসাবে জেনারেটরগুলি সহজেই প্রয়োগ করতে পারেন (এবং ধারণাটি তৈরি করতে পারেন):

যখনই yieldডাকা হয়, এটি ক্রিয়াকে একটি ধারাবাহিকতা ফিরিয়ে আনতে বলে। যখন ফাংশনটি আবার কল করা হয়, এটি যেখানেই ছেড়ে যায় সেখান থেকে এটি শুরু হয়। সুতরাং, সিউডো-সিউডোকোডে (যেমন, সিউডোকোড নয়, তবে কোড নয়) জেনারেটরের nextপদ্ধতিটি মূলত:

class Generator():
  def __init__(self,iterable,generatorfun):
    self.next_continuation = lambda:generatorfun(iterable)

  def next(self):
    value, next_continuation = self.next_continuation()
    self.next_continuation = next_continuation
    return value

যেখানে মূল yieldশব্দটি বাস্তব জেনারেটর ফাংশনের জন্য সিনট্যাকটিক চিনি, মূলত এরকম কিছু:

def generatorfun(iterable):
  if len(iterable) == 0:
    raise StopIteration
  else:
    return (iterable[0], lambda:generatorfun(iterable[1:]))

মনে রাখবেন এটি কেবল সিউডোকোড এবং পাইথনে জেনারেটরগুলির প্রকৃত বাস্তবায়ন আরও জটিল। কী চলছে তা বোঝার অনুশীলন হিসাবে, yieldকীওয়ার্ডটি ব্যবহার না করে জেনারেটর অবজেক্টগুলি বাস্তবায়নের জন্য ধারাবাহিকতা পাসিং স্টাইলটি ব্যবহার করার চেষ্টা করুন ।


152

এখানে সরল ভাষার উদাহরণ রয়েছে is আমি নিম্ন-স্তরের পাইথন ধারণাগুলিতে উচ্চ স্তরের মানব ধারণার মধ্যে একটি চিঠিপত্র সরবরাহ করব।

আমি সংখ্যার ধারাবাহিকতায় পরিচালনা করতে চাই, তবে আমি এই ক্রমটি তৈরি করার সাথে আমার নিজের ক্ষতি করতে চাই না, আমি কেবল যে অপারেশনটি করতে চাই তার উপর দৃষ্টি নিবদ্ধ করতে চাই। সুতরাং, আমি নিম্নলিখিতটি করি:

  • আমি আপনাকে ফোন করে বলছি যে আমি সংখ্যার একটি ক্রম চাই যা একটি নির্দিষ্ট উপায়ে উত্পাদিত হয় এবং আমি আপনাকে আলগোরিদম কী তা জানাতে চাই।
    এই পদক্ষেপটি defজেনারেটরের ফাংশনটি ইনিংয়ের সাথে সম্পর্কিত , যেমন ফাংশনটিতে একটি yield
  • কিছুক্ষণ পরে, আমি আপনাকে বলছি, "ঠিক আছে, আমাকে সংখ্যার ক্রমটি বলার জন্য প্রস্তুত হন"।
    এই পদক্ষেপটি জেনারেটরের ক্রিয়াকলাপের সাথে মিল রাখে যা একটি জেনারেটরের বস্তুটি দেয়। মনে রাখবেন আপনি আমাকে এখনও কোনও সংখ্যা বলবেন না; আপনি শুধু আপনার কাগজ এবং পেন্সিল দখল।
  • আমি আপনাকে জিজ্ঞাসা করছি, "আমাকে পরবর্তী নম্বরটি বলুন", এবং আপনি আমাকে প্রথম সংখ্যাটি বলবেন; তারপরে, আপনি পরবর্তী নম্বরের জন্য আমার কাছে অপেক্ষা করার জন্য অপেক্ষা করুন। আপনি কোথায় ছিলেন, কোন সংখ্যা আপনি ইতিমধ্যে বলেছেন এবং পরবর্তী নম্বরটি কী তা মনে রাখা আপনার কাজ। আমি বিশদ সম্পর্কে যত্ন নেই।
    এই পদক্ষেপটি .next()জেনারেটর অবজেক্টে কল করার অনুরূপ ।
  • … আগের পদক্ষেপটি পুনরাবৃত্তি করুন, যতক্ষণ না…
  • অবশেষে, আপনি শেষ হতে পারে। আপনি আমাকে একটি সংখ্যা বলবেন না; আপনি কেবল চিৎকার করেন, "আপনার ঘোড়াগুলি ধরে রাখুন! আমি শেষ হয়ে গিয়েছি! আর সংখ্যা নেই!"
    এই পদক্ষেপটি জেনারেটরের সাথে তার কাজ শেষ হওয়া অবজেক্টের সাথে মিলে যায় এবং একটি StopIterationব্যতিক্রম উত্থাপন জেনারেটরের ফাংশনটি ব্যতিক্রম বাড়ানোর প্রয়োজন হয় না। এটি স্বয়ংক্রিয়ভাবে উত্থাপিত হয় যখন ফাংশনটি শেষ হয় বা একটি জারি করে return

এটি একটি জেনারেটর কী করে (একটি ফাংশন যা একটি রয়েছে yield); এটি কার্যকর করা শুরু করে, যখনই এটি করে তখনও বিরতি দেয় yieldএবং যখন কোনও .next()মান জিজ্ঞাসা করা হয় তখন এটি সর্বশেষটি থেকে অব্যাহত থাকে। পাইথনের পুনরাবৃত্ত প্রোটোকলের সাথে নকশায় এটি পুরোপুরি ফিট করে, যা ক্রমানুসারে মানগুলি অনুরোধ করার পদ্ধতি বর্ণনা করে।

আইটিরেটর প্রোটোকলের সর্বাধিক বিখ্যাত ব্যবহারকারী হলেন forপাইথনের কমান্ড। সুতরাং, যখনই আপনি এটি করেন:

for item in sequence:

উপরে বর্ণিত মত sequenceএকটি তালিকা, একটি স্ট্রিং, অভিধান বা জেনারেটর অবজেক্ট কিনা তা বিবেচ্য নয় ; ফলাফল একই: আপনি একের পর এক ক্রম থেকে আইটেমগুলি পড়েন।

নোট করুন যে defকোনও ফাংশনটিতে একটি yieldকীওয়ার্ড রয়েছে তা কেবল জেনারেটর তৈরি করার উপায় নয়; এটি তৈরির পক্ষে সহজতম উপায়।

আরও সঠিক তথ্যের জন্য পাইথন ডকুমেন্টেশনে পুনরুক্তি প্রকারের , ফলন বিবরণী এবং জেনারেটর সম্পর্কে পড়ুন ।


130

যদিও অনেকগুলি উত্তর দেখায় যে আপনি কেন yieldএকটি জেনারেটর তৈরি করতে একটি ব্যবহার করবেন, এর জন্য আরও ব্যবহার রয়েছে yield। এটি একটি কর্টিন তৈরি করা বেশ সহজ, যা দুটি ব্লকের কোডের মধ্যে তথ্য প্রেরণকে সক্ষম করে। yieldজেনারেটর তৈরি করতে ব্যবহারের বিষয়ে ইতিমধ্যে যে সূক্ষ্ম উদাহরণ দেওয়া হয়েছে তার কোনও পুনরাবৃত্তি করব না ।

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

উদাহরণ স্বরূপ:

>>> def coroutine():
...     i = -1
...     while True:
...         i += 1
...         val = (yield i)
...         print("Received %s" % val)
...
>>> sequence = coroutine()
>>> sequence.next()
0
>>> sequence.next()
Received None
1
>>> sequence.send('hello')
Received hello
2
>>> sequence.close()

চতুর! একটি ট্রাম্পোলিন (লিস্প অর্থে)। প্রায়শই কেউ সেগুলি দেখতে পায় না!
00 প্রোমিথিউস

129

এর অন্য yieldব্যবহার এবং অর্থ রয়েছে (পাইথন ৩.৩ থেকে):

yield from <expr>

পিইপি 380 থেকে - একজন সাবজিনেটরে ডেলিগেটের জন্য সিনট্যাক্স :

একটি জেনারেটরের জন্য তার ক্রিয়াকলাপের কিছু অংশ অন্য জেনারেটরে দেওয়ার জন্য একটি সিনট্যাক্স প্রস্তাব করা হয়। এটি 'ফলন' সম্বলিত কোডের একটি বিভাগকে ফ্যাক্টর আউট এবং অন্য জেনারেটরে রাখার অনুমতি দেয়। তদ্ব্যতীত, subgenerator একটি মান সঙ্গে ফিরে অনুমতি দেওয়া হয়, এবং মান প্রতিনিধি জেনারেটর উপলব্ধ করা হয়।

নতুন সিনট্যাক্স অপ্টিমাইজেশনের জন্য কিছু সুযোগও উন্মুক্ত করে যখন একটি জেনারেটর অন্যের দ্বারা উত্পাদিত মানগুলি পুনরায় লাভ করে।

তবুও এটি পরিচয় করিয়ে দেবে (পাইথন ৩.৫ থেকে):

async def new_coroutine(data):
   ...
   await blocking_action()

নিয়মিত জেনারেটরের সাথে বিভ্রান্ত হওয়ার কারণে করোটিনগুলি এড়াতে (আজ yieldউভয় ক্ষেত্রে ব্যবহৃত হয়)।


117

সমস্ত দুর্দান্ত উত্তর, তবে নতুনদের জন্য কিছুটা কঠিন।

আমি ধরে নিচ্ছি আপনি returnবিবৃতিটি শিখেছেন ।

উপমা হিসাবে returnএবং yieldযমজ হয়। returnঅর্থ 'ফিরে আসা এবং থামুন' যেখানে 'ফলন' এর অর্থ 'ফিরে আসা, তবে চালিয়ে যান'

  1. এর সাথে একটি নাম_লিস্ট পাওয়ার চেষ্টা করুন return
def num_list(n):
    for i in range(n):
        return i

চালাও এটা:

In [5]: num_list(3)
Out[5]: 0

দেখুন, আপনি তাদের তালিকার চেয়ে কেবল একটি একক সংখ্যা পান। returnআপনাকে কখনই সুখে জয় লাভ করতে দেয় না, কেবল একবার প্রয়োগ করে প্রস্থান করে।

  1. আসে yield

প্রতিস্থাপন returnসঙ্গে yield:

In [10]: def num_list(n):
    ...:     for i in range(n):
    ...:         yield i
    ...:

In [11]: num_list(3)
Out[11]: <generator object num_list at 0x10327c990>

In [12]: list(num_list(3))
Out[12]: [0, 1, 2]

এখন, আপনি সমস্ত নম্বর পেতে জয়।

returnযা একবার চালায় এবং থামবে তার সাথে তুলনা করুন , yieldআপনি প্ল্যান করেছেন এমন সময় বার করে। আপনি returnহিসাবে return one of themএবং yieldহিসাবে ব্যাখ্যা করতে পারেন return all of them। একে বলা হয় iterable

  1. আরও একটি পদক্ষেপের সাথে আমরা yieldবিবৃতিটি পুনরায় লিখতে পারিreturn
In [15]: def num_list(n):
    ...:     result = []
    ...:     for i in range(n):
    ...:         result.append(i)
    ...:     return result

In [16]: num_list(3)
Out[16]: [0, 1, 2]

এটি সম্পর্কে মূল yield

একটি তালিকা returnআউটপুট এবং অবজেক্ট yieldআউটপুট মধ্যে পার্থক্য:

আপনি সর্বদা তালিকা অবজেক্ট থেকে [0, 1, 2] পাবেন তবে কেবল yieldএকবার ' অবজেক্ট আউটপুট' থেকে তাদের পুনরুদ্ধার করতে পারেন । সুতরাং, এটি generatorপ্রদর্শিত হিসাবে একটি নতুন নাম অবজেক্ট আছে Out[11]: <generator object num_list at 0x10327c990>

উপসংহারে, এটি ছাঁটাই করার রূপক হিসাবে:

  • returnএবং yieldযমজ
  • listএবং generatorযমজ

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

104

জেনারেটরগুলি বাস্তবে কীভাবে বাস্তবায়ন করা যায় তার কয়েকটি পাইথনের উদাহরণ এখানে দেওয়া হয়েছে যেন পাইথন তাদের জন্য সিনট্যাকটিক চিনি সরবরাহ করেনি:

পাইথন জেনারেটর হিসাবে:

from itertools import islice

def fib_gen():
    a, b = 1, 1
    while True:
        yield a
        a, b = b, a + b

assert [1, 1, 2, 3, 5] == list(islice(fib_gen(), 5))

জেনারেটরের পরিবর্তে লেক্সিকাল ক্লোজার ব্যবহার করা

def ftake(fnext, last):
    return [fnext() for _ in xrange(last)]

def fib_gen2():
    #funky scope due to python2.x workaround
    #for python 3.x use nonlocal
    def _():
        _.a, _.b = _.b, _.a + _.b
        return _.a
    _.a, _.b = 0, 1
    return _

assert [1,1,2,3,5] == ftake(fib_gen2(), 5)

জেনারেটরের পরিবর্তে অবজেক্ট ক্লোজার ব্যবহার করা হচ্ছে (কারণ ক্লোজারসঅ্যান্ডঅবজেক্টসএরএকুইভ্যালেন্ট )

class fib_gen3:
    def __init__(self):
        self.a, self.b = 1, 1

    def __call__(self):
        r = self.a
        self.a, self.b = self.b, self.a + self.b
        return r

assert [1,1,2,3,5] == ftake(fib_gen3(), 5)

97

আমি জেনারেটরগুলির দ্রুত বিবরণের জন্য "বেজলির 'পাইথন: প্রয়োজনীয় রেফারেন্সের পৃষ্ঠা 19 পড়তে যাচ্ছি", তবে আরও অনেকে ইতিমধ্যে ভাল বিবরণ পোস্ট করেছেন।

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

জেনারেটর এবং করোটিনগুলি ডেটা-প্রবাহের ধরণের অ্যাপ্লিকেশনগুলি সেট আপ করার একটি দুর্দান্ত উপায়। আমি ভেবেছিলাম yieldফাংশনগুলিতে বিবৃতিটির অন্যান্য ব্যবহার সম্পর্কে জানার পক্ষে এটি সার্থক হবে ।


97

প্রোগ্রামিং দৃষ্টিকোণ থেকে, পুনরুক্তিকারীদের থুনস হিসাবে প্রয়োগ করা হয়

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

http://en.wikipedia.org/wiki/Message_passing

" পরবর্তী " হ'ল " ইটার " কল দ্বারা নির্মিত একটি ক্লোজারে পাঠানো একটি বার্তা ।

এই গণনা বাস্তবায়নের জন্য প্রচুর উপায় রয়েছে। আমি মিউটেশনটি ব্যবহার করেছি, তবে বর্তমান মান এবং পরবর্তী যিল্ডারকে ফিরিয়ে এনে রূপান্তর ছাড়াই এটি করা সহজ।

এখানে একটি বিক্ষোভ রয়েছে যা R6RS এর কাঠামো ব্যবহার করে তবে শব্দার্থকগুলি পাইথনের সাথে একেবারে অভিন্ন। এটি গণনার একই মডেল, পাইথনটিতে এটি পুনরায় লেখার জন্য কেবল সিনট্যাক্সের পরিবর্তন দরকার।

Welcome to Racket v6.5.0.3.

-> (define gen
     (lambda (l)
       (define yield
         (lambda ()
           (if (null? l)
               'END
               (let ((v (car l)))
                 (set! l (cdr l))
                 v))))
       (lambda(m)
         (case m
           ('yield (yield))
           ('init  (lambda (data)
                     (set! l data)
                     'OK))))))
-> (define stream (gen '(1 2 3)))
-> (stream 'yield)
1
-> (stream 'yield)
2
-> (stream 'yield)
3
-> (stream 'yield)
'END
-> ((stream 'init) '(a b))
'OK
-> (stream 'yield)
'a
-> (stream 'yield)
'b
-> (stream 'yield)
'END
-> (stream 'yield)
'END
->

84

এখানে একটি সহজ উদাহরণ:

def isPrimeNumber(n):
    print "isPrimeNumber({}) call".format(n)
    if n==1:
        return False
    for x in range(2,n):
        if n % x == 0:
            return False
    return True

def primes (n=1):
    while(True):
        print "loop step ---------------- {}".format(n)
        if isPrimeNumber(n): yield n
        n += 1

for n in primes():
    if n> 10:break
    print "wiriting result {}".format(n)

আউটপুট:

loop step ---------------- 1
isPrimeNumber(1) call
loop step ---------------- 2
isPrimeNumber(2) call
loop step ---------------- 3
isPrimeNumber(3) call
wiriting result 3
loop step ---------------- 4
isPrimeNumber(4) call
loop step ---------------- 5
isPrimeNumber(5) call
wiriting result 5
loop step ---------------- 6
isPrimeNumber(6) call
loop step ---------------- 7
isPrimeNumber(7) call
wiriting result 7
loop step ---------------- 8
isPrimeNumber(8) call
loop step ---------------- 9
isPrimeNumber(9) call
loop step ---------------- 10
isPrimeNumber(10) call
loop step ---------------- 11
isPrimeNumber(11) call

আমি পাইথন বিকাশকারী নই, তবে এটি আমার yieldকাছে প্রোগ্রাম প্রবাহের অবস্থান এবং পরবর্তী লুপটি "ফলন" অবস্থান থেকে শুরু করে বলে মনে হচ্ছে। দেখে মনে হচ্ছে এটি এই অবস্থানে অপেক্ষা করছে এবং তার ঠিক আগে, বাইরে কোনও মান ফেরত দেওয়া এবং পরের বার কাজ চালিয়ে যাওয়া।

এটি একটি আকর্ষণীয় এবং দুর্দান্ত দক্ষতা বলে মনে হচ্ছে: ডি


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

68

এখানে কি yieldকরে তার একটি মানসিক চিত্র রয়েছে ।

আমি কোনও থ্রেডটিকে স্ট্যাকের মতো মনে করতে চাই (এমনকি যখন এটি সেভাবে প্রয়োগ করা হয় না)।

যখন কোনও সাধারণ ফাংশন বলা হয়, তখন এটি স্থানীয় ভেরিয়েবলগুলি স্ট্যাকের উপরে রাখে, কিছু গণনা করে, তারপর স্ট্যাকটি সাফ করে এবং ফিরে আসে। এর স্থানীয় ভেরিয়েবলের মানগুলি আর কখনও দেখা যায় না।

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

সুতরাং এটি হ'ল একধরণের হিমশীতল ফাংশন যা জেনারেটরটি ঝুলছে।

next()পরবর্তীকালে যখন ডাকা হয়, এটি স্ট্যাকের মধ্যে ফাংশনটির জিনিসপত্রগুলি পুনরুদ্ধার করে এবং এটিকে পুনরায় প্রাণবন্ত করে তোলে। ফাংশনটি যেখান থেকে এটি ছেড়ে গেছে সেখান থেকে গণনা অব্যাহত রেখেছে, এটি শীতল স্টোরেজে অনন্তকাল অতিবাহিত করেছিল তা অবজ্ঞাত।

নিম্নলিখিত উদাহরণগুলির সাথে তুলনা করুন:

def normalFunction():
    return
    if False:
        pass

def yielderFunction():
    return
    if False:
        yield 12

আমরা যখন দ্বিতীয় ফাংশনটি কল করি তখন এটি প্রথমটির সাথে খুব আলাদা আচরণ করে। yieldবিবৃতি অনধিগম্য হতে পারে, কিন্তু যদি এটা বর্তমান যে কোন জায়গায়, এটি কি আমরা সঙ্গে লেনদেন করছেন প্রকৃতি পরিবর্তন।

>>> yielderFunction()
<generator object yielderFunction at 0x07742D28>

কলিং yielderFunction()এর কোড চালায় না, তবে কোড থেকে জেনারেটর তৈরি করে। ( yielderপঠনযোগ্যতার জন্য এ জাতীয় জিনিসগুলির উপসর্গের সাথে নামকরণ করা ভাল ধারণা ))

>>> gen = yielderFunction()
>>> dir(gen)
['__class__',
 ...
 '__iter__',    #Returns gen itself, to make it work uniformly with containers
 ...            #when given to a for loop. (Containers return an iterator instead.)
 'close',
 'gi_code',
 'gi_frame',
 'gi_running',
 'next',        #The method that runs the function's body.
 'send',
 'throw']

gi_codeএবং gi_frameক্ষেত্র যেখানে হিমায়িত রাষ্ট্র সংরক্ষণ করা হয় হয়। তাদের সাথে অন্বেষণ করে dir(..), আমরা নিশ্চিত করতে পারি যে উপরে আমাদের মানসিক মডেলটি বিশ্বাসযোগ্য।


59

প্রতিটি উত্তর মতামত, yieldসিকোয়েন্স জেনারেটর তৈরি করতে ব্যবহৃত হয়। এটি গতিশীলভাবে কিছু সিকোয়েন্স তৈরির জন্য ব্যবহৃত হয়। উদাহরণস্বরূপ, একটি নেটওয়ার্কে লাইন দ্বারা একটি ফাইল লাইন পড়ার সময়, আপনি yieldনিম্নলিখিত ফাংশনটি ব্যবহার করতে পারেন :

def getNextLines():
   while con.isOpen():
       yield con.read()

আপনি নিম্নলিখিত হিসাবে এটি আপনার কোড ব্যবহার করতে পারেন:

for line in getNextLines():
    doSomeThing(line)

এক্সিকিউশন কন্ট্রোল ট্রান্সফার গোছা

এক্সিকিউশন কন্ট্রোলটি getNextLines () থেকে। এ স্থানান্তরিত হবে forফলন সম্পাদন করা হলে । সুতরাং, প্রতিবার getNextLines () আহ্বান করা হয়েছে, শেষ বার যেখানে থামানো হয়েছিল সেই জায়গা থেকে মৃত্যুদন্ড কার্যকর করা হবে।

এইভাবে সংক্ষেপে, নিম্নলিখিত কোড সহ একটি ফাংশন

def simpleYield():
    yield "first time"
    yield "second time"
    yield "third time"
    yield "Now some useful value {}".format(12)

for i in simpleYield():
    print i

মুদ্রণ করবে

"first time"
"second time"
"third time"
"Now some useful value 12"

59

এটি কী তা বোঝার জন্য একটি সহজ উদাহরণ: yield

def f123():
    for _ in range(4):
        yield 1
        yield 2


for i in f123():
    print (i)

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

1 2 1 2 1 2 1 2

5
আপনি কি আউটপুট সম্পর্কে নিশ্চিত? আপনি যদি সেই মুদ্রণ বিবৃতিটি ব্যবহার করে চালিত করেন তবে তা কেবল একটি লাইনেই মুদ্রিত হবে print(i, end=' ')? অন্যথায়, আমি বিশ্বাস করি যে ডিফল্ট আচরণটি প্রতিটি সংখ্যা একটি নতুন লাইনে রাখে
user9074332

@ ব্যবহারকারী 9074332, আপনি ঠিক বলেছেন তবে বোঝার সুবিধার্থে এটি একটি লাইনে লেখা হয়েছে
গ্যাভ্রিয়েল কোহেন

57

(আমার নীচের উত্তরটি পাইথন জেনারেটর ব্যবহারের দৃষ্টিভঙ্গি থেকে কেবল জেনারেটর প্রক্রিয়াটির অন্তর্নিহিত বাস্তবায়ন নয় , যার মধ্যে স্ট্যাক এবং গাদা হেরফেরের কয়েকটি কৌশল জড়িত))

পাইথন ফাংশনটির yieldপরিবর্তে যখন ব্যবহৃত হয় return, তখন সেই ফাংশনটি বিশেষ কিছুতে পরিণত হয় generator function। এই ফাংশনটি generatorটাইপের কোনও অবজেক্ট ফিরিয়ে দেবে । মূল yieldশব্দটি হ'ল একটি পতাকা যা পাইথন সংকলককে এই জাতীয় ফাংশনটি বিশেষভাবে চিকিত্সার জন্য অবহিত করে। কিছু ফাংশন এখান থেকে ফিরে আসার পরে সাধারণ ফাংশনগুলি সমাপ্ত হবে। কিন্তু সংকলকটির সাহায্যে জেনারেটরের ফাংশনটিকে পুনঃসূচনাযোগ্য হিসাবে ভাবা যেতে পারে । অর্থাত্, কার্যকর করা প্রসঙ্গে পুনরুদ্ধার করা হবে এবং শেষ সময় থেকে কার্যকর করা চলবে। যতক্ষণ না আপনি স্পষ্টভাবে রিটার্ন কল করবেন, যা একটি StopIterationব্যতিক্রম বাড়িয়ে তুলবে (যা পুনরাবৃত্ত প্রোটোকলেরও একটি অংশ) বা ফাংশনটির শেষে পৌঁছাবে। আমি রেফারেন্স অনেক পাওয়া generatorকিন্তু এই একথেকে functional programming perspectiveসবচেয়ে digestable হয়।

(এখন আমি পিছনে যুক্তি generatorএবং iteratorআমার নিজের বোঝাপড়ার উপর ভিত্তি করে কথা বলতে চাই I আমি আশা করি এটি আপনাকে পুনরুক্তি এবং জেনারেটরের প্রয়োজনীয় প্রেরণা উপলব্ধি করতে সহায়তা করতে পারে Such এই জাতীয় ধারণাটি অন্যান্য ভাষায় যেমন সি # তেও প্রদর্শিত হবে))

আমি যেমন বুঝতে পেরেছি, যখন আমরা একগুচ্ছ ডেটা প্রক্রিয়া করতে চাই, আমরা সাধারণত প্রথমে কোথাও কোথাও ডেটা সঞ্চয় করি এবং তারপরে একে একে প্রক্রিয়াজাত করি। তবে এই নিষ্পাপ দৃষ্টিভঙ্গি সমস্যাযুক্ত। যদি ডেটা ভলিউম বিশাল হয় তবে এগুলি পূর্বে পুরো হিসাবে সংরক্ষণ করা ব্যয়বহুল। সুতরাং dataসরাসরি নিজেকে স্টোরেজ করার পরিবর্তে , কেন metadataকোনওরকম অপ্রত্যক্ষভাবে অর্থ সঞ্চয় করে নাthe logic how the data is computed

এই জাতীয় মেটাডেটা মোড়ানোর জন্য 2 টি পন্থা রয়েছে।

  1. ওও পদ্ধতির, আমরা মেটাটাটা গুটিয়ে রাখি as a class। এটিই তথাকথিত iteratorযিনি পুনরায় প্রোটোকল প্রয়োগ করেন (যেমন __next__(), এবং __iter__()পদ্ধতিগুলি)। এটিও দেখা যায় সাধারণভাবে পুনরাবৃত্তিকারী ডিজাইন প্যাটার্ন
  2. ক্রিয়ামূলক পন্থা, আমরা মেটাডেটা মোড়ানো as a function। এটি তথাকথিত generator function। তবে হুডের নীচে, প্রত্যাবর্তিত generator objectস্থির IS-Aপুনরাবৃত্তি কারণ এটি পুনরুক্তি প্রোটোকলও প্রয়োগ করে।

যে কোনও উপায়ে, একটি পুনরুক্তি তৈরি করা হয়, অর্থাত্ এমন কোনও বস্তু যা আপনাকে চাইলে ডেটা দিতে পারে। ওও পদ্ধতিটি কিছুটা জটিল হতে পারে। যাইহোক, কোনটি ব্যবহার করবেন তা আপনার উপর নির্ভর করে।


54

সংক্ষেপে, yieldবিবৃতিটি আপনার ফাংশনটিকে এমন একটি কারখানায় রূপান্তর করে generatorযা একটি বিশেষ অবজেক্ট তৈরি করে যা আপনার মূল ফাংশনের দেহের চারপাশে আবৃত। যখন generatorপুনরাবৃত্তি করা হয়, এটি পরবর্তী yieldসময়ে পৌঁছা না হওয়া পর্যন্ত এটি আপনার ফাংশনটি সম্পাদন করে তারপরে কার্যকর করা স্থগিত করে এবং উত্তীর্ণ হওয়া মানটির মূল্যায়ন করে yield। মৃত্যুদন্ড কার্যকর করার পথটি ফাংশনটি না বের হওয়া পর্যন্ত এটি প্রতিটি পুনরাবৃত্তিতে এই প্রক্রিয়াটির পুনরাবৃত্তি করে। এই ক্ষেত্রে,

def simple_generator():
    yield 'one'
    yield 'two'
    yield 'three'

for i in simple_generator():
    print i

কেবল আউটপুট

one
two
three

শক্তিটি লুপের সাহায্যে জেনারেটর ব্যবহার করে আসে যা একটি অনুক্রম গণনা করে, জেনারেটর প্রতিটি সময় গণনার পরবর্তী ফলাফলকে 'ফলন' দিতে থামিয়ে লুপটি কার্যকর করে, এইভাবে এটি উড়ে একটি তালিকা গণনা করে, সুবিধাটি মেমরির হয়ে থাকে বিশেষত বড় গণনার জন্য সংরক্ষিত

বলুন যে আপনি একটি নিজস্ব rangeফাংশন তৈরি করতে চেয়েছিলেন যা সংখ্যার পুনরাবৃত্তিযোগ্য পরিসীমা তৈরি করে, আপনি এটি এর মতো করতে পারেন,

def myRangeNaive(i):
    n = 0
    range = []
    while n < i:
        range.append(n)
        n = n + 1
    return range

এবং এটি এর মতো ব্যবহার করুন;

for i in myRangeNaive(10):
    print i

তবে এটি অদক্ষ কারণ

  • আপনি একটি অ্যারে তৈরি করেন যা আপনি কেবল একবার ব্যবহার করেন (এটি মেমরির অপচয় করে)
  • এই কোডটি আসলে সেই অ্যারের উপরে দুইবার লুপ করে! :(

ভাগ্যক্রমে গাইডো এবং তার দল জেনারেটর বিকাশের জন্য যথেষ্ট উদার ছিল যাতে আমরা কেবল এটি করতে পারি;

def myRangeSmart(i):
    n = 0
    while n < i:
       yield n
       n = n + 1
    return

for i in myRangeSmart(10):
    print i

এখন প্রতিটি পুনরাবৃত্তির উপরে জেনারেটরের একটি ফাংশন ফাংশন next()সম্পাদন করে যতক্ষণ না এটি হয় 'ফলন' বিবৃতিতে পৌঁছে যায় যেখানে এটি বন্ধ হয়ে যায় এবং 'ফলন' দেয় বা ফাংশনটির শেষে পৌঁছায়। এই ক্ষেত্রে প্রথম কলটিতে, next()ফলন বিবরণী এবং ফলন 'এন' অবধি কার্যকর করা হবে, পরের কলটিতে এটি বর্ধন বিবরণী কার্যকর করবে, 'যখন' তে ফিরে যাবে, মূল্যায়ন করবে, এবং যদি সত্য হয়, এটি বন্ধ হয়ে যাবে এবং আবার 'এন' ফলন করা হবে, যতক্ষণ না শর্তটি মিথ্যা হয়ে যায় এবং জেনারেটরটি ফাংশনটির শেষের দিকে ঝাঁপ দেয় ততক্ষণ এটি চলতে থাকবে।


53

ফলন একটি বস্তু

একটি returnএকটি ফাংশন একটি একক মান ফিরে আসবে।

আপনি যদি কোনও ক্রিয়াকলাপটি একটি বিশাল মানকে ফেরত দিতে চান তবে ব্যবহার করুন yield

আরও গুরুত্বপূর্ণ, yieldএকটি বাধা হয়

CUDA ভাষার বাধার মতো, এটি সম্পূর্ণ না হওয়া পর্যন্ত এটি নিয়ন্ত্রণ স্থানান্তর করবে না।

এটি হিট না হওয়া পর্যন্ত এটি শুরু থেকে আপনার ফাংশনে কোডটি চালাবে yield। তারপরে, এটি লুপের প্রথম মানটি ফিরিয়ে দেবে।

তারপরে, প্রতিটি অন্যান্য কল আপনি একবারে ফাংশনে লিখেছেন সেই লুপটি চালাবে, ফেরতের কোনও মান না পাওয়া পর্যন্ত পরবর্তী মানটি ফিরে আসবে।


52

অনেকে এর returnপরিবর্তে ব্যবহার করেন yieldতবে কিছু ক্ষেত্রে yieldকাজ করা আরও দক্ষ এবং সহজ হতে পারে।

এখানে একটি উদাহরণ যা yieldনিখুঁতভাবে সেরা:

প্রত্যাবর্তন (কার্যক্রমে)

import random

def return_dates():
    dates = [] # With 'return' you need to create a list then return it
    for i in range(5):
        date = random.choice(["1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th", "10th"])
        dates.append(date)
    return dates

ফলন (কার্যক্রমে)

def yield_dates():
    for i in range(5):
        date = random.choice(["1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th", "10th"])
        yield date # 'yield' makes a generator automatically which works
                   # in a similar way. This is much more efficient.

কলিং ফাংশন

dates_list = return_dates()
print(dates_list)
for i in dates_list:
    print(i)

dates_generator = yield_dates()
print(dates_generator)
for i in dates_generator:
    print(i)

উভয় ফাংশন একই কাজ করে তবে yieldপাঁচটির পরিবর্তে তিনটি লাইন ব্যবহার করে এবং এটি নিয়ে চিন্তার জন্য কম ভেরিয়েবল রয়েছে।

এটি কোড থেকে ফলাফল:

আউটপুট

আপনি উভয় ফাংশন একই জিনিস করতে দেখতে পারেন। পার্থক্যটি কেবল return_dates()একটি তালিকা yield_dates()দেয় এবং একটি জেনারেটর দেয়।

বাস্তব জীবনের উদাহরণ হ'ল লাইনের দ্বারা ফাইল লাইন পড়ার মতো বা যদি আপনি কেবল কোনও জেনারেটর তৈরি করতে চান।


43

yieldএকটি ফাংশন জন্য রিটার্ন উপাদান মত। পার্থক্যটি হল, যে yieldউপাদানটি একটি ফাংশনটিকে জেনারেটরে পরিণত করে। কোনও জেনারেটর কোনও 'ফলন' না হওয়া অবধি কোনও ফাংশনের মতো আচরণ করে। জেনারেটরটি পরবর্তী বলা না হওয়া পর্যন্ত থামবে এবং ঠিক ঠিক একই বিন্দু থেকে এটি শুরু হয়েছিল। আপনি কল করে একের মধ্যে সমস্ত 'ফলিত' মানের একটি ক্রম পেতে পারেন list(generator())



36

yieldফিবোনাচি সিরিজের গণনা করার জন্য এখানে একটি সাধারণ ভিত্তিক পদ্ধতির বর্ণনা দেওয়া হয়েছে:

def fib(limit=50):
    a, b = 0, 1
    for i in range(limit):
       yield b
       a, b = b, a+b

আপনি এটি আপনার আরপিএলে প্রবেশ করুন এবং তারপরে চেষ্টা করুন এবং কল করুন, আপনি একটি রহস্যজনক ফলাফল পাবেন:

>>> fib()
<generator object fib at 0x7fa38394e3b8>

এটি কারণ yieldপাইথনের সংকেতযুক্ত উপস্থিতি যা আপনি একটি জেনারেটর তৈরি করতে চান , এটি এমন একটি বস্তু যা চাহিদা অনুসারে মান উত্পন্ন করে।

সুতরাং, আপনি এই মানগুলি কীভাবে তৈরি করবেন? এটি হয় অন্তর্নির্মিত ফাংশনটি ব্যবহার করে সরাসরি করা যেতে পারে next, বা পরোক্ষভাবে মানগুলি গ্রাস করে এমন একটি নির্মাণে এটি খাওয়ানোর মাধ্যমে।

ব্যবহার বিল্ট ইন next()ফাংশন, আপনি সরাসরি ডাকা .next/ __next__জেনারেটর অত্যাচার একটি মান উত্পাদন করতে:

>>> g = fib()
>>> next(g)
1
>>> next(g)
1
>>> next(g)
2
>>> next(g)
3
>>> next(g)
5

অপ্রত্যক্ষভাবে, যদি আপনি fibকোনও forলুপ, একটি listআরম্ভকারী, tupleআরম্ভকারী বা অন্য যে কোনও বস্তুর প্রত্যাশা করে যা মান উত্পন্ন / উত্পাদন করে তবে আপনি জেনারেটরটিকে "গ্রাস" করবেন যতক্ষণ না এর দ্বারা আরও কোনও মান উত্পাদিত না হয় (এবং এটি ফিরে আসে) :

results = []
for i in fib(30):       # consumes fib
    results.append(i) 
# can also be accomplished with
results = list(fib(30)) # consumes fib

একইভাবে, একটি tupleপ্রাথমিককরণ সহ:

>>> tuple(fib(5))       # consumes fib
(1, 1, 2, 3, 5)

কোনও জেনারেটর কোনও ক্রিয়া থেকে এই অর্থে পৃথক হয় যে এটি অলস। এটি স্থানীয় রাষ্ট্র বজায় রেখে এবং যখনই আপনার প্রয়োজন পুনরায় শুরু করার অনুমতি দিয়ে এটি সম্পাদন করে।

আপনি যখন প্রথম fibকল করে ডাকবেন:

f = fib()

পাইথন ফাংশনটি সংকলন করে, yieldকীওয়ার্ডটির মুখোমুখি হয় এবং কেবল কোনও জেনারেটর অবজেক্ট আপনাকে ফিরিয়ে দেয়। মনে হয় খুব সহায়ক নয়।

আপনি যখন অনুরোধ করেন এটি প্রত্যক্ষ বা অপ্রত্যক্ষভাবে এটি প্রথম মান উত্পন্ন করে, এটি এটি খুঁজে পাওয়া সমস্ত বিবৃতি কার্যকর করে, যতক্ষণ না এটির মুখোমুখি হয় yield, এটি তখন আপনার সরবরাহ করা মূল্য yieldএবং বিরতি দেয়। উদাহরণস্বরূপ যা এটি আরও ভালভাবে প্রমাণ করে, আসুন কিছু printকল ব্যবহার করুন ( print "text"পাইথন 2 তে প্রতিস্থাপন করুন):

def yielder(value):
    """ This is an infinite generator. Only use next on it """ 
    while 1:
        print("I'm going to generate the value for you")
        print("Then I'll pause for a while")
        yield value
        print("Let's go through it again.")

এখন, REPL এ প্রবেশ করুন:

>>> gen = yielder("Hello, yield!")

আপনার কাছে এখন একটি জেনারেটর অবজেক্ট রয়েছে এটির মান উত্পন্ন করার জন্য একটি আদেশের জন্য অপেক্ষা করছে। nextকী ছাপা হয়েছে তা ব্যবহার করুন এবং দেখুন:

>>> next(gen) # runs until it finds a yield
I'm going to generate the value for you
Then I'll pause for a while
'Hello, yield!'

উদ্ধৃত ফলাফলগুলি যা মুদ্রিত হয়েছে। উদ্ধৃত ফলাফলটি যা থেকে ফিরে আসে yieldnextএখনই আবার ফোন করুন :

>>> next(gen) # continues from yield and runs again
Let's go through it again.
I'm going to generate the value for you
Then I'll pause for a while
'Hello, yield!'

জেনারেটর মনে করে এটি এ বিরতি দিয়েছিল yield valueএবং সেখান থেকে আবার শুরু হয়েছিল। পরবর্তী বার্তাটি মুদ্রিত হয় এবং yieldবিবৃতিটি এটি থামিয়ে পুনরায় সঞ্চালনের জন্য অনুসন্ধান করে ( whileলুপের কারণে )।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.