Bir üreteç anlayışı tam olarak nasıl çalışır?


98

Jeneratör anlayışı ne işe yarar? O nasıl çalışır? Bununla ilgili bir eğitim bulamadım.


2
Açık olmak gerekirse, bunların dil adı oluşturucu ifadelerdir , üreteç anlayışları değil .
ShadowRanger

2
@ShadowRanger Temmuz 2018'de Python-dev posta listesinde , tutarlılık adına onları "oluşturucu anlayışları" olarak adlandırmak için geçici ancak oldukça oybirliğiyle kabul edilen "Adlandırmayı anlama sözdizimi" üzerine tartışma var.
Aaron Hall

Yanıtlar:


151

Liste anlayışlarını anlıyor musunuz? Eğer öyleyse, bir üreteç ifadesi bir liste anlama gibidir, ancak ilgilendiğiniz tüm öğeleri bulup bunları listelemek yerine bekler ve her öğeyi tek tek ifadeden çıkarır.

>>> my_list = [1, 3, 5, 9, 2, 6]
>>> filtered_list = [item for item in my_list if item > 3]
>>> print(filtered_list)
[5, 9, 6]
>>> len(filtered_list)
3
>>> # compare to generator expression
... 
>>> filtered_gen = (item for item in my_list if item > 3)
>>> print(filtered_gen)  # notice it's a generator object
<generator object <genexpr> at 0x7f2ad75f89e0>
>>> len(filtered_gen) # So technically, it has no length
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'generator' has no len()
>>> # We extract each item out individually. We'll do it manually first.
... 
>>> next(filtered_gen)
5
>>> next(filtered_gen)
9
>>> next(filtered_gen)
6
>>> next(filtered_gen) # Should be all out of items and give an error
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> # Yup, the generator is spent. No values for you!
... 
>>> # Let's prove it gives the same results as our list comprehension
... 
>>> filtered_gen = (item for item in my_list if item > 3)
>>> gen_to_list = list(filtered_gen)
>>> print(gen_to_list)
[5, 9, 6]
>>> filtered_list == gen_to_list
True
>>> 

Bir üretici ifadesinin bir seferde yalnızca bir öğe vermesi gerektiğinden, bellek kullanımında büyük tasarruflara yol açabilir. Üreteç ifadeleri, her seferinde bir öğe almanız, o öğeye göre birçok hesaplama yapmanız ve ardından bir sonraki öğeye geçmeniz gereken senaryolarda en mantıklı olanıdır. Birden fazla değere ihtiyacınız varsa, bir üreteç ifadesi de kullanabilir ve her seferinde birkaçını yakalayabilirsiniz. Programınız ilerlemeden önce tüm değerlere ihtiyacınız varsa, bunun yerine bir liste anlama kullanın.


4
Burada bir soru var. Sonucu almak için next (gen_name) kullandım ve Python 3'te çalıştı. __Next __ () kullanmamız gereken belirli bir senaryo var mı?
Ankit Vashistha

4
@AnkitVashistha Hayır, Python 3 next(...)yerine her zaman kullanın .__next__().
Todd Sewell

2
@gotgenes @AnkitVashistha If you need more than one value, you can also use a generator expression and grab a few at a time. Bu kullanım hakkında bir örnek verebilir misiniz? Teşekkürler.
LittleZero

21

Bir üreteç anlayışı, bir liste anlayışının tembel versiyonudur.

Liste yerine bir yineleyici, yani sonraki öğeyi verecek next () yöntemine sahip bir nesne döndürmesi dışında, tıpkı bir liste anlama gibidir.

Liste anlamalarına aşina değilseniz buraya bakın ve oluşturucular için buraya bakın .


4

Liste / oluşturucu kavrama, mevcut bir listeden yeni bir liste / oluşturucu oluşturmak için kullanabileceğiniz bir yapıdır.

Diyelim ki 1'den 10'a kadar her sayının karelerinin listesini oluşturmak istiyorsunuz. Bunu Python'da yapabilirsiniz:

>>> [x**2 for x in range(1,11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

burada range(1,11)listeyi oluşturur [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ancak rangeişlev Python 3.0'dan önce bir üretici değildir ve bu nedenle kullandığım yapı bir liste anlamadır.

Aynı şeyi yapan bir jeneratör yaratmak isteseydim, şu şekilde yapabilirdim:

>>> (x**2 for x in xrange(1,11))
<generator object at 0x7f0a79273488>

Ancak Python 3'te rangebir oluşturucu olduğundan, sonuç yalnızca kullandığınız söz dizimine bağlıdır (köşeli parantezler veya yuvarlak parantezler).


4
Bu yanlış. Dış ifadenin bir oluşturucu olup olmadığının iç ifadenin olup olmadığı ile hiçbir ilgisi yoktur. Açıkçası, bir üreteç ifadesinin bir listeden öğe alan pek bir anlamı olmasa da, bunu yapabilirsiniz.
Antimon

Bu daha açık bir şekilde yeniden yazılabilir mi? Ne dediğini anlıyorum ama Antimony'nin dediği gibi, sanki başka bir şey söylüyormuşsun gibi görünüyor. (ve söylediğiniz gibi görünen şey yanlış)
Lyndon White

4

Jeneratör anlayışı, belirli bir yapıya sahip jeneratörler oluşturmanın kolay bir yoludur. Diyelim generatorki, içindeki tüm çift sayıları birer birer veren bir your_list. İşlev stilini kullanarak oluşturursanız, şu şekilde olur:

def allEvens( L ):
    for number in L:
        if number % 2 is 0:
            yield number

evens = allEvens( yourList )

Bu oluşturucu anlama ifadesi ile aynı sonucu elde edebilirsiniz:

evens = ( number for number in your_list if number % 2 == 0 )

Her iki durumda da, aradığınızda bir next(evens)sonraki çift numarayı alırsınız your_list.


1

Jeneratör anlayışı, bir kaynak üzerinde hareket eden bir imleç gibi bir şey olan yinelenebilir değerler yaratmak için bir yaklaşımdır. Eğer mysql imlecini veya mongodb imlecini biliyorsanız, tüm gerçek verilerin belleğe bir kerede değil, birer birer yüklendiğinin farkında olabilirsiniz. İmleciniz ileri geri hareket eder, ancak bellekte her zaman bir satır / liste öğesi vardır.

Kısacası, üreteç anlayışını kullanarak python'da kolayca imleçler oluşturabilirsiniz.


0

Jeneratör anlayışının başka bir örneği:

print 'Generator comprehensions'

def sq_num(n):
    for num in (x**2 for x in range(n)):    
        yield num

for x in sq_num(10):
    print x 

0

Oluşturucular yalnızca listelerle aynıdır, küçük fark, listelerde listenin tüm gerekli sayılarını veya öğelerini birlerde almamız, ancak jeneratörlerde gerekli sayıların birer birer verilmesidir. Dolayısıyla, gerekli öğeleri elde etmek için gerekli tüm öğeleri almak için for döngüsünü kullanmalıyız.

#to get all the even numbers in given range
 
def allevens(n):
    for x in range(2,n):
        if x%2==0:
            yield x

for x in allevens(10)
print(x)

#output
2
4
6
8
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.