python - ماذا تفعل كلمة "العائد"؟


ما هو استخدام الكلمة الرئيسية 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 = list(), [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 ؟ هل تم إرجاع قائمة؟ عنصر واحد؟ هل يطلق عليه مرة أخرى؟ متى ستتوقف المكالمات اللاحقة؟

1. الكود يأتي من يوشن شولز (جرشولز)، الذي جعل مكتبة بيثون كبيرة لمسافات متري. هذا هو الرابط إلى المصدر الكامل: وحدة مسباس .



Answers



لفهم ما yield ، يجب أن نفهم ما هي المولدات . وقبل المولدات تأتي إيتيرابلز .

Iterables

عند إنشاء قائمة، يمكنك قراءة عناصرها واحدا تلو الآخر. قراءة بنودها واحدا تلو الآخر يسمى التكرار:

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

mylist هو 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 ، يجب أن نفهم أنه عند استدعاء الدالة، التعليمات البرمجية التي قمت بكتابتها في الجسم وظيفة لا يعمل. وظيفة يعود فقط كائن مولد، وهذا هو صعبة بعض الشيء :-)

ثم، سيتم تشغيل التعليمات البرمجية الخاصة بك في كل مرة لاستخدام مولد.

الآن الجزء الصعب:

في المرة الأولى للمكالمات كائن مولد إنشاؤها من الدالة الخاصة بك، فإنه سيتم تشغيل التعليمات البرمجية في الدالة الخاصة بك من البداية حتى يضرب 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 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 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 candidates 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() هي طريقة كائن قائمة تتوقع تكرارا وتضيف قيمها إلى القائمة.

عادة نقوم بتمرير قائمة لها:

>>> 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 # 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
...

يمكن أن يكون مفيدا لأشياء مختلفة مثل التحكم في الوصول إلى الموارد.

إترتولز، أفضل صديق

وحدة إيترتولز يحتوي على وظائف خاصة لمعالجة الملفات القابلة للتكرار. ترغب في أي وقت مضى لتكرار مولد؟ سلسلة مولدات اثنين؟ قيم المجموعة في قائمة متداخلة مع بطانة واحدة؟ Map / Zip دون إنشاء قائمة أخرى؟

ثم مجرد import itertools .

مثال؟ دعونا نرى أوامر ممكنة من وصوله لسباق الخيل 4:

>>> 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)]

فهم الآليات الداخلية للتكرار

التكرار هو عملية تنطوي على __iter__() (تنفيذ الطريقة __iter__() ) والمكرر (تنفيذ الأسلوب __next__() ). المتغيرات هي أي كائنات يمكنك الحصول على التكرار من. التكرارات هي الكائنات التي تمكنك من تكرار على أتم.

مزيد من المعلومات حول هذه المقالة حول كيفية عمل حلقة .




اختصار غروكينغ yield

عندما ترى وظيفة مع بيانات yield ، وتطبيق هذه خدعة سهلة لفهم ما سيحدث:

  1. إدراج result = [] سطر result = [] في بداية الدالة.
  2. استبدال كل result.append(expr) yield expr مع result.append(expr) .
  3. إدراج return result خط في الجزء السفلي من وظيفة.
  4. ياي - لا مزيد من البيانات yield ! قراءة ورمز رمز.
  5. قارن الدالة إلى التعريف الأصلي.

هذه الخدعة قد تعطيك فكرة عن المنطق وراء وظيفة، ولكن ما يحدث فعلا مع yield يختلف اختلافا كبيرا أن ما يحدث في النهج القائم على القائمة. في كثير من الحالات نهج العائد سيكون الكثير من الذاكرة أكثر كفاءة وأسرع جدا. في حالات أخرى هذه الحيلة سوف تحصل على عالقة في حلقة لانهائية، على الرغم من أن وظيفة الأصلي يعمل على ما يرام. تابع القراءة لمعرفة المزيد...

لا تخلط بين إيترابلز، إيراتورس والمولدات الخاصة بك

أولا، بروتوكول التكرار - عند الكتابة

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

يقوم بيثون بتنفيذ الخطوتين التاليتين:

  1. الحصول على التكرار mylist :

    استدعاء iter(mylist) -> هذا بإرجاع كائن مع أسلوب ( next() (أو __next__() في بيثون 3).

    [هذه هي الخطوة معظم الناس ينسى أن أقول لكم عن]

  2. يستخدم التكرار إلى حلقة فوق العناصر:

    الحفاظ على استدعاء الأسلوب next() على المكرر عاد من الخطوة 1. يتم تعيين قيمة العودة من next() إلى x ويتم تنفيذ الجسم حلقة. إذا تم رفع StopIteration استثناء من داخل next() ، فهذا يعني أنه لا توجد قيم أكثر في التكرار ويتم الخروج الحلقة.

والحقيقة هي أن بايثون تقوم بتنفيذ الخطوتين المذكورتين أعلاه في أي وقت تريد أن تدور فيه محتويات الكائن - لذلك يمكن أن تكون حلقة، ولكن يمكن أيضا أن تكون كود مثل otherlist.extend(mylist) (حيث otherlist هي قائمة بايثون) .

القائمة هنا هي mylist لأنه ينفذ بروتوكول التكرار. في فئة المعرفة من قبل المستخدم، يمكنك تنفيذ الأسلوب __iter__() لجعل مثيلات الفصل الخاص بك قابلة للتكرار. يجب أن تعيد هذه الطريقة أداة تكرار . المكرر هو كائن مع الطريقة next() . يمكن تنفيذ كل من __iter__() next() على نفس الفئة، ويكون __iter__() عودة self . هذا سوف تعمل لحالات بسيطة، ولكن ليس عندما تريد اثنين متكررات حلقات فوق نفس الكائن في نفس الوقت.

لذلك هذا هو بروتوكول التكرار، العديد من الكائنات تنفيذ هذا البروتوكول:

  1. المدمج في القوائم، والقواميس، والقمم، والمجموعات، والملفات.
  2. فئات تعريف المستخدم التي تنفذ __iter__() .
  3. مولدات كهرباء.

لاحظ أن for حلقة لا يعرف أي نوع من الكائن انها تتعامل مع - انها مجرد اتباع بروتوكول التكرار، وسعيدة للحصول على البند بعد البند كما يدعو next() . قوائم مدمجة تعود عناصرها واحدا تلو الآخر، والقواميس ترجع المفاتيح واحدا تلو الآخر، والملفات العودة خطوط واحدا تلو الآخر، الخ والمولدات تعود ... حسنا هذا حيث يأتي yield في:

def f123():
    yield 1
    yield 2
    yield 3

for item in f123():
    print item

بدلا من بيانات yield ، إذا كان لديك ثلاثة بيانات return في f123() فقط سيتم تنفيذ الأول، وسوف تخرج الوظيفة. ولكن f123() ليست وظيفة عادية. عندما يتم استدعاء f123() ، فإنه لا يعود أي من القيم في عبارات العائد! تقوم بإرجاع كائن مولد. أيضا، وظيفة لا حقا الخروج - يذهب إلى حالة معلقة. عندما تحاول الحلقة من أجل حلقة فوق كائن المولد، تستأنف الدالة من حالتها المعلقة في السطر التالي تماما بعد yield الذي سبق أن عادت منه، وتنفذ السطر التالي من التعليمات البرمجية، وفي هذه الحالة بيان yield ، وتعود إلى البند التالي. يحدث هذا حتى خروج وظيفة، عند هذه النقطة يثير المولد StopIteration ، ومخارج حلقة.

وبالتالي فإن كائن المولد هو نوع من مثل محول - في نهاية واحدة أنها تعرض بروتوكول التكرار، من خلال تعريض __iter__() next() أساليب للحفاظ على حلقة سعيدة. على الطرف الآخر ومع ذلك، فإنه يعمل على وظيفة ما يكفي للحصول على القيمة التالية للخروج منه، ويضعه مرة أخرى في وضع مع وقف التنفيذ.

لماذا استخدام المولدات؟

عادة يمكنك كتابة التعليمات البرمجية التي لا تستخدم مولدات ولكن تنفذ نفس المنطق. خيار واحد هو استخدام القائمة المؤقتة "خدعة" ذكرت من قبل. وهذا لن يعمل في جميع الحالات، على سبيل المثال إذا كان لديك حلقات لانهائية، أو أنها قد تجعل الاستخدام غير الفعال للذاكرة عندما يكون لديك قائمة طويلة حقا. النهج الآخر هو تنفيذ فئة جديدة قابلة للتكرار SomethingIter التي تبقي الدولة في الأعضاء على سبيل المثال وتنفذ الخطوة المنطقية التالية في انها next() (أو __next__() في بيثون 3) الأسلوب. اعتمادا على المنطق، رمز داخل الطريقة 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

لمزيد من المعلومات عن ما يحدث خلف الكواليس، يمكن إعادة كتابة الحلقة إلى:

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

هل هذا أكثر منطقية أو مجرد الخلط بينك أكثر؟ :)

إديت: أود أن أشير إلى أن هذا هو تبسيط مفرط لأغراض التوضيح. :)

إديت 2: نسيت لرمي استثناء ستوبتيراشيون




يتم تقليل الكلمة الرئيسية yield إلى حالتين بسيطتين:

  1. إذا قام المترجم بالكشف yield الكلمة الرئيسية 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 . chain.from_iterable بدلا من chain عندما يكون ذلك chain.from_iterable ). على سبيل المثال، قد تستخدم حتى المولدات لتنفيذ قوائم كسول بلا حدود طويلة مثل itertools.count() . هل يمكن تنفيذ def enumerate(iterable): zip(count(), iterable) الخاص بك 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() وأجزاء أخرى من بروتوكول التكرار) لتنفيذ الأشياء الهوى، وعادة على حساب إمكانية القراءة، لذا حاول تجنب القيام بذلك ...

تفصيلات

عادة، فإن معظم الناس لا يهتمون التمييز التالية وربما ترغب في التوقف عن القراءة هنا.

في بيثون الكلام، وهو متكرر هو أي كائن الذي "يفهم مفهوم حلقة ل" مثل قائمة [1,2,3] ، ومكرر هو مثال محدد من طلب للحصول على حلقة مثل [1,2,3].__iter__() . مولد هو بالضبط نفس أي متكرر، باستثناء الطريقة التي كتبت (مع بناء جملة وظيفة).

عند طلب التكرار من قائمة، فإنه يقوم بإنشاء جديد التكرار. ومع ذلك، عند طلب التكرار من التكرار (الذي نادرا ما تفعله)، فإنه يعطي فقط نسخة من نفسه.

وهكذا، في حالة غير المحتمل أنك تفشل في فعل شيء من هذا القبيل ...

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

... ثم تذكر أن مولد هو التكرار . وهذا هو، هو لمرة واحدة الاستخدام. إذا كنت ترغب في إعادة استخدامه، يجب عليك استدعاء myRange(...) مرة أخرى. إذا كنت بحاجة إلى استخدام النتيجة مرتين، تحويل النتيجة إلى قائمة وتخزينها في متغير x = list(myRange(5)) . أولئك الذين يحتاجون على الاطلاق لاستنساخ مولد (على سبيل المثال، الذين يقومون مخطئ هاكيش ميتابروغرامينغ) يمكن استخدام itertools.tee إذا لزم الأمر على الاطلاق، منذ تم تأجيل مقترح التكرار بيثون بيب معايير الاقتراح.




yield هو تماما مثل return - فإنه يعود مهما كنت أقول ذلك. والفرق الوحيد هو أنه في المرة القادمة التي استدعاء الدالة، يبدأ التنفيذ من المكالمة الأخيرة إلى بيان yield .

في حالة التعليمات البرمجية الخاصة بك، وظيفة get_child_candidates يتصرف مثل التكرار بحيث عند توسيع قائمتك، فإنه يضيف عنصر واحد في وقت واحد إلى القائمة الجديدة.

list.extend يدعو list.extend حتى انها استنفدت. في حالة عينة التعليمات البرمجية التي نشرت، سيكون أكثر وضوحا لمجرد إرجاع توبل وإلحاق ذلك إلى القائمة.




ماذا تفعل الكلمة الرئيسية للجلب في بيثون؟

الإجابة الملخص / الملخص

  • وظيفة مع yield ، عندما تسمى، بإرجاع مولد .
  • المولدات هي التكرار لأنها تنفذ بروتوكول التكرار ، حتى تتمكن من تكرار عليها.
  • ويمكن أيضا مولد إرسال المعلومات ، مما يجعل من المفاهيمية كوروتين .
  • في بيثون 3، يمكنك تفويض من مولد واحد إلى آخر في كلا الاتجاهين مع yield from .
  • (ينقد الملحق بضعة إجابات، بما في ذلك أعلى واحد، ويناقش استخدام return في مولد.)

مولدات كهرباء:

yield هو فقط القانونية داخل تعريف وظيفة، وإدراج yield في تعريف وظيفة يجعلها ترجع مولد.

وتأتي فكرة المولدات من لغات أخرى (انظر الحاشية 1) مع تطبيقات مختلفة. في مولدات بايثون، يتم تجميد تنفيذ التعليمات البرمجية عند نقطة العائد. عندما يسمى المولد (تناقش الطرق أدناه) يستأنف التنفيذ ثم يتجمد عند العائد التالي.

yield يوفر طريقة سهلة لتنفيذ بروتوكول التكرار ، المعرفة __iter__ : __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

مولد بسيط أعلاه هو أيضا ما يعادل أدناه - اعتبارا من بيثون 3.3 (وغير متوفر في بيثون 2)، يمكنك استخدام 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

وفد تعاوني إلى شبه الكوروتين مع yield from

الآن، أذكر أن yield from متاح في بيثون 3. وهذا يسمح لنا لتفويض كوروتينس إلى سوبكوروتين:

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 في بيب 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 الكثير. أنا متأكد من أنني يمكن أن أضيف أمثلة أكثر شمولا لهذا. إذا كنت تريد المزيد أو لديك بعض النقد البناء، واسمحوا لي أن أعرف من خلال التعليق أدناه.

الملحق:

نقد أعلى / الإجابة المقبولة **

  • يتم الخلط بين ما يجعلها متكررة ، فقط باستخدام قائمة كمثال. انظر __iter__ أعلاه، ولكن باختصار: يمكن للتكرار أن يحتوي على أسلوب __iter__ يعيد التكرار . يوفر .next (بيثون 2 أو .__next__ (بيثون 3) الأسلوب، الذي يسمى ضمنا من قبل الحلقات حتى يثير StopIteration ، وبمجرد أن يفعل ذلك، وسوف تستمر في القيام بذلك.
  • ثم يستخدم تعبير مولد لوصف ما هو مولد. منذ مولد هو مجرد وسيلة مريحة لإنشاء التكرار ، فإنه يخلط فقط المسألة، ونحن ما زلنا لم تصل بعد إلى جزء yield .
  • في السيطرة على استنفاد مولد يستدعي الأسلوب .next ، وعندما بدلا من ذلك يجب عليه استخدام وظيفة بنيت، next . وسوف تكون طبقة مناسبة من غير المباشرة، لأن قانونه لا يعمل في بيثون 3.
  • Itertools؟ ولم يكن ذلك ذا صلة بما yield عنه yield على الإطلاق.
  • لا مناقشة للأساليب التي yield توفر جنبا إلى جنب مع yield from الوظيفي الجديد yield from في بيثون 3. الجواب العلوي / المقبول هو إجابة غير مكتملة جدا.

نقد الجواب يشير إلى 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.7
  • تحذير Py3k في 2.7.x
  • سينتاكسيرور في 3.8

هتاف، نيك.

- نيك كوجلان | نكوغلان في gmail.com | بريسبان، أستراليا

وعلاوة على ذلك، هناك مسألة معلقة (10544) والتي يبدو أن لافتا في اتجاه هذا أبدا أن تكون فكرة جيدة (PyPy، وتنفيذ بيثون مكتوبة في بيثون، ورفع بالفعل تحذيرات البنية).

خلاصة القول، حتى مطوري سي بايثون يقول لنا خلاف ذلك: لا تضع yieldفي التعبير المولد أو الفهم.

و returnجاء في مولد

في بايثون 2 :

في وظيفة مولد، و returnهذا غير مسموح به بيان لتشمل expression_list. وفي هذا السياق، عارية returnيدل على أن يتم المولد وسيكون سببا StopIterationفي أن تثار.

و expression_listهو في الأساس أي عدد من العبارات مفصولة بفواصل - أساسا، في بايثون 2، يمكنك إيقاف مولد مع return، ولكن لا يمكنك إرجاع قيمة.

في بيثون 3 :

في وظيفة مولد، و returnيشير بيان أن يتم المولد وسيكون سببا StopIterationفي أن تثار. يتم استخدام القيمة التي تم إرجاعها (إن وجدت) كحجة لبناء StopIterationويصبح StopIteration.valueالسمة.

الحواشي

  1. تم الرجوع إليها لغات CLU، Sather، وأيقونة في اقتراح إدخال مفهوم مولدات لبيثون. الفكرة العامة هي أن وظيفة يمكن الحفاظ على الحالة الداخلية وتسفر عن نقاط البيانات متوسطة على الطلب من قبل المستخدم. وعد أن يكون متفوقة في الأداء لمناهج أخرى، بما في ذلك بيثون خيوط ، التي لا تتوفر حتى على بعض الأنظمة.

  2. وهذا يعني، على سبيل المثال، أن xrangeالكائنات ( rangeفي بيثون 3) ليست Iteratorالصورة، على الرغم من أنها iterable، لأنها يمكن إعادة استخدامها. مثل القوائم، من __iter__أساليب تعود الأشياء مكرر.

  3. yieldوقدم في الأصل كبيان، وهذا يعني أنه يمكن أن تظهر فقط في بداية سطر في كتلة التعليمات البرمجية. الآن yieldيخلق التعبير الغلة. https://docs.python.org/2/reference/simple_stmts.html#grammar-token-yield_stmt كان هذا التغيير المقترح للسماح للمستخدم إرسال البيانات إلى مولد كما يمكن للمرء أن الحصول عليها. لإرسال البيانات، يجب على المرء أن يكون قادرا على تعيين إلى شيء، ولهذا، بيانا فقط لن تعمل.




هناك شيء واحد إضافي أن أذكر: وظيفة يمكن أن ينتج لا يملك في الواقع لإنهاء. لقد قانون مكتوب مثل هذا:

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);

حقا يساعد على تبسيط بعض المشاكل، ويجعل بعض الأمور أسهل للعمل مع.




بالنسبة لأولئك الذين يفضلون سبيل المثال العمل الحد الأدنى، والتأمل في هذا تفاعلية بيثون الدورة:

>>> 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



العائد يعطيك المولد.

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

كما ترون، في حالة أول فو يحمل قائمة كاملة في الذاكرة في آن واحد. انها ليست صفقة كبيرة للحصول على قائمة مع 5 عناصر، ولكن ماذا إذا كنت ترغب في قائمة 5000000؟ ليس هذا فقط هو أكل ذاكرة ضخمة، يكلف أيضا الكثير من الوقت لبناء في الوقت الذي يتم استدعاء الدالة. في الحالة الثانية، شريط فقط يمنحك المولد. مولد هو iterable - مما يعني أنه يمكنك استخدامها في لحلقة، الخ، ولكن يمكن الوصول إلى كل قيمة مرة واحدة فقط. كما لا يتم تخزين كافة القيم في الذاكرة في نفس الوقت. الكائن مولد "يتذكر" حيث كان في حلقات المرة الأخيرة التي وصفته - وبهذه الطريقة، إذا كنت تستخدم iterable إلى (مثلا) عد إلى 50 مليار دولار، لم يكن لديك لحساب إلى 50 مليار جميع في وقت واحد وتخزين 50 مليار الأرقام إلى الاعتماد كليا. مرة أخرى، وهذا هو مثال مفتعلة جدا،وربما كنت ستستخدم itertools إذا كنت تريد حقا أن العد إلى 50 مليار. :)

هذه هي حالة الاستخدام الأكثر بسيطة من المولدات الكهربائية. كما قلت، فإنه يمكن استخدامها لكتابة التباديل فعالة، وذلك باستخدام العائد لدفع الامور من خلال مكدس الاستدعاءات بدلا من استخدام نوع من متغير المكدس. ويمكن أيضا أن تستخدم مولدات لاجتياز الشجرة المتخصصة، وجميع أنواع الأشياء الأخرى.




هناك نوع واحد من الجواب الذي لا أشعر أعطيت حتى الآن، من بين العديد من الإجابات العظيمة التي تصف كيفية استخدام المولدات الكهربائية. هنا هي نظرية الجواب PL:

في yieldبيان في بيثون بإرجاع المولد. مولد في بيثون هو دالة تقوم بإرجاع استمرارا (وعلى وجه التحديد نوع من coroutine، ولكن استمرارا تمثل آلية عامة لفهم ما يجري).

استمرارا في نظرية لغات البرمجة هي نوع أكثر جذرية بكثير من الحساب، ولكن لا يتم استخدامها في كثير من الأحيان لأنها صعبة للغاية لسبب حول وأيضا من الصعب جدا تنفيذها. لكن فكرة ما هو استمرار، واضح وصريح: هو حالة من الحساب التي لم تنته بعد. في هذه الحالة يتم حفظ القيم الحالية من المتغيرات والعمليات التي لم يتم تنفيذها، وهلم جرا. ثم في مرحلة ما بعد في البرنامج استمرار يمكن الاستناد إليها، مثل أن المتغيرات البرنامج يتم إعادة تعيين لتلك الدولة ويتم تنفيذ العمليات التي تم حفظها خارج.

استمرارا في هذا النموذج أكثر عمومية، يمكن تنفيذها بطريقتين. في call/ccالطريق، يتم حفظ كومة البرنامج حرفيا وبعد ذلك عندما يتم استدعاء استمرار، يتم استعادة المكدس.

في استمرار مرور النمط (CPS)، استمرارا ليست سوى الوظائف الطبيعية (فقط في اللغات حيث وظائف من الدرجة الأولى) والذي يدير مبرمج صراحة ويمر في جميع أنحاء لالوظائف الفرعية. في هذا النمط، يتم تمثيل الدولة البرنامج من قبل الإغلاق (والمتغيرات التي يحدث ليكون المشفرة فيها) بدلا من المتغيرات الموجودة في مكان ما على المكدس. وظائف التي تدير التحكم في التدفق يقبل استمرار كوسائط (في بعض الاختلافات من CPS، وظائف قد يقبل استمرارا متعددة) والتلاعب التحكم في التدفق من خلال التذرع لهم ببساطة عن طريق دعوتهم وتعود بعد ذلك. وهناك مثال بسيط جدا من استمرار مرور الاسلوب هو كما يلي:

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 )

في هذا (التبسيط جدا) مثلا، مبرمج يحفظ عملية كتابة هذا الملف بالفعل إلى استمرار (والتي يمكن المحتمل أن تكون عملية معقدة للغاية مع الكثير من التفاصيل لكتابة)، ثم يمر أن استمرار (أي بمثابة المستويين الأول إغلاق الفئة) إلى مشغل آخر والتي لا إلى مزيد من المعالجة، ومن ثم تطلق عليه إذا لزم الأمر. (I استخدام هذا النمط تصميم الكثير في مجال البرمجة واجهة المستخدم الرسومية الفعلية، إما لأنه يوفر لي الأسطر من التعليمات البرمجية أو، وهو الأهم، لإدارة التحكم في التدفق بعد تشغيل الأحداث GUI)

وسوف تبقى من هذا المنصب، ودون فقدان عمومية، وضع تصور استمرارا كما CPS، لأنها من الجحيم أسهل كثيرا على فهم وقراءة.


الآن دعونا نتحدث عن المولدات في بيثون. المولدات هي سلالة معينة من استمرار. في حين استمرارا قادرة بشكل عام لانقاذ الدولة من حساب (أي مكدس الاستدعاءات البرنامج)، مولدات قادرة على حفظ حالة التكرار أكثر من فقط مكرر . وعلى الرغم من هذا التعريف مضلل قليلا لحالات الاستخدام معينة من المولدات الكهربائية. على سبيل المثال:

def f():
  while True:
    yield 4

ومن الواضح أن هذا iterable معقول الذي يعرف جيدا السلوك - في كل مرة يتكرر مولد أكثر من ذلك، فإنها ترجع 4 (ويفعل ذلك إلى الأبد). ولكنها ليست على الأرجح نوع من تنميط iterable الذي يتبادر إلى الذهن عند التفكير في المكررات (أي for x in collection: do_something(x)). هذا المثال يوضح قوة المولدات: إذا كان أي شيء مكرر، يمكن للمولد حفظ حالة التكرار لها.

أن أكرر: الإستمرار يمكن أن ينقذ الدولة من كومة من البرنامج ويمكن مولدات حفظ حالة التكرار. وهذا يعني أن استمرارا هي أكثر بكثير قوية من المولدات، ولكن أيضا أن المولدات هي كثيرا، أسهل كثيرا. أنها أسهل للمصمم لغة لتنفيذ، وأنها أسهل للمبرمج لاستخدام (إذا كان لديك بعض الوقت لحرق، في محاولة لقراءة وفهم هذه الصفحة حول استمرارا والدعوة / سم مكعب ).

ولكن هل يمكن أن تنفذ بسهولة (وتصور) مولدا كهربائيا كما بسيطة، حالة معينة من استمرار مرور نمط:

كلما 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الكلمة.




انها تعود مولد. أنا لست على دراية خاصة مع بيثون، ولكن أعتقد أنه من نفس النوع من شيء اسمه كتل C # الصورة مكرر إذا كنت على دراية هؤلاء.

هناك على المادة IBM وهو ما يفسر ذلك جيدا إلى حد معقول (لبيثون) بقدر ما أستطيع أن أرى.

الفكرة الأساسية هي أن المترجم / مترجم / كل ما يفعل بعض الخداع بحيث بقدر ما يتعلق الأمر المتصل، وأنها يمكن أن تبقي يدعو المقبل () وأنها سوف تبقي القيم يعود - كما لو كان مؤقتا طريقة مولد . الآن من الواضح أنك لا يمكن حقا "وقفة" وسيلة، حتى يبني مترجم آلة الدولة عليك أن تتذكر أين أنت حاليا، وما هي المتغيرات المحلية وغيرها تبدو. هذا هو أسهل بكثير من الكتابة مكرر نفسك.




TL، DR

عندما تجد نفسك بناء listمن الصفر ...

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

... yieldالقطع بدلا من ذلك.

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

كان هذا لقائي الاول آها لحظة مع العائد.

yield هو وسيلة السكرية القول

بناء سلسلة من الاشياء

نفس السلوك:

>>> for square in squares_list(4):
...     print(square)
...
0
1
4
9
>>> for square in squares_the_yield_way(4):
...     print(square)
...
0
1
4
9

سلوك مختلفة:

العائد تستخدم مرة واحدة : يمكنك تكرار إلا من خلال مرة واحدة. من الناحية المفاهيمية وظيفة العائد بإرجاع حاوية أمر من الأمور. لكنه كشف عن أن نسميه أي وظيفة بعائد فيه على وظيفة مولد . وعلى المدى لما يعود ذلك هو مكرر .

العائد كسول ، فإنه يؤجل حساب حتى كنت في حاجة إليها. وظيفة بعائد فيه لا يتم تنفيذ في الواقع على الإطلاق عند تسميتها. الكائن مكرر فإنها ترجع يستخدم السحر للحفاظ على السياق الداخلي الدالة. في كل مرة تتصل next()على مكرر (كما يحدث في حلقة ل)، بوصة تنفيذ قدما للعائد المقبل. (أو return، الأمر الذي يثير StopIterationوتنتهي هذه السلسلة.)

العائد تنوعا . ويمكن أن تفعل حلقات لانهائية:

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

إذا كنت بحاجة إلى استخدام متعددة والسلسلة ليست العملاقة، فقط تمرير مكرر لlist()

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

خيار رائع للكلمة yieldلأنه على حد سواء معاني الفعل تطبيق:

العائد - إنتاج أو تقديم (كما في الزراعة)

... توفير البيانات التالية في السلسلة.

العائد - تفسح المجال أو أن يتخلى عن (كما في السلطة السياسية)

... التخلي عن تنفيذ وحدة المعالجة المركزية حتى السلف مكرر.




مثال بلغة واضحة. وسوف توفر المراسلات بين مفاهيم الإنسان رفيع المستوى لمفاهيم الثعبان على مستوى منخفض.

أريد أن تعمل على سلسلة من الأرقام، لكنني لا أريد أن يكلف نفسه عناء نفسي مع إنشاء هذا التسلسل، أريد فقط التركيز على العملية أريد القيام به. لذا، أفعل ما يلي:

  • أدعو لكم واقول لكم أنني أريد سلسلة من الأرقام التي يتم إنتاجها بطريقة معينة، وأنا أعلمك ما هي الخوارزمية.
    هذه الخطوة يتوافق مع defining وظيفة مولد، أي وظيفة تحتوي على yield.
  • في وقت لاحق، وأنا أقول لكم، "حسنا، والاستعداد ليقول لي الرقم المسلسل".
    هذه الخطوة يتوافق مع استدعاء الدالة مولد والتي ترجع كائن المولد. لاحظ أنك لا تقول لي أي أرقام حتى الآن، كنت مجرد انتزاع ورقة وقلم رصاص.
  • أنا أسألك، "يقول لي الرقم التالي"، وكنت تقول لي الرقم الأول. بعد ذلك، عليك الانتظار بالنسبة لي أن أسألك عن الرقم التالي. انه عملك لتذكر المكان الذي كنت فيه، ما الأرقام التي سبق أن قلنا، ما هو العدد القادم. لا يهمني حول التفاصيل.
    هذه الخطوة يتوافق مع الدعوة .next()على الكائن المولد.
  • ... تكرار الخطوة السابقة، حتى ...
  • في نهاية المطاف، قد وصل إلى نهايته. لم يكن ليقول لي رقم، أنت فقط الصراخ، "عقد الخيول الخاصة بك! انتهيت! لا مزيد من الأرقام!"
    هذه الخطوة يتوافق مع الكائن مولد تنتهي وظيفتها، ورفع StopIterationاستثناء وظيفة مولد لا تحتاج إلى رفع استثناء، انها رفعت تلقائيا عندما تنتهي وظيفة أو تصدر ل return.

هذا ما مولد يفعل (وظيفة التي تحتوي على yield)؛ ويبدأ تنفيذ، مؤقتا كلما كان يفعل yield، وعندما سألت عن .next()قيمة أنها لا تزال من النقطة التي كان الماضي. تناسبها تماما حسب التصميم مع بروتوكول مكرر من الثعبان، الذي يصف كيفية بالتسلسل طلب للقيم.

المستخدم الأكثر شهرة في بروتوكول مكرر هو forأمر في بيثون. لذا، كلما كنت تفعل:

for item in sequence:

لا يهم إذا sequenceهي قائمة، سلسلة، القاموس أو مولد كائن مثل المذكورة أعلاه. والنتيجة هي نفسها: هل قرأت البنود خارج تسلسل واحدا تلو الآخر.

لاحظ أن defining وظيفة التي تحتوي على yieldالكلمة ليست الطريقة الوحيدة لخلق مولد. انها مجرد أسهل طريقة لإنشاء واحد.

للحصول على معلومات أكثر دقة، قرأت عن أنواع مكرر ، و بيان العائد و المولدات في وثائق بيثون.




في حين أن الكثير من الأجوبة تظهر لماذا كنت تستخدم yieldلإنشاء مولد، هناك المزيد من الاستخدامات ل yield. فمن السهل جدا لجعل coroutine، والتي تمكن مرور المعلومات بين كتلتين من التعليمات البرمجية. لن أكرر أي من الأمثلة الجميلة التي أعطيت بالفعل حول استخدام 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()



هناك آخر yieldاستخدام ومعنى (منذ الثعبان 3.3):

yield from <expr>

http://www.python.org/dev/peps/pep-0380/

ويقترح بناء الجملة من أجل مولد تفويض جزء من عملياتها لمولد آخر. هذا يسمح المقطع من التعليمات البرمجية التي تحتوي على 'العائد "إلى أن يؤخذ بها ووضعها في مولد آخر. بالإضافة إلى ذلك، يسمح للsubgenerator للعودة مع القيمة، ويتم إجراء قيمة متاحة للمولد التفويض.

كما يفتح بناء الجملة الجديد بعض فرص التحسين عندما القيم مولد واحد إعادة غلة التي تنتجها آخر.

علاوة على ذلك هذا سوف أعرض (منذ الثعبان 3.5):

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

لتجنب coroutines الخلط بينه وبين المولد العادي (اليوم yieldيستخدم في كليهما).




كنت ذاهبا إلى الرد على "صفحة 19 نقرأ بيزلي" بيثون: مرجع أساسي "للحصول على وصف سريع للمولدات"، لكن آخرين كثيرين قد نشر أوصاف جيدة بالفعل.

أيضا، لاحظ أن yieldيمكن استخدامها في coroutines مثل ثنائي استخدامها في وظائف المولد. على الرغم من أنه ليس استخدام نفس كما مقتطف الشفرة، (yield)ويمكن استخدام تعبير في وظيفة. عندما يرسل المتصل قيمة إلى طريقة استخدام send()الأسلوب، ثم coroutine سيتم تنفيذ حتى في اليوم التالي (yield)واجهتها بيان.

مولدات وcoroutines هي وسيلة باردة لإنشاء تطبيقات نوع تدفق البيانات. وأعتقد أنه سيكون من المفيد معرفة عن استخدام آخر لل yieldبيان في وظائف.




وفيما يلي بعض الأمثلة بيثون من كيفية تنفيذ في الواقع كما لو مولدات بيثون لم تقدم نحوي السكر لهما:

كمولد بيثون:

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)

عن طريق إغلاق الكائن بدلا من المولدات (لأن ClosuresAndObjectsAreEquivalent )

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)



من وجهة نظر البرمجة، وتنفيذ المكررات كما thunks

http://en.wikipedia.org/wiki/Thunk_(functional_programming)

لتنفيذ المكررات / مولدات / حمامات موضوع التنفيذ المتزامن / الخ كما thunks (وتسمى أيضا وظائف مجهول)، واحد يستخدم الرسائل المرسلة إلى كائن الإغلاق، التي لديها المرسل، والمرسل الإجابات إلى "الرسائل".

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
-> 



اليك مثال بسيط:

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يشغل منصب تدفق البرنامج وبدء حلقة القادمة من موقف "العائد". يبدو أنه ينتظر في هذا الموقف، وقبل ذلك، إرجاع قيمة خارج، وتستمر في المرة القادمة للعمل.

يبدو لي قدرة مثيرة للاهتمام وجميلة: D




هنا هو صورة ذهنية لما 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(..)، يمكننا أن نؤكد أن النموذج العقلي اعلاه هو موثوق.




كما تشير كل إجابة، yieldوتستخدم لإنشاء مولد تسلسل. انها تستخدم لتوليد بعض تسلسل بشكل حيوي. على سبيل المثال.أثناء قراءة الخط الملف عن طريق خط على الشبكة، يمكنك استخدام yieldوظيفة على النحو التالي:

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

يمكنك استخدامه في التعليمات البرمجية كما يلي:

for line in getNextLines():
    doSomeThing(line)

تنفيذ نقل تحكم مسكتك

سيتم نقل السيطرة التنفيذ من getNextLines () إلى لحلقة عند تنفيذ الغلة. وهكذا، في كل مرة 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"

آمل أن يكون هذا يساعدك.




yieldمثل عنصر مقابل وظيفة. والفرق هو أن yieldالعنصر يتحول وظيفة في المولد. مولد يتصرف تماما مثل وظيفة حتى شيء هو 'حققت'. توقف المولد حتى يتم استدعاؤه المقبل، ويستمر من نفس النقطة بالضبط حيث بدأت. يمكنك الحصول على سلسلة من كل القيم "أثمرت" في واحد، من خلال الدعوة list(generator()).




العائد هو كائن

A returnفي وظيفة سيعود قيمة واحدة.

إذا كنت تريد وظيفة للعودة مجموعة ضخمة من القيم وتستخدم yield.

الأهم من ذلك، yieldهو حاجز

مثل الجدار في كودا اللغة، وانها لن تنقل السيطرة حتى يحصل على الانتهاء منه.

أي

فإنه سيتم تشغيل التعليمات البرمجية في وظيفة الخاص بك من البداية حتى أنه يضرب yield. ثم، وأنها سوف تعود القيمة الأولى من الحلقة. ثم، فإن كل مكالمة أخرى تشغيل حلقة كنت قد كتبت في وظيفة واحدة لمزيد من الوقت، إرجاع القيمة التالية حتى ليس هناك قيمة للعودة.




(جوابي أدناه يتحدث فقط من وجهة نظر باستخدام بيثون مولد، وليس تنفيذ الكامن وراء آلية مولد ، الذي ينطوي على بعض الحيل من كومة والتلاعب كومة).

عندما yieldيستخدم بدلا من returnفي وظيفة الثعبان، يتم تشغيل هذه الوظيفة إلى شيء خاص يسمى generator function. وأن الدالة بإرجاع كائن من generatorنوع. و yieldالكلمة هي العلم أن تخطر مترجم الثعبان لعلاج هذه وظيفة خاصة. ستنتهي الوظائف العادية مرة واحدة يتم إرجاع بعض القيمة من ذلك. ولكن مع مساعدة من مترجم، وظيفة مولد يمكن اعتبار تكملة كما. وهذا هو، سوف يتم استعادة سياق التنفيذ وسيستمر تنفيذ من الماضي البعيد. حتى استدعاء صراحة العودة، والتي سوف تثير StopIterationاستثناء (والذي هو أيضا جزء من بروتوكول مكرر)، أو تصل إلى نهاية الدالة. لقد وجدت الكثير من الإشارات حول generatorولكن هذا واحدمن functional programming perspectiveهو الأكثر digestable.

(الآن أريد أن أتحدث عن الأساس المنطقي وراء generator، و iteratorعلى أساس فهم بلدي. آمل أن يكون هذا يمكن أن تساعدك على فهم الدافع الأساسي للمكرر والمولد. ويبين هذا المفهوم حتى في لغات أخرى أيضا مثل C #).

كما فهمت، عندما نريد معالجة مجموعة من البيانات، ونحن عادة أولا تخزين البيانات في مكان ما ثم معالجته واحدا تلو الآخر. ولكن هذه بديهية النهج هو إشكالية. وإذا كان حجم البيانات ضخمة، انها باهظة الثمن لتخزينها كما مسبقا كله. وذلك بدلا من تخزين dataنفسها مباشرة، لماذا لا تخزن نوعا من metadataغير مباشرة، أيthe logic how the data is computed .

هناك 2 النهج التفاف هذه الفوقية.

  1. النهج OO، نحن التفاف الفوقية as a class. وهذا هو ما يسمى iteratorالذي ينفذ بروتوكول مكرر (أي __next__()، و __iter__()الأساليب). وهذا أيضا هو شائع نمط تصميم مكرر .
  2. النهج الوظيفي، ونحن التفاف الفوقية as a function. وهذا هو ما يسمى ب generator function. ولكن تحت غطاء محرك السيارة، وعاد generator objectلا يزال IS-Aمكرر لأنه ينفذ أيضا بروتوكول مكرر.

وفي كلتا الحالتين، يتم إنشاء مكرر، إذ أن بعض الكائنات التي يمكن أن تعطيك البيانات التي تريدها. قد يكون النهج OO معقدة بعض الشيء. على أي حال، أي واحد لاستخدام متروك لكم.




و yieldالكلمة ببساطة بجمع النتائج العودة. التفكير في yieldمثلreturn +=




وهنا بسيطة yieldالنهج القائم، لحساب سلسلة فيبوناكسي، وأوضح:

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

عند إدخال هذا الأمر في REPL الخاص بك ثم حاول والذي يطلق عليه، وستحصل على نتيجة حيرة:

>>> 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!'

نتائج غير المتداولة هي ما المطبوعة. والنتيجة هي ما نقلت وعاد من yield. ندعو nextمرة أخرى الآن:

>>> 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حلقة).




كثير من الناس استخدام 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تستخدم 3 خطوط بدلا من 5 ولديها واحد أقل متغير ما يدعو للقلق.

هذه هي النتيجة من التعليمات البرمجية:

كما يمكنك أن ترى كل وظائف تفعل الشيء نفسه، والفرق الوحيد return_dates()يعطي قائمة و yield_dates()يعطي مولد

ومن الأمثلة حياة الحقيقي يكون شيئا مثل قراءة خط ملف سطرا أو إذا كنت ترغب فقط في جعل مولد




وباختصار، فإن 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وظيفة التي تنتج مجموعة iterable من الأرقام، يمكنك أن تفعل ذلك مثل ذلك،

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

واستخدامه مثل هذا؛

for i in myRangeNaive(10):
    print i

ولكن هذا هو ineffecient ل

  • يمكنك إنشاء مجموعة التي تستخدمها مرة واحدة فقط (هذه الذاكرة النفايات)
  • هذا الرمز حلقات في الواقع أكثر من ذلك الصفيف مرتين! :(

لحسن الحظ كان جويدو وفريقه سخية بما يكفي لتطوير مولدات ولذا فإننا يمكن أن تفعل هذا فقط.

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

for i in myRangeSmart(10):
    print i

الآن على كل تكرار وظيفة على مولد دعا next()ينفذ وظيفة حتى أنه إما تصل إلى "العائد" البيان الذي يتوقف و "الغلة" قيمة أو تصل إلى نهاية الدالة. في هذه الحالة على المكالمة الأولى، next()ينفذ حتى بيان المحصول وتسفر 'ن'، على التالي نسميها سيتم تنفيذ بيان الزيادة، والقفز إلى 'الوقت'، وتقييم ذلك، وإذا كان هذا صحيحا، فإنه سيتم إيقاف و تحقق 'ن' مرة أخرى، وسوف تستمر على هذا النحو حتى ترجع حالة في حين كاذبة ومولد يقفز إلى نهاية الدالة.




و yieldالكلمة

في لمحة، و yieldيستخدم لتحديد بيان المولدات الكهربائية، واستبدال returnوظيفة لتوفير نتيجة لذلك إلى المتصل به دون تدمير المتغيرات المحلية. على عكس وظيفة، حيث في استدعاء كل ذلك يبدأ مع مجموعة جديدة من المتغيرات، ومولد استئناف التنفيذ حيث تركت تشغيله.

حول مولدات بيثون منذ yieldيستخدم الكلمة فقط مع المولدات، فمن المنطقي أن نشير إلى مفهوم مولدات أولا.

فكرة المولدات هي لحساب سلسلة من النتائج واحدة من جانب واحد على الطلب (على الطاير). في أبسط الحالات، مولد ويمكن استخدام قائمة، حيث يتم احتساب كل عنصر بتكاسل. دعونا نقارن قائمة والمولدات التي تفعل نفس الشيء - العودة صلاحيات اثنين:

>>> # First, we define a list
>>> the_list = [2**x for x in range(5)]
>>>
>>> # Type check: yes, it's a list
>>> type(the_list)
<class 'list'>
>>>
>>> # Iterate over items and print them
>>> for element in the_list:
...     print(element)
...
1
2
4
8
16
>>>
>>> # How about the length?
>>> len(the_list)
5
>>>
>>> # Ok, now a generator.
>>> # As easy as list comprehensions, but with '()' instead of '[]':
>>> the_generator = (x+x for x in range(3))
>>>
>>> # Type check: yes, it's a generator
>>> type(the_generator)
<class 'generator'>
>>>
>>> # Iterate over items and print them
>>> for element in the_generator:
...     print(element)
...
0
2
4
>>>
>>> # Everything looks the same, but the length...
>>> len(the_generator)
Traceback (most recent call last):
  File "", line 1, in
TypeError: object of type 'generator' has no len()

بالتكرار عبر قائمة ومولد تبدو تماما نفس الشيء. ومع ذلك، على الرغم من أن مولد iterable، فإنه ليس مجموعة، وبالتالي لا يوجد لديه طول. مجموعات (القوائم، الصفوف، ومجموعات، الخ) إبقاء جميع القيم في الذاكرة ويمكننا الوصول إليها كلما دعت الحاجة. مولد بحساب القيم على الطاير وينسى لهم، لذلك ليس لديها أي نظرة عامة حول مجموعة النتائج الخاصة.

المولدات هي مفيدة بشكل خاص للمهام تستهلك ذاكرة، حيث ليست هناك حاجة للحفاظ على جميع عناصر قائمة الذاكرة الثقيلة يمكن الوصول إليها في نفس الوقت. حساب مجموعة من القيم واحدة تلو الأخرى ويمكن أيضا أن يكون مفيدا في الحالات التي تحتاج أبدا نتيجة كاملة، مما أسفر عن نتائج المتوسطة إلى المتصل حتى بعض الشرط هو راض ويتوقف مزيد من المعالجة.

باستخدام بيثون yieldالكلمة

وخير مثال هو مهمة البحث، حيث عادة ليست هناك حاجة إلى الانتظار لجميع النتائج التي يمكن العثور عليها. إجراء بحث نظام الملفات، فإن المستخدم سيكون أكثر سعادة لتلقي النتائج على ذبابة، بدلا من الانتظار للحصول على محرك البحث للذهاب من خلال كل ملف واحد وفقط بعد ذلك بإرجاع نتائج. هل هناك أي أشخاص الذين ينتقلون حقا من خلال جميع نتائج بحث Google حتى الصفحة الأخيرة؟

وبما أنه لا يمكن إنشاء وظائف البحث باستخدام-comprehensions قائمة، ونحن نذهب لتحديد مولد باستخدام وظيفة مع بيان العائد / الكلمة. يجب وضع تعليمات العائد إلى مكان مولد إرجاع نتيجة وسيطة إلى المتصل وينام حتى يحدث الاحتجاج المقبل.

def search(keyword, filename):
    print('generator started')
    f = open(filename, 'r')
    # Looping through the file line by line
    for line in f:
        if keyword in line:
            # If keyword found, return it
            yield line
    f.close()

حتى الآن وقد وصفت أكثر الجوانب العملية للمولدات بيثون. لمزيد من المعلومات التفصيلية ومناقشة مثيرة للاهتمام نلقي نظرة على اقتراح بيثون تعزيز 255، الذي يناقش سمة من سمات اللغة في التفاصيل.

Pythoning سعيدة! لمزيد من المعلومات الذهاب إلى http://pythoncentral.io/python-generators-and-yield-keyword/




بعد TL آخر؛ DR

مكرر على قائمة : next()إرجاع العنصر التالي في القائمة

مولد مكرر : next()سوف يحسب العنصر التالي على الطاير (تنفيذ التعليمات البرمجية)

تستطيع أن ترى العائد / مولد كوسيلة لتشغيل يدويا التحكم في التدفق من الخارج (مثل مواصلة حلقة 1 الخطوة)، من خلال الدعوة المقبل، مهما كانت معقدة التدفق.

ملاحظة: المولد هو NOT وظيفة عادية، فإنه يتذكر حالة سابقة مثل المتغيرات المحلية (كومة)، انظر الأجوبة أو مقالات لشرح مفصل أخرى، المولد لا يمكن إلا أن كرر على مرة واحدة . هل يمكن الاستغناء عنها yieldلكنه لن يكون لطيفا، لذلك يمكن اعتبار "لطيفة جدا" السكر اللغة.