it-swarm-tr.com

"Verim" anahtar kelimesi ne işe yarar?

Python'da yield anahtar sözcüğünün kullanımı nedir? Bu ne işe yarıyor?

Örneğin, bu kodu anlamaya çalışıyorum1:

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  

Ve bu arayan:

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 yöntemi çağrıldığında ne olur? Liste geri döndü mü? Tek bir element mi? Yine mi arandı? Sonraki çağrılar ne zaman durur?


1. Kod metrik uzaylar için harika bir Python kütüphanesi yapan Jochen Schulz'dan (jrschulz) geliyor. Bu, tüm kaynağa link: Modül mspace .

8943
Alex. S.

yield'in ne yaptığını anlamak için, jeneratörlerin ne olduğunu anlamanız gerekir. Ve jeneratörleri anlayabilmeniz için önce şunu söylemelisiniz yinelemeler .

Iterables

Bir liste oluşturduğunuzda, öğelerini tek tek okuyabilirsiniz. Öğelerini birer birer okumak, yineleme olarak adlandırılır:

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

mylist bir yinelenebilir . Bir liste kavrama kullandığınızda, bir liste ve böylece yinelenebilir bir şey yaratırsınız:

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

"for... in..." işlevini kullanabileceğiniz her şey yinelenebilir; lists, strings, dosyalar ...

Bu yinelemeler kullanışlıdır, çünkü bunları istediğiniz kadar okuyabilirsiniz, ancak tüm değerleri hafızada saklarsınız ve bu her zaman istediğiniz gibi olmaz.

Jeneratörler

Jeneratörler yineleyicilerdir, bir tür yinelenebilir yalnızca bir kez yineleyebilir . Jeneratörler tüm değerleri hafızada tutmazlar, anında değerleri oluştururlar :

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

Aynı, () yerine [] kullanmanız dışında aynı. ANCAK, siz , jeneratörler yalnızca bir kez kullanılabildiğinden, ikinci kez for i in mygenerator gerçekleştiremezsiniz: 0'ı hesaplarlar, sonra unuturlar ve 1'i hesaplarlar ve 4'ü birer birer hesaplamayı sonlandırırlar.

Yol ver

yield, return gibi kullanılan, anahtar sözcüktür, ancak işlev bir jeneratör döndürür.

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

İşte işe yaramaz bir örnek, ancak işlevinizin yalnızca bir kez okumanız gereken büyük bir değer kümesi getireceğini bildiğinizde kullanışlıdır.

yield öğesinde ustalaşmak için, işlevi çağırdığınızda bunu anlamalısınız, işlev gövdesine yazdığınız kod çalışmaz. İşlev yalnızca jeneratör nesnesini döndürür, bu biraz zor :-)

Ardından, kodunuz for jeneratörü her kullandığında bıraktığı yerden devam eder.

Şimdi zor kısmı:

for, fonksiyonunuzdan yaratılan jeneratör nesnesini ilk çağırdığında, fonksiyonunuzdaki kodu baştan itibaren yield tuşuna basana kadar çalıştırır, sonra döngünün ilk değerini döndürür. Ardından, diğer aramalar fonksiyona yazdığınız döngüyü bir kez daha çalıştırır ve döndürülecek değer elde edilinceye kadar bir sonraki değeri döndürür.

İşlev çalıştıktan sonra jeneratör boş sayılır, ancak artık yield tuşuna basmaz. Bunun nedeni, döngünün sona ermesi ya da artık bir "if/else" öğesini tatmin etmemeniz olabilir.


Kodunuz açıklandı

Jeneratör:

# 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

Arayan:

# 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

Bu kod birkaç akıllı parça içerir:

  • Döngü bir listede yinelenir, ancak döngü yinelenirken liste genişler :-) Sonsuz bir döngüye ulaşabildiğiniz için biraz tehlikeli olsa bile bu iç içe geçmiş tüm verileri gözden geçirmenin özlü bir yoludur. Bu durumda, candidates.extend(node._get_child_candidates(distance, min_dist, max_dist)), jeneratörün tüm değerlerini tüketir, ancak while, aynı düğümde uygulanmadığından, öncekilerden farklı değerler üretecek yeni jeneratör nesneleri oluşturmaya devam eder.

  • extend() yöntemi yinelenebilir bekleyen ve değerlerini listeye ekleyen bir liste nesnesi yöntemidir.

Genellikle bir liste çıkarırız:

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

Ancak kodunuzda bir jeneratör var, ki bu iyi çünkü:

  1. Değerleri iki kez okumanıza gerek yok.
  2. Çok fazla çocuğunuz olabilir ve hepsinin bellekte saklanmasını istemiyorsunuz.

Ve işe yarıyor çünkü Python, bir yöntemin argümanının bir liste olup olmadığına aldırmıyor. Python yinelemeler bekler, böylece dizelerle, listelerle, totelerle ve jeneratörler ile çalışır! Buna ördek yazması denir ve Python'in bu kadar iyi olmasının nedenlerinden biri. Ama bu başka bir hikaye, başka bir soru için ...

Bir jeneratörün gelişmiş kullanımını görmek için burada durabilir veya biraz okuyabilirsiniz:

Jeneratör yorgunluğunu kontrol etme

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

Not: Python 3 içinprint(corner_street_atm.__next__()) veya print(next(corner_street_atm)) kullanın

Bir kaynağa erişimi kontrol etmek gibi çeşitli şeyler için faydalı olabilir.

İtertools, en iyi arkadaşın

İtertools modülü, tekrarlanabilirleri işlemek için özel fonksiyonlar içerir. Hiç bir jeneratörü çoğaltmak istediniz mi? Zincir iki jeneratör? Tek astarlı iç içe bir listede grup değerleri? Map / Zip başka bir liste oluşturmadan?

O zaman sadece import itertools.

Bir örnek? Dört atlı bir yarış için olası varış talimatlarını görelim:

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

Yinelemenin iç mekanizmalarını anlama

Yineleme yinelemeleri (__iter__() yöntemini uygulayan) ve yineleyicileri (__next__() yöntemini uygulayan) içeren bir işlemdir. Yinelemeler, yineleyiciyi alabileceğiniz nesnelerdir. Yineleyiciler yinelemeler üzerinde yinelemenizi sağlayan nesnelerdir.

Bu makalede daha fazlası var nasıl for döngüler işe yarıyor .

13704
e-satis

Grokkingyield için kısayol

yield ifadeleriyle bir işlev gördüğünüzde, ne olacağını anlamak için bu kolay numarayı uygulayın:

  1. Fonksiyonun başına bir satır result = [] ekleyin.
  2. Her yield expr __ result.append(expr) ile değiştirin.
  3. İşlevin altına bir satır return result ekleyin.
  4. Yay - daha fazla yield deyimi! Kodu oku ve çöz.
  5. Fonksiyonu orijinal tanımla karşılaştırın.

Bu numara size işlevin arkasındaki mantık hakkında bir fikir verebilir, ancak gerçekte yield ile olan şey, listeye dayalı yaklaşımda olanlardan önemli ölçüde farklıdır. Çoğu durumda, verim yaklaşımı çok daha fazla bellek verimli ve daha hızlı olacaktır. Diğer durumlarda, bu numara, orijinal fonksiyonun iyi çalışmasına rağmen sonsuz bir döngüde sıkışıp kalmanıza neden olur. Daha fazlasını öğrenmek için okumaya devam edin ...

Yineleyicilerinizi, yineleyicilerinizi ve jeneratörlerinizi karıştırmayın.

İlk olarak, yineleyici protokolü - yazarken

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

Python aşağıdaki iki adımı gerçekleştirir:

  1. mylist için bir yineleyici alır:

    iter(mylist) -> çağrısı, next() yöntemiyle bir nesneyi döndürür (veya Python 3'te __next__()).

    [Bu, çoğu insanın size anlatmayı unuttuğu adımdır]

  2. Öğeler üzerinde döngü oluşturmak için yineleyiciyi kullanır:

    Yineleyicideki next() yöntemini çağırmaya devam edin 1. adımdan geri döndü. next() öğesinden dönüş değeri x öğesine atanır ve döngü gövdesi yürütülür. Bir istisna StopIterationnext() içinden yükseltilirse, yinelemede daha fazla değer olmadığı ve döngüden çıkıldığı anlamına gelir.

Gerçek şu ki, Python yukarıdaki iki adımı istediği zaman gerçekleştirir, bir nesnenin içeriğini loop over - yani bir for döngüsü olabilir, ancak otherlist.extend(mylist) gibi bir kod da olabilir (otherlist bir Python listesi ).

Burada mylist bir yinelenebilir, çünkü yineleyici protokolünü uygular. Kullanıcı tanımlı bir sınıfta, sınıfınızın örneklerini yinelenebilir hale getirmek için __iter__() yöntemini uygulayabilirsiniz. Bu yöntem bir yineleyici döndürmelidir. Bir yineleyici, next() yöntemiyle bir nesnedir. Hem __iter__() hem de next() öğelerini aynı sınıfa uygulamak ve __iter__() dönüş self döndürmek mümkündür. Bu basit durumlar için işe yarayacaktır, ancak iki yinelemenin aynı nesnenin üzerinde aynı anda dönmesini istemiyorsanız.

Yani yineleme protokolü, birçok nesne bu protokolü uygular:

  1. Yerleşik listeler, sözlükler, dosyalar, kümeler, dosyalar.
  2. __iter__() öğesini uygulayan kullanıcı tanımlı sınıflar.
  3. Jeneratörler.

Bir for döngüsünün ne tür bir nesne ile uğraştığını bilmediğini unutmayın - yalnızca yineleyici protokolünü izler ve öğeden sonra next() adını verdiği öğeyi almaktan mutluluk duyar. Yerleşik listeler, öğelerini birer birer döndürür, sözlükler anahtarlar birer birer döndürür, dosyalar satırlar _ birer birer döndürür, vb. yield geliyor:

def f123():
    yield 1
    yield 2
    yield 3

for item in f123():
    print item

yield ifadeleri yerine, f123() öğesinde üç return ifadesi olsaydı, yalnızca birincisi çalıştırılırdı ve işlev kapanırdı. Ancak f123() sıradan bir işlev değildir. f123() çağrıldığında, yok, verim ifadelerindeki değerlerden herhangi birini döndürür! Bir jeneratör nesnesini döndürür. Ayrıca, fonksiyon gerçekten çıkmaz - askıya alınmış bir duruma geçer. for döngüsü, jeneratör nesnesi üzerinde döngü oluşturmaya çalıştığında, işlev, daha önce döndürdüğü yield öğesinden sonraki satırda askıya alınmış durumundan devam eder, bu durumda bir yield deyimi çalıştırır ve sonraki öğe. Bu, fonksiyon çıkıncaya kadar gerçekleşir, bu noktada jeneratör StopIteration yükseltir ve döngü çıkar. 

Böylece jeneratör nesnesi bir adaptöre benzer - bir ucunda for döngüsünü mutlu tutmak için __iter__() ve next() yöntemlerini göstererek yineleyici protokolünü gösterir. Bununla birlikte, diğer ucunda, bir sonraki değeri elde etmek için işlevi yeterince çalıştırır ve askıya alma moduna geri döndürür.

Neden Jeneratör Kullanmalı?

Genellikle, jeneratör kullanmayan ancak aynı mantığı uygulayan kod yazabilirsiniz. Bir seçenek daha önce bahsettiğim geçici liste 'hile' kullanmaktır. Örneğin, bu her durumda işe yaramayacaktır. Sonsuz döngüleriniz varsa veya çok uzun bir listeniz varsa hafızayı verimsiz kullanabilir. Diğer yaklaşım, durumlarını üye tutan ve bir sonraki mantıksal adımı next() (ya da Python 3'te __next__()) yönteminde gerçekleştiren bir sonraki yinelenebilir sınıf SomethingIter uygulamaktır. Mantığa bağlı olarak, next() yönteminin içindeki kod çok karmaşık görünebilir ve hatalara açık olabilir. Burada jeneratörler temiz ve kolay bir çözüm sunar.

1751
user28409

Bu şekilde düşün:

Bir yineleyici, bir next () metoduna sahip bir nesne için sadece şık bir sondaj terimidir. Böylece verim kazandırma işlevi şu şekilde olur:

Orijinal versiyon:

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

for i in some_function():
    print i

Bu temelde Python yorumlayıcısının yukarıdaki kodla yaptığı şeydir:

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

Sahne arkasında neler olup bittiğiyle ilgili daha fazla bilgi için for döngüsü şuna yeniden yazılabilir:

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

Bu daha mantıklı mı yoksa sadece seni daha fazla mı karıştırıyor? :)

Bunun, açıklama amaçlı olarak -aşırı basitleştirme olduğunu unutmamalıyım. :)

445
Jason Baker

yield anahtar sözcüğü iki basit gerçeğe indirgenir:

  1. Derleyici, bir fonksiyonun herhangi bir yerinde yield anahtar kelimesini her yerdetespit ederse, bu fonksiyon artık return deyimi ile geri dönmez.yerine, hemen , bir tembel "bekleyen liste" nesnesini döndürür bir jeneratör
  2. Bir jeneratör yinelenebilir. yinelenebilirnedir? Her öğeyi belirli bir sırayla ziyaret etmek için yerleşik bir protokolle list veya set veya range veya dict-view gibi bir şey.

Özet olarak: bir jeneratör tembel, artımlı olarak beklemede olan bir listedir , veyield ifadeleri, liste değerlerini programlamak için fonksiyon gösterimini kullanmanıza izin verir jeneratör kademeli olarak tükürmelidir.

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

Örnek

Şimdi bir fonksiyon tanımlayalım makeRange, Python'un range gibi. makeRange(n) çağrısı JENERATÖR GERİ DÖ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>
_

Jeneratörü beklemedeki değerlerini hemen geri göndermeye zorlamak için, bunu list() içine aktarabilirsiniz (tıpkı yinelenebilir gibi):

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

"Sadece bir liste döndürmek" örneğiyle karşılaştırmak

Yukarıdaki örnekte sadece eklediğiniz ve iade ettiğiniz bir liste oluşturduğu düşünülebilir:

_# 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]
_

Yine de büyük bir fark var; son bölüme bakınız.


Jeneratörleri nasıl kullanabilirsiniz?

Yinelenebilir bir listenin kavranmasının son kısmıdır ve tüm üreteçler yinelenebilir, bu yüzden sıklıkla şöyle kullanılırlar:

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

Jeneratörler için daha iyi bir his elde etmek için itertools modülü ile oynayabilirsiniz (garanti edildiğinde chain yerine _chain.from_iterable_ kullandığınızdan emin olun). Örneğin, jeneratörleri, itertools.count() gibi sonsuz uzun süreli tembel listeler uygulamak için bile kullanabilirsiniz. Kendi def enumerate(iterable): Zip(count(), iterable) dosyanızı uygulayabilir veya alternatif olarak while-döngüsündeki yield anahtar sözcüğünü kullanarak yapabilirsiniz.

Lütfen dikkat: jeneratörler aslında coroutines uygulamak veya deterministik olmayan programlama veya diğer zarif şeyler gibi birçok şey için kullanılabilir. Ancak, burada sunulan "tembel listeler" bakış açısı bulabileceğiniz en yaygın kullanımdır.


Kamera ARKASI

"Python yineleme protokolü" bu şekilde çalışır. Bu, list(makeRange(5)) işlevini yaptığınızda neler oluyor. Bu daha önce "tembel, artan bir liste" olarak tanımladığım şey.

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

Yerleşik işlevi next(), yalnızca "yineleme protokolünün" bir parçası olan ve tüm yineleyicilerde bulunan .next() işlevini çağırır. Genellikle okunabilirlik pahasına, süslü şeyleri uygulamak için next() işlevini (ve yineleme protokolünün diğer bölümlerini) el ile kullanabilirsiniz, bu yüzden bunu yapmaktan kaçının ...


Önemsiz ayrıntılar

Normalde, çoğu insan aşağıdaki farklılıkları önemsemez ve muhtemelen burada okumayı bırakmak ister.

Python-speak, yinelenebilir, bir liste _[1,2,3]_ ve bir yineleyici gibi "for-loop kavramını anlayan" herhangi bir nesnedir., [1,2,3].__iter__() gibi istenen for döngüsü için belirli bir örnektir. Bir jeneratör, yazılma şekli dışında (işlev sözdizimi ile), herhangi bir yineleyici ile tamamen aynıdır.

Bir listeden bir yineleyici istediğinde, yeni bir yineleyici oluşturur. Bununla birlikte, bir yineleyiciden (nadiren yapacağınız) bir yineleyici isteğinde bulunduğunuzda, yalnızca kendi kopyasını verir.

Böylece, olası bir olayda böyle bir şey yapamamanız ...

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

... o zaman bir jeneratörün bir yineleyici; yani, bir kerelik kullanımdır. Tekrar kullanmak istiyorsanız, myRange(...) adresini tekrar aramalısınız. Sonucu iki kez kullanmanız gerekirse, sonucu bir listeye dönüştürün ve bunu x = list(myRange(5)) değişkeninde saklayın. Bir jeneratörü kesinlikle klonlamaya ihtiyaç duyanlar (örneğin, korkunç bir şekilde metaprogramlamayı hackleyenler), itertools.tee kesinlikle gerekliyse, kopyalanabilir yineleyiciden Python PEP standartlar önerisi ertelendi.

414
ninjagecko

yield, tıpkı return gibi bir şeydir - ne diyorsan onu verir (bir jeneratör olarak). Fark, bir dahaki sefere jeneratörü çağırdığınızda, yürütmenin son çağrıdan yield deyimine başlamasıdır. Return'den farklı olarak, bir verim gerçekleştiğinde yığın çerçevesi temizlenmez, ancak kontrol arayana geri gönderilir, böylece işlev bir daha çağrıldığında durumu devam eder.

Kodunuzda get_child_candidates işlevi bir yineleyici gibi işlev görür, böylece listenizi genişlettiğinizde yeni listeye bir öğe ekler.

list.extend bitinceye kadar bir yineleyici çağırır. Gönderdiğiniz kod örneğinde, sadece bir Tuple döndürüp listeye eklemek çok daha net olacaktır.

260
Douglas Mayle

Söylenecek ilave bir şey daha var: Verimi artıran bir işlev aslında. Böyle bir kod yazdım:

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

Sonra bunu diğer kodlarda kullanabilirim:

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

Bazı sorunların basitleştirilmesine gerçekten yardımcı olur ve bazı şeylerin birlikte çalışmasını kolaylaştırır. 

199
Claudiu

Minimal bir çalışma örneği tercih edenler için, bu etkileşimli Python oturumunda meditasyon yapın:

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

TL; DR

Bunun yerine:

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

bunu yap:

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

Sıfırdan bir liste oluştururken ne zaman bulursanız, onun yerine yield

Bu benim ilk "aha" anımdı.


yield, şekerli söylemenin bir yoludur 

bir seri şeyler inşa etmek

Aynı davranış:

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

Farklı davranış:

Verim tek geçişli: yalnızca bir kez yineleme yapabilirsiniz. Bir fonksiyonun içinde bir verim olduğunda, ona bir jeneratör işlevi diyoruz. Ve bir yineleyici döndürdüğü şeydir. Bu terimler ortaya çıkıyor. Bir konteynerin rahatlığını kaybediyoruz, ancak gerektiğinde hesaplanan bir dizinin gücünü ve keyfi olarak kazanıyoruz.

Verim tembel, hesaplamayı durdurur. İçinde bulunan bir işlev aslında onu çağırdığınızda hiç çalışmaz. Kaldığı yeri hatırlayan bir yineleyici nesnesi döndürür. Yinelemede next() 'ı her çağırdığınızda (bu for-loop'da gerçekleşir) bir sonraki verime ilerleyen inç. return, StopIteration'ı yükseltir ve diziyi sonlandırır (bu, for-loop'un doğal sonu).

Verim çok yönlüdür. Verilerin hep birlikte depolanması gerekmemektedir, her seferinde bir tane kullanılabilir hale getirilebilir. Sonsuz olabilir.

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

Çoklu geçişlere ihtiyacınız varsa ve seri çok uzun değilse, bunun üzerine list() dosyasını çağırın:

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

yield Kelimesinin mükemmel tercihi çünkü her iki anlam da uygula:

verim - üretmek veya sağlamak (tarımda olduğu gibi)

... serideki bir sonraki verileri sağlar.

verim - yol göster veya bırak (siyasi iktidarda olduğu gibi)

... yineleyici ilerleyene kadar CPU çalışmasından vazgeç.

160
Bob Stein

Kazanç size bir jeneratör verir. 

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

Gördüğünüz gibi, ilk durumda foo listenin tamamını aynı anda bellekte tutuyor. 5 elementli bir liste için büyük bir sorun değil, peki ya 5 milyonluk bir liste istiyorsan? Bu sadece büyük bir hafıza yiyici değil, aynı zamanda işlevin çağrıldığı anı oluşturmak için de çok zaman alıyor. İkinci durumda, çubuk sadece size bir jeneratör verir. Bir jeneratör yinelenebilir - bu, onu bir for vb. Döngüde kullanabileceğiniz anlamına gelir, ancak her değere yalnızca bir kez erişilebilir. Tüm değerler aynı zamanda bellekte de saklanmaz; Jeneratör nesnesi, onu en son ne zaman aradığınızda döngüdeyken "hatırlıyor" - bu yolla, (yani) 50 milyar sayıma kadar bir yinelemeli kullanıyorsanız, tümü 50 milyar saymak zorunda değilsiniz. hemen ve saymak için 50 milyar numarayı saklayın. Yine, bu oldukça tartışmalı bir örnek, gerçekten 50 milyar sayarsanız, itertools kullanırsınız. :)

Jeneratörlerin en basit kullanım şekli budur. Söylediğiniz gibi, bir çeşit yığın değişkeni kullanmak yerine arama yığınında bir şeyler yukarı itmek için verimi kullanarak verimli izinler yazmak için kullanılabilir. Jeneratörler ayrıca özelleştirilmiş ağaç geçişi ve diğer her türlü şey için de kullanılabilir.

148
RBansal

Bir jeneratör döndürüyor. Python'a özellikle aşina değilim, ancak bununla aşina iseniz, C # 'ın yineleyici blokları ile aynı şeyin olduğuna inanıyorum.

Temel fikir, derleyici/tercüman/ne yaparsa yapsın hile yapmaktır, böylece arayanlar söz konusu olduğunda, bir sonraki çağrıyı devam ettirebilirler () ve değerleri döndürmeye devam edecektir - / jeneratör yöntemi duraklatılmış gibi. Şimdi açık bir şekilde, gerçekten bir yöntemi “duraklatamazsınız”, bu nedenle derleyici, şu anda nerede olduğunuzu ve yerel değişkenlerin vs. nasıl göründüğünü hatırlamanız için bir durum makinesi oluşturur. Bu, bir yineleyici yazmaktan çok daha kolaydır.

145
Jon Skeet

Jeneratörü nasıl kullanacağımı anlatan pek çok cevap arasında, henüz verilmediğini hissetmediğim bir cevap var. İşte programlama dili teorisi cevap:

Python'daki yielddeyimi bir jeneratör döndürür. Python'da bir jeneratör, döndüren bir işlevdir continuations (ve özellikle bir tür koroin, fakat süreklilik, neler olduğunu anlamak için daha genel bir mekanizmadır).

Programlama dilleri teorisindeki süreklilikler çok daha temel bir hesaplama türüdür, ancak sıklıkla kullanılmazlar, çünkü bunun için mantıklı olması ve uygulaması çok zordur. Ancak, devam etmenin ne olduğu fikri basit ve açıktır: henüz bitmemiş bir hesaplamanın halidir. Bu durumda, değişkenlerin mevcut değerleri, henüz gerçekleştirilmemiş olan işlemler ve benzerleri kaydedilir. Ardından programın ilerleyen noktalarında programın değişkenleri bu duruma sıfırlanacak ve kaydedilen işlemler gerçekleştirilebilecek şekilde devam edilebilir.

Süreklilikler, bu daha genel biçimde, iki şekilde uygulanabilir. call/cc yolunda, programın yığını tam anlamıyla kaydedilir ve ardından devam çağrıldığında, yığın geri yüklenir.

Devam eden geçiş stilinde (CPS), devam etmeler, programcının açıkça yönettiği ve alt rutinlere aktardığı normal fonksiyonlardır (sadece fonksiyonların birinci sınıf olduğu dillerde). Bu tarzda, program durumu, yığında bir yerde bulunan değişkenlerden ziyade kapaklar (ve bunlarda kodlanmış olan değişkenler) ile temsil edilir. Kontrol akışını yöneten fonksiyonlar devam etmeyi argüman olarak kabul eder (bazı CPS varyasyonlarında, fonksiyonlar birden fazla devam etmeyi kabul edebilir) ve kontrol akışını basitçe onları çağırarak ve daha sonra geri döndürerek değiştirerek işler. Devamlı geçiş stilinin çok basit bir örneği şöyledir:

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)

Bu (çok basit) örnekte, programcı dosyayı gerçekten bir süreklilik içine yazma işlemini kaydeder (potansiyel olarak yazmak için birçok ayrıntı içeren çok karmaşık bir işlem olabilir) ve ardından bu sürekliliği (örneğin ilk olarak) sınıf kapatma) biraz daha işlem yapan başka bir operatöre ve sonra gerekirse çağırır. (Bu tasarım desenini gerçek GUI programlamasında çok kullanıyorum, çünkü bana kod satırlarını kaydediyor veya daha da önemlisi GUI olaylarının tetiklenmesinden sonra kontrol akışını yönetmek için.

Bu yazının geri kalanı, genelliği kaybetmeden, devam etmeyi CPS olarak kavramsallaştıracak, çünkü anlaşılması ve okunması çok daha kolay.


Şimdi Python'daki jeneratörler hakkında konuşalım. Jeneratörler, devam etmenin belirli bir alt tipidir. Devam etmeler genel olarak bir hesaplama) durumunu kaydedebilirken [] (yani programın çağrı yığını), jeneratörler yalnızca bir iterasyon durumunu yineleyici üzerinden kaydedebilir). Bununla birlikte, bu tanım jeneratörlerin belirli kullanım durumları için biraz yanıltıcıdır. Örneğin:

def f():
  while True:
    yield 4

Bu açıkça, davranışları iyi tanımlanmış makul bir yinelemektir - jeneratör her seferinde yinelendiğinde, 4 değerini döndürür (ve bunu sonsuza dek yapar). Ancak, muhtemelen yineleyicileri düşünürken akla gelen, prototipik bir yinelemeli tip değildir (yani for x in collection: do_something(x)). Bu örnek, jeneratörlerin gücünü gösterir: eğer bir şey bir yineleyici ise, bir jeneratör yineleme durumunu koruyabilir.

Yinelemek için: Devam etmeler bir programın yığınının durumunu kaydedebilir ve jeneratörler yinelemenin durumunu kaydedebilir. Bu, sürekliliğin jeneratörlerden çok daha güçlü olduğu, ancak jeneratörlerin çok, çok daha kolay olduğu anlamına gelir. Dil tasarımcısının uygulaması kolaydır ve programcının kullanması daha kolaydır (yazmak için biraz zamanınız varsa, okuma ve anlamaya çalışın/ devam etmeleri ve/cc ile ilgili bu sayfa).

Ancak, jeneratörleri basit, özel bir sürekli geçiş tarzı olarak kolayca uygulayabilir (ve kavramsallaştırabilirsiniz):

yieldne zaman çağrılırsa, işleve bir devam döndürmesi söylenir. İşlev tekrar çağrıldığında, bıraktığı yerden başlar. Dolayısıyla, sözde-sözde kodda (yani, sözde kod değil, ancak kod değil), jeneratörün nextyöntemi temel olarak şöyledir:

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

yieldanahtar sözcüğü, gerçek üreteç işlevi için aslında sözdizimsel şekerdir;

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

Unutmayın ki bu sadece sahte koddur ve jeneratörlerin Python'daki gerçek uygulaması daha karmaşıktır. Ancak, neler olduğunu anlamak için bir alıştırma olarak, yieldanahtar sözcüğünü kullanmadan jeneratör nesnelerini uygulamak için sürekli geçiş stilini kullanmaya çalışın.

134
aestrivex

İşte düz dilde bir örnek. Yüksek seviye insan kavramları ile düşük seviye Python kavramları arasında bir yazışma sağlayacağım.

Bir dizi dizide işlem yapmak istiyorum, ancak bu dizinin yaratılmasıyla kendimi rahatsız etmek istemiyorum, sadece yapmak istediğim işleme odaklanmak istiyorum. Bu yüzden, aşağıdakileri yapıyorum:

  • Sizi ararım ve size belirli bir şekilde üretilen bir sayı dizisi istediğimi söylerim ve algoritmanın ne olduğunu size bildiririm. 
    Bu adım, jeneratör işlevindeki defining, yani yield içeren işlevlere karşılık gelir.
  • Bir süre sonra size, "Tamam, bana sayıların sırasını anlatmaya hazırlanın" dedim. 
    Bu adım, bir jeneratör nesnesini döndüren jeneratör işlevinin çağrılmasına karşılık gelir. Bana henüz herhangi bir numara söylemediğinizi unutmayın; Sadece kağıt ve kalemini al.
  • "Bana bir sonraki numarayı söyle" derim ve sen bana ilk numarayı söylersin; ondan sonra, bir sonraki numarayı sormamı beklersin. Nerede olduğunuzu, daha önce hangi sayıları söylediğinizi ve bir sonraki sayının ne olduğunu hatırlamak sizin işiniz. Detaylar umurumda değil. 
    Bu adım, jeneratör nesnesindeki .next() çağrısına karşılık gelir.
  • … Bir önceki adımı tekrarlayın,…
  • sonunda bir sona gelebilir. Bana bir numara söylemiyorsun; sadece bağırdın, "atlarını tut! İşim bitti! Artık numara yok!" 
    Bu adım, işini sona erdiren ve StopIteration istisnasını yükselten jeneratör nesnesine karşılık gelir. Jeneratör fonksiyonunun istisnayı arttırması gerekmez. İşlev bittiğinde veya bir return verdiğinde otomatik olarak kaldırılır.

Bir jeneratörün yaptığı budur (yield içeren bir işlev); çalıştırmaya başlar, yield ne zaman yaparsa duraklar ve .next() değeri istendiğinde en son olduğu noktadan devam eder. Sıralı olarak nasıl değer talep edileceğini açıklayan Python'un yineleyici protokolü ile tasarıma mükemmel uyum sağlar.

Yineleyici protokolünün en ünlü kullanıcısı Python'da for komutudur. Ne zaman bir yaparsan:

for item in sequence:

sequence öğesinin yukarıda açıklandığı gibi bir liste, bir dize, bir sözlük veya bir jeneratör object olması önemli değildir; sonuç aynıdır: bir dizideki öğeleri tek tek okuyorsunuz.

def anahtar sözcüğünü içeren bir yield anahtar sözcüğü içeren bir işlevin üreteç oluşturmanın tek yolu olmadığını unutmayın; bu, yaratmanın en kolay yolu.

Daha doğru bilgi için Python belgelerinde yineleyici türleri , verim ifadesi ve jeneratörler hakkında bilgi edinin.

120
tzot

Birçok cevap neden bir jeneratör oluşturmak için bir yield kullandığınızı gösterirken, yield için daha fazla kullanım alanı vardır. Bilginin iki kod bloğu arasında aktarılmasını sağlayan bir coroutine yapmak oldukça kolaydır. Bir jeneratör oluşturmak için yield kullanımıyla ilgili verilen herhangi bir iyi örneği tekrar etmeyeceğim.

yield öğesinin aşağıdaki kodda ne yaptığını anlamanıza yardımcı olmak için, parmağınızı kullanarak bir yield olan herhangi bir kodda döngüyü izlemek için kullanabilirsiniz. Parmağınız yield öğesine her basışında, bir next veya send girilmesini beklemeniz gerekir. next çağrıldığında, yield… ismini alana kadar kodu izlersiniz. yield öğesinin sağındaki kod değerlendirilir ve arayana geri gönderilir ... sonra beklersiniz. next tekrar çağrıldığında, kodda başka bir döngü gerçekleştirirsiniz. Bununla birlikte, bir coroinde, yield öğesinin, ayrıca arayan - işlevini veren bir değer gönderecek olan send… ile de kullanılabileceğini unutmayın. Bir send verilirse, yield gönderilen değeri alır ve sol tarafa gönderir… daha sonra yield ismine tekrar vurana kadar kod boyunca izler devam eder (next denir gibi).

Örneğin:

>>> 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()
105
Mike McKerns

Başka bir yield kullanımı ve anlamı var (Python 3.3'ten beri):

yield from <expr>

PEP 380 - Bir Alt Jeneratörü Temsil Etme Sözdizimi:

Bir jeneratörün faaliyetlerinin bir bölümünü başka bir jeneratöre devretmesi için bir sözdizimi önerilmiştir. Bu, 'verim' içeren bir kod bölümünün dışarı çıkarılıp başka bir jeneratöre yerleştirilmesine izin verir. Ek olarak, alt üretecin bir değerle geri dönmesine izin verilir ve bu değer, temsilci oluşturucu için kullanılabilir duruma getirilir.

Yeni sözdizimi, bir jeneratör başka bir kişi tarafından üretilen değerleri yeniden ürettiğinde optimizasyon için bazı fırsatlar da açar.

Dahası bu (Python 3.5'ten beri) tanıtacağı:

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

korotinlerin normal bir jeneratör ile karıştırılmasını önlemek için (bugün yield her ikisinde de kullanılır).

97
Sławomir Lenart

Tüm harika cevaplar, ancak yeni başlayanlar için biraz zor.

return deyimini öğrendiğinizi varsayıyorum.

Bir benzetme olarak, return ve yield ikizlerdir. return 'return and stop', 'verim' 'return, fakat devam et' anlamına gelir

  1. return ile num_list almayı deneyin.
def num_list(n):
    for i in range(n):
        return i

Çalıştır:

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

Görüyorsun, listeden ziyade sadece bir numara alıyorsun. return asla mutlu olmanıza izin vermez, sadece bir kere uygular ve çıkar.

  1. yield geliyor

return öğesini yield ile değiştirin:

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]

Şimdi, tüm sayıları elde etmek için sen kazandın.

Bir kez çalışan ve duran return ile karşılaştırıldığında, yield planladığınız zamanları çalıştırır. return öğesini return one of them ve yield öğelerini return all of them olarak yorumlayabilirsiniz. Buna iterable adı verilir.

  1. Bir adım daha, yield deyimini return ile yeniden yazabiliriz.
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 ile ilgili çekirdek.

Bir liste return çıktısı ile yield çıktısı nesnesi arasındaki fark şudur:

Bir liste nesnesinden her zaman [0, 1, 2] alırsınız, ancak bunları yalnızca 'nesne yield output' nesnesinden bir kez alabilir. Bu nedenle, generator nesnesinin Out[11]: <generator object num_list at 0x10327c990> ile gösterildiği gibi yeni bir adı var.

Sonuç olarak, onu parçalamak için bir metafor olarak:

  • return ve yield ikizlerdir
  • list ve generator ikizlerdir
95
Algebra

Python, sanki Python onlar için sözdizimsel şeker sağlamıyormuş gibi, gerçekte jeneratörlerin nasıl uygulanacağına dair bazı Python örnekleri:

Bir Python üreticisi olarak:

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

Jeneratörler yerine sözlük kapanışlarını kullanma

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)

Jeneratörler yerine nesne kapanışlarını kullanma (çünkü 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)
83
Dustin Getz

"Jeneratörlerin hızlı bir açıklaması için Beazley'in" Python: Essential Reference "sayfasını 19. sayfasını okuyacağım" yazacaktım, fakat pek çok kişi zaten iyi açıklamalar yaptı.

Ayrıca, yield öğesinin coroteinlerde, jeneratör işlevlerinde kullanımlarının ikili olarak kullanılabileceğini unutmayın. Kod snippet'inizle aynı kullanımda olmamasına rağmen, (yield), bir işlevde ifade olarak kullanılabilir. Bir arayan, send() yöntemini kullanarak yönteme bir değer gönderdiğinde, coroutine bir sonraki (yield) ifadesiyle karşılaşıncaya kadar yürütülür.

Jeneratörler ve koroinler veri akışı tipi uygulamaları ayarlamak için harika bir yoldur. İşlevlerde yield ifadesinin diğer kullanımları hakkında bilgi sahibi olmanın yarar olacağını düşündüm.

81
johnzachary

Bir programlama bakış açısından, yineleyiciler thunks olarak uygulanır.

Eşzamanlı çalıştırma için yineleyiciler, jeneratörler ve iş parçacığı havuzları, eşyalar olarak (aynı zamanda adsız işlevler de denir) uygulamak için, biri bir dağıtıcıya sahip olan bir kapanma nesnesine gönderilen mesajları kullanır ve dağıtıcı "iletilere" yanıt verir.

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

"next", "iter" çağrısı tarafından oluşturulan bir kapanmaya gönderilen bir mesajdır.

Bu hesaplamayı uygulamanın birçok yolu vardır. Mutasyon kullandım, ancak mevcut değeri ve bir sonraki vericiyi döndürerek mutasyon olmadan yapmak kolaydır.

İşte R6RS yapısını kullanan bir gösteri, ancak anlambilim kesinlikle Python's ile aynıdır. Aynı hesaplama modeli ve Python'da yeniden yazmak için yalnızca sözdizimindeki bir değişiklik gerekiyor.

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

İşte basit bir örnek:

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)

Çıktı:

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

Ben bir Python geliştiricisi değilim, fakat bana bakıyor. yield program akışını ve bir sonraki döngü "verim" konumundan başlıyor. Bu pozisyonda bekliyor gibi görünüyor ve bundan hemen önce dışarıya bir değer döndürmek ve bir dahaki sefere çalışmaya devam ediyor.

İlginç ve güzel bir yetenek gibi görünüyor: D

70
Engin OZTURK

İşte yield'in yaptıklarının zihinsel bir görüntüsü.

Bir iş parçacığının bir yığına sahip olduğunu düşünmeyi seviyorum (bu şekilde uygulanmadığında bile).

Normal bir işlev çağrıldığında, yerel değişkenlerini yığına koyar, bir miktar hesaplama yapar, sonra yığını temizler ve geri döner. Yerel değişkenlerinin değerleri bir daha asla görülmez.

Bir yieldişlevinde, kodu çalışmaya başladığında (yani, işlev çağrıldıktan sonra, next() yöntemi çağrılan bir jeneratör nesnesini döndürerek), yerel değişkenlerini yığına yerleştirir ve bir süre hesaplar. Ancak, yielddeyimine ulaştığında, yığının bir bölümünü temizlemeden ve geri döndürmeden önce, yerel değişkenlerinin anlık görüntüsünü alır ve bunları jeneratör nesnesine depolar. Ayrıca şu anda kodunda bulunduğu yeri de yazar (yani belirli yielddeyimi).

Yani jeneratörün üzerinde durduğu donmuş bir fonksiyon.

Daha sonra next() çağrıldığında, işlevin eşyalarını yığına alır ve yeniden canlandırır. Bu fonksiyon, kaldığı yerden hesaplamaya devam ediyor, soğuk hava deposunda sonsuzluk harcadığı gerçeğini habersiz.

Aşağıdaki örnekleri karşılaştırın:

def normalFunction():
    return
    if False:
        pass

def yielderFunction():
    return
    if False:
        yield 12

İkinci işlevi çağırdığımızda, birincisine çok farklı davranır. yielddeyimine erişilemeyebilir, ancak herhangi bir yerde varsa, uğraştığımızın niteliğini değiştirir.

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

yielderFunction() işlevini çağırmak kodunu çalıştırmaz, ancak bir jeneratörü kodun dışında tutar. (Belki bu tür şeyleri okunabilirlik için yielderöneki ile adlandırmak iyi bir fikirdir.)

>>> 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 ve gi_frame alanları donmuş durumun saklandığı yerdir. Onları dir(..) ile keşfederek, yukarıdaki zihinsel modelimizin güvenilir olduğunu onaylayabiliriz.

58
Evgeni Sergeev

Her cevabın önerdiği gibi, bir dizi üreteci oluşturmak için yield kullanılır. Dinamik olarak bir dizi oluşturmak için kullanılır. Örneğin, bir dosyayı bir ağ üzerinde satır satır okurken, yield işlevini aşağıdaki gibi kullanabilirsiniz:

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

Kodunuzda aşağıdaki gibi kullanabilirsiniz:

for line in getNextLines():
    doSomeThing(line)

Yürütme Kontrol Transferi gotcha

Uygulama kontrolü, verim çalıştırıldığında getNextLines () 'den for döngüsüne aktarılır. Böylece, getNextLines () her çağrıldığında, yürütme en son duraklatıldığı yerden başlar.

Böylece kısacası, aşağıdaki kodla bir işlev

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

for i in simpleYield():
    print i

basacak

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

(Aşağıdaki cevabım sadece, yığın istifleme ve yığın manipülasyon hilelerini içeren jeneratör mekanizmasının uygulanmasının altında değil değil, Python jeneratör kullanma perspektifinden bahseder.

python işlevinde yield yerine return kullanıldığında, bu işlev _generator function_ adlı özel bir şeye dönüştürülür. Bu işlev, generator tipinde bir nesne döndürür. yield anahtar sözcüğü, böyle bir işlevi özel olarak ele alması için python derleyicisini bildiren bir bayraktır. Normal işlevler bir kez sonlanır ondan bir miktar değer döndürülür. Ancak, derleyici yardımıyla, jeneratör işlevi yeniden başlatılabilir olarak düşünülebilir . Yani, yürütme içeriği geri yüklenecek ve yürütme son çalıştırmadan itibaren devam edecek. Açıkça geri dönüşü çağırıncaya kadar, bu bir StopIteration istisnasını (ayrıca yineleyici protokolünün bir parçasıdır) ya da işlevin sonuna ulaşır. generator hakkında birçok referans buldum, ancak bu one , _functional programming perspective_ 'dan elde edilen en sindirilebilirdir.

(Şimdi, kendi anlayışımı temel alan generator ve iterator'in ardındaki mantık hakkında konuşmak istiyorum. Umarım bu, yineleyici ve jeneratörün temel motivasyonu Bu tür kavramlar C # gibi diğer dillerde de ortaya çıkar.

Anladığım kadarıyla, bir grup veriyi işlemek istediğimizde, genellikle verileri bir yere depolar ve sonra birer birer işleriz. Ancak bu saf yaklaşım sorunludur. Veri hacmi çok büyükse, bunları önceden bir bütün olarak saklamak pahalıdır. Öyleyse, data in kendisini doğrudan saklamak yerine, neden bir tür metadata 'ı dolaylı olarak saklamadıklarını, yani _the logic how the data is computed_ .

Bu meta verileri sarmak için 2 yaklaşım vardır.

  1. OO yaklaşıyorsa, meta verileri _as a class_ olarak sardık. Bu, yineleyici protokolünü uygulayan iterator olarak adlandırılır (yani, __next__() ve __iter__() yöntemleri). Bu aynı zamanda sık görülen yineleyici tasarım deseni .
  2. İşlevsel yaklaşım, meta verileri _as a function_ kaydırıyoruz. Bu sözde _generator function_. Ancak kaputun altında, döndürülen _generator object_ hala _IS-A_ yineleyici, çünkü yineleyici protokolünü de uygular.

Her iki durumda da, bir yineleyici oluşturulur, yani istediğiniz verileri size verebilecek bir nesne. OO yaklaşımı biraz karmaşık olabilir. Neyse, hangisini kullanman sana kalmış.

46
smwikipedia

Verim bir nesnedir

Bir işlevdeki return, tek bir değer döndürür.

Eğer bir fonksiyonun çok büyük bir değer kümesi döndürmesini istiyorsanız , yield öğesini kullanın.

Daha da önemlisi, yield bir bariyer dir.

cUDA dilindeki engel gibi, tamamlanana kadar kontrolü transfer etmeyecektir.

Yani, fonksiyonunuzdaki kodu baştan başlayarak yield isabet edene kadar çalıştıracaktır. Ardından, döngünün ilk değerini döndürür.

Ardından, diğer tüm çağrılar, fonksiyonda yazdığınız döngüyü bir kez daha çalıştırır ve döndürülen herhangi bir değer bulunana kadar bir sonraki değeri döndürür.

43
Kaleem Ullah

Özetle, yield deyimi, işlevinizi, orijinal işlevinizin etrafında saran generator adlı özel bir nesne üreten bir fabrikaya dönüştürür. generator yinelendiğinde, işlevinizi bir sonraki yield değerine ulaşana kadar yürütür, ardından yürütmeyi askıya alır ve yield öğesine iletilen değeri değerlendirir. Yürütme yolu işlevden çıkana kadar bu işlemi her yinelemede tekrar eder. Örneğin,

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

for i in simple_generator():
    print i

basitçe çıktılar

one
two
three

Güç, bir sekansı hesaplayan bir döngü ile jeneratörü kullanmaktan gelir, jeneratör, hesaplamanın bir sonraki sonucunu 'vermek' için her seferinde durma döngüsünü çalıştırır, bu şekilde anında bir liste hesaplar, faydası hafızadır. özellikle büyük hesaplamalar için kaydedildi

Yinelenebilir sayı aralığı üreten kendi range işlevinizi oluşturmak istediğinizi varsayalım.

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

ve bu şekilde kullanın;

for i in myRangeNaive(10):
    print i

Ama bu verimsiz çünkü

  • Yalnızca bir kez kullandığınız bir dizi oluşturursunuz (bu, belleği boşa harcar)
  • Bu kod aslında o dizinin üzerine iki kez dönüyor! :(

Neyse ki Guido ve ekibi jeneratörler geliştirebilecek kadar cömertlerdi;

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

for i in myRangeSmart(10):
    print i

Şimdi her yinelemede, next() adı verilen bir işlev üzerindeki işlev, içinde durduğu ve değeri 'veren' bir 'verim' deyimine ulaşana veya işlevin sonuna ulaşana kadar işlevi gerçekleştirir. Bu durumda, ilk çağrıda next(), verim ifadesine ve 'n' verimine kadar çalıştırır, bir sonraki çağrıda, artış ifadesini yürütür, 'while' a geri döner, değerlendirir ve doğruysa durur ve tekrar 'n' verir, süre koşulu yanlış dönene ve jeneratör fonksiyonun sonuna atlayana kadar bu şekilde devam edecektir.

42
redbandit

Birçok kişi return yerine yield kullanır, ancak bazı durumlarda yield ile çalışmak daha verimli ve daha kolay olabilir.

İşte yield kesinlikle için en iyi örnek:

return (işlevde)

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

verimi (işlevinde)

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.

Arama işlevleri

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)

Her iki işlev de aynı şeyi yapar, ancak yield, beş yerine üç satır kullanır ve endişelenecek bir değişkeni yoktur.

Bu, kodun sonucudur:

 Output

Gördüğünüz gibi her iki fonksiyon da aynı şeyi yapıyor. Tek fark return_dates() bir liste verir ve yield_dates() bir jeneratör verir.

Gerçek hayattan bir örnek, bir dosyayı satır satır okumak veya yalnızca bir jeneratör yapmak gibi bir şey olabilir.

40
Tom Fuller

yield, bir işlev için bir dönüş elemanı gibidir. Fark, yield öğesinin bir işlevi jeneratöre dönüştürmesidir. Bir jeneratör 'bir şey elde edilinceye kadar' bir fonksiyon gibi davranır. Jeneratör bir daha aranana kadar durur ve tam olarak aynı noktadan devam eder. Tüm 'verilmiş' değerlerin bir sıralamasını list(generator()) çağırarak elde edebilirsiniz.

35
Theoremiser

yield anahtar sözcüğü, yalnızca dönen sonuçları toplar. yield öğesini return += gibi düşünün

35
Bahtiyar Özdere

İşte fibonacci serisini hesaplamak için basit bir yield tabanlı yaklaşım:

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

Bunu REPL içine girip ardından onu aramayı denediğinizde, gizemli bir sonuç elde edersiniz:

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

Bunun nedeni, yield öğesinin Python'a bir generator , yani talep üzerine değer üreten bir nesne oluşturmak istediğinizi göstermesidir.

Peki, bu değerleri nasıl üretiyorsunuz? Bu doğrudan yerleşik işlev next kullanılarak veya dolaylı olarak değerleri tüketen bir yapıya besleyerek yapılabilir. 

Dahili next() işlevini kullanarak, jeneratörü bir değer üretmeye zorlayarak, .next/__next__ işlevini doğrudan çağırırsınız:

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

Dolaylı olarak, fib döngüsüne for __, list başlatıcı, Tuple başlatıcıya veya değer üreten/üreten bir nesne bekleyen başka herhangi bir şey sağlarsanız, üreteçleri tarafından üretilinceye kadar "tüketeceksiniz" ve geri döner):

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

Benzer şekilde, Tuple başlatıcı ile: 

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

Bir jeneratör, tembel olduğu anlamındaki bir fonksiyondan farklıdır. Bunu, yerel durumunu koruyarak ve ihtiyaç duyduğunuzda sürdürmenize izin vererek gerçekleştirir. 

İlk olarak fib öğesini çağırarak çağırdığınızda:

f = fib()

Python işlevi derler, yield anahtar sözcüğüyle karşılaşır ve yalnızca bir nesneyi geri döndürür. Çok yardımcı değil görünüyor. 

Daha sonra talep ettiğinizde, doğrudan veya dolaylı olarak ilk değeri ürettiğinde, bulduğu tüm ifadeleri, bir yield ile karşılaşıncaya kadar çalıştırır, daha sonra yield için verdiğiniz değeri geri verir ve duraklatır. Bunu daha iyi gösteren bir örnek için, bazı print çağrıları kullanalım (Python 2'de print "text" ile değiştirin):

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.")

Şimdi REPL'e giriniz:

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

şimdi bir değer üretmesi için bir komut bekleyen bir jeneratör nesnesine sahipsiniz. next öğesini kullanın ve neyin basıldığını görün:

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

Belirtilmemiş sonuçlar, basılan sonuçtur. Alıntılanan sonuç yield öğesinden döndürülen sonuçtur. Şimdi next arayın:

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

Jeneratör yield value konumunda duraklatıldığını hatırlar ve oradan devam eder. Bir sonraki mesaj yazdırılır ve duraklatılacak yield ifadesinin aranması tekrar gerçekleştirilir (while döngüsü nedeniyle).

32

Kolayca açıklanabileceklerine basit bir örnek: yield

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


for i in f123():
    print i

Çıktı: 

1 2 1 2 1 2 1 2
29
Gavriel Cohen

Bir başka TL; DR

Listedeki yineleyici : next() listenin bir sonraki elemanını döndürür

Iterator generator : next() anında bir sonraki öğeyi hesaplar (kodu çalıştır)

Verimi/üreteci, kontrol akışını dışarıdan elle çalıştırmanın bir yolu olarak görebilirsiniz (bir döngü devam ediyor gibi), next öğesini çağırarak akışını karmaşık hale getirin.

Not : Jeneratör normal bir fonksiyondurDEĞIL'dir. Yerel değişkenler (yığın) gibi önceki durumu hatırlar. Ayrıntılı açıklama için diğer cevaplara veya makalelere bakın. Jeneratör sadece bir kez tekrarlanabilir . yield'siz yapabilirsin, ama bu kadar hoş olmazdı, bu yüzden 'çok hoş' bir dil şekeri sayılabilir.

28

verim geri dönüşe benzer. Fark: 

verim bir işlevi yinelenebilir yapar (aşağıdaki örnekte primes(n = 1) işlevi yinelenebilir hale gelir).
Temel olarak anlamı, fonksiyonun bir sonraki çağrışında, bıraktığı yerden devam edecek (yield expression satırından sonra).

def isprime(n):
    if n == 1:
        return False
    for x in range(2, n):
        if n % x == 0:
            return False
    else:
        return True

def primes(n = 1):
   while(True):
       if isprime(n): yield n
       n += 1 

for n in primes():
    if n > 100: break
    print(n)

Yukarıdaki örnekte isprime(n) işlevi doğruysa, asal sayıyı döndürür. Bir sonraki yinelemede, bir sonraki satırdan devam edecek 

n += 1  
24
blueray

Buradaki cevapların hepsi harika; ancak bunlardan sadece biri (en çok oylanan) kodunuzun nasıl çalıştığı ile ilgilidir. Diğerleri genel olarak jeneratörlerle ve nasıl çalıştıklarıyla ilgilidir.

Böylece, jeneratörlerin ne olduğunu veya verimlerin ne yaptığını tekrarlamayacağım; Bunların mevcut harika cevaplarla kapsandığını düşünüyorum. Ancak, birkaç saatini sizinkilere benzer bir kodu anlamaya çalışırken harcadıktan sonra, nasıl çalıştığını çözeceğim.

Kodunuz ikili ağaç yapısını geçiyor. Örneğin bu ağacı alalım:

    5
   / \
  3   6
 / \   \
1   4   8

Ve bir ikili arama ağacı geçişinin daha basit bir uygulaması:

class Node(object):
..
def __iter__(self):
    if self.has_left_child():
        for child in self.left:
            yield child

    yield self.val

    if self.has_right_child():
        for child in self.right:
            yield child

Yürütme kodu, şu şekilde __iter__ öğesini uygulayan Treenesnesindedir:

def __iter__(self):

    class EmptyIter():
        def next(self):
            raise StopIteration

    if self.root:
        return self.root.__iter__()
    return EmptyIter()

while candidates ifadesi for element in tree ile değiştirilebilir; Python bunu şu dile çevir:

it = iter(TreeObj)  # returns iter(self.root) which calls self.root.__iter__()
for element in it: 
    .. process element .. 

Node.__iter__ işlevi bir jeneratör olduğundan, yineleme başına içindeki kodu çalıştırılır. Yani infaz şöyle görünür:

  1. kök eleman ilk; childs'ın bırakılıp bırakılmadığını kontrol edin ve foronları yineleyin (ilk yineleyici nesnesi olduğu için onu söyleyelim1)
  2. bir çocuğu olduğundan forçalıştırılır. for child in self.left, bir Node nesnesinin kendisi olan (it2) self.left'dan bir yeni yineleyici oluşturur.
  3. 2 ile aynı mantık ve yeni bir iteratoroluşturulur (it3)
  4. Şimdi ağacın sol ucuna ulaştık. it3 adlı kullanıcının sola ihtiyacı yok ve devam ediyor ve yield self.value
  5. next(it3) işlevine yapılan bir sonraki çağrıda StopIterationişlevini yükseltir ve doğru childs olmadığından var olur (hiçbir şey vermeden işlevin sonuna ulaşır)
  6. it1 ve it2 hala etkindir - yorulmazlar ve next(it2) işlevini çağırmak değerleri yükseltir, StopIterationyükseltir
  7. Şimdi it2 bağlamına geri döndük ve durduğu yerden devam eden next(it2) işlevini çağırıyoruz: yield child ifadesinden hemen sonra. Artık solcu çocuğu olmadığı için devam eder ve self.val değerini verir.

Buradaki yakalayıcı, ağacı dolaşmak için her yinelemenin alt yineleyici oluşturur ve geçerli yineleyici durumunu elinde tutmasıdır. Sona ulaştığında yığını geriye doğru hareket ettirir ve değerler doğru sırayla döndürülür (en küçük verim değeri önce).

Kod örneğiniz farklı bir teknikte benzer bir şey yaptı: her çocuk için bir bir öğe listesi doldurdu, sonra bir sonraki yinelemede onu çıkar ve geçerli nesnede işlev kodunu çalıştırır (dolayısıyla selfname__) .

Umarım bu efsanevi konuya biraz katkıda bulunmuştur. Bu süreci anlamak için birkaç güzel saat geçirdim.

10
Chen A.

Kısacası, verim kullanımı, anahtar kelimeye benzer return , bunun dışında bir jeneratör döndürür.
A jeneratör nesnesi yalnızca bir kez geçer .

verimin iki faydası vardır:

  1. Bu değerleri iki kez okumanıza gerek yoktur;
  2. Birçok alt düğümü, hepsini belleğe koymadan alabilirsiniz.
8
123

Bir benzetme, buradaki fikri kavramaya yardımcı olabilir:

Günde binlerce ve binlerce ampul üretebilen inanılmaz bir makine yarattığınızı hayal edin. Makine bu ampulleri benzersiz seri numarasına sahip kutularda üretir. Tüm bu ampulleri aynı anda saklamak için yeterli alana sahip değilsiniz (yani, depolama sınırlaması nedeniyle makinenin hızına ayak uyduramazsınız), dolayısıyla bu makineyi talep üzerine ampul üretecek şekilde ayarlamak istersiniz.

Python jeneratörleri bu konseptten pek de farklı değil.

Kutular için benzersiz seri numaraları üreten, x işlevine sahip olduğunuzu hayal edin. Açıkçası, işlev tarafından üretilen çok sayıda bu tür barkodlara sahip olabilirsiniz. Akıllı ve verimli bir seçenek, isteğe bağlı olarak bu seri numaralarını üretmektir.

Makinenin kodu:

def barcode_generator():
    serial_number = 10000  # Initial barcode
    while True:
        yield serial_number
        serial_number += 1


barcode = barcode_generator()
while True:
    number_of_lightbulbs_to_generate = int(input("How many lightbulbs to generate? "))
    barcodes = [next(barcode) for _ in range(number_of_lightbulbs_to_generate)]
    print(barcodes)

    # function_to_create_the_next_batch_of_lightbulbs(barcodes)

    produce_more = input("Produce more? [Y/n]: ")
    if produce_more == "n":
        break

Gördüğünüz gibi her seferinde bir sonraki benzersiz seri numarasını üretmek için kendi kendine yeten bir "fonksiyon" var. Bu fonksiyon bir jeneratör geri döner! Gördüğünüz gibi, her seferinde yeni bir seri numarasına ihtiyacımız yok, işlevi çağırmıyoruz, ancak bir sonraki seri numarasını elde etmek için jeneratöre verilen next() işlevini kullanıyoruz.

Çıktı:

How many lightbulbs to generate? 5
[10000, 10001, 10002, 10003, 10004]
Produce more? [Y/n]: y
How many lightbulbs to generate? 6
[10005, 10006, 10007, 10008, 10009, 10010]
Produce more? [Y/n]: y
How many lightbulbs to generate? 7
[10011, 10012, 10013, 10014, 10015, 10016, 10017]
Produce more? [Y/n]: n
8
Rafael

Python'da generators (özel bir iterators türü), bir dizi değer üretmek için kullanılır ve yield anahtar kelimesi, jeneratör işlevlerinin return anahtar sözcüğü gibidir. 

Diğer etkileyici şey yield anahtar kelimesi, bir jeneratör işlevinin state değerini kaydetmektir

Böylece, number öğesinin her veriminde bir generator değerini farklı bir değere ayarlayabiliriz. 

İşte bir örnek:

def getPrimes(number):
    while True:
        if isPrime(number):
            number = yield number     # a miracle occurs here
        number += 1

def printSuccessivePrimes(iterations, base=10):
primeGenerator = getPrimes(base)
primeGenerator.send(None)
for power in range(iterations):
    print(primeGenerator.send(base ** power))
7
ARGeo

Yol ver

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

Kısacası , döngünün durmadığını ve nesne veya değişken gönderildikten sonra bile çalışmaya devam ettiğini görebilir (döngünün yürütmeden sonra durduğu return öğesinin aksine).

6
Gavriel Cohen

yield Python'da kullanılabilecek bir Jeneratör türüdür.

i̇şte Verimin gerçekte ne yaptığını görmek için bir link, Ayrıca nesilde. Jeneratörler ve Verim Anahtar Kelime - Python Merkezi (PC)

Ayrıca yieldreturn gibi çalışır, ancak return öğesinden farklı bir şekilde. yield daha fazlasını açıklayan bir bağlantı bile olsa, diğerini o kadar iyi anlamıyorsanız. Verim becerinizi geliştirin - jeffknupp

En basit ifadeyle, “verim”, bir değere “dön” e benzer, ancak Jeneratör üzerinde çalışır.

1
user3701435

Basit verimle, değerler yerine jeneratör nesnesini döndürür. 

Basit bir örnek aşağıda yardımcı olacaktır!

def sim_generator():
    for i in range(3):
        yield(i)

obj = sim_generator()
next(obj) # another way is obj.__next__()
next(obj)
next(obj)

yukarıdaki kod 0, 1, 2 döndürür

hatta kısa

for val in sim_generator():
    print(val)

0, 1, 2 döndür

Bu yardımcı olur umarım

1
Vivek Ananthan

Basit bir jeneratör fonksiyonu

def my_gen():
    n = 1
    print('This is printed first')
    # Generator function contains yield statements
    yield n

    n += 1
    print('This is printed second')
    yield n

    n += 1
    print('This is printed at last')
    yield n

im ifadesi, tüm durumlarını kurtaran işlevi duraklatır ve daha sonra arka arkaya çağrılar sırasında oradan devam eder.

https://www.programiz.com/python-programming/generator

0
Savai Maheshwari

yield bir şey verir. Biri 5 bardak kek yapmanı ister gibi. En az bir fincan kek işiniz bittiğinde, diğer kekleri yaparken yemek yemesini onlara verebilirsiniz.

In [4]: def make_cake(numbers):
   ...:     for i in range(numbers):
   ...:         yield 'Cake {}'.format(i)
   ...:

In [5]: factory = make_cake(5)

Burada factory, size kek yapan jeneratörü denir. make_function işlevini çağırırsanız, bu işlevi çalıştırmak yerine bir jeneratör alırsınız. Çünkü bir değişkende yield anahtar kelimesi mevcutsa, bir jeneratör olur.

In [7]: next(factory)
Out[7]: 'Cake 0'

In [8]: next(factory)
Out[8]: 'Cake 1'

In [9]: next(factory)
Out[9]: 'Cake 2'

In [10]: next(factory)
Out[10]: 'Cake 3'

In [11]: next(factory)
Out[11]: 'Cake 4'

Bütün pastaları tükettiler, ama yine bir tane istediler.

In [12]: next(factory)
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-12-0f5c45da9774> in <module>
----> 1 next(factory)

StopIteration:

ve daha fazla sormayı bırakmaları söyleniyor. Bu yüzden bir jeneratörü tükettikten sonra bununla işiniz biter. Daha fazla kek istiyorsanız tekrar make_cake arayın. Fincan kek için başka bir sipariş vermek gibidir.

In [13]: factory = make_cake(3)

In [14]: for cake in factory:
    ...:     print(cake)
    ...:
Cake 0
Cake 1
Cake 2

Yukarıdaki gibi bir jeneratör ile döngü için de kullanabilirsiniz.

Bir örnek daha: Haydi, istediğinizde rastgele bir şifre istediğinizi söyleyelim.

In [22]: import random

In [23]: import string

In [24]: def random_password_generator():
    ...:     while True:
    ...:         yield ''.join([random.choice(string.ascii_letters) for _ in range(8)])
    ...:

In [25]: rpg = random_password_generator()

In [26]: for i in range(3):
    ...:     print(next(rpg))
    ...:
FXpUBhhH
DdUDHoHn
dvtebEqG

In [27]: next(rpg)
Out[27]: 'mJbYRMNo'

Burada rpg, sonsuz sayıda rasgele şifre üretebilen bir jeneratördür. Dolayısıyla, jeneratörlerin sınırlı sayıda öğeye sahip olan listenin aksine dizilimin uzunluğunu bilmediğimizde de faydalı olduğunu söyleyebiliriz.

0
thavan