Python verimi, Jeneratörler ve Jeneratör İfadeleri

Bu eğitimde, Python oluşturucuları kullanarak nasıl kolayca yineleme oluşturacağınızı, yineleyicilerden ve normal işlevlerden nasıl farklı olduğunu ve neden kullanmanız gerektiğini öğreneceksiniz.

Video: Python Jeneratörleri

Python'daki Jeneratörler

Python'da bir yineleyici oluşturmak için çok iş var. __iter__()Ve __next__()yöntemiyle bir sınıf uygulamalıyız , iç durumları takip etmeliyiz ve StopIterationdöndürülecek değer olmadığında yükseltmeliyiz .

Bu hem uzun hem de mantığa aykırıdır. Jeneratör böyle durumlarda kurtarmaya gelir.

Python üreteçleri, yineleyiciler oluşturmanın basit bir yoludur. Yukarıda bahsettiğimiz tüm işler Python'da otomatik olarak jeneratörler tarafından yapılır.

Basitçe söylemek gerekirse, bir üretici, üzerinde yineleyebileceğimiz (her seferinde bir değer) bir nesneyi (yineleyici) döndüren bir işlevdir.

Python'da Jeneratörler Oluşturun

Python'da bir jeneratör oluşturmak oldukça basittir. Normal bir işlevi tanımlamak kadar kolaydır, ancak bir yieldifade yerine bir returnifadeyle.

Bir işlev en az bir yieldifade içeriyorsa (başka yieldveya returnifadeler içerebilir ), bir üretici işlev haline gelir. Her ikisi de yieldve returnbir işlevden bir değer döndürür.

Aradaki fark, bir returnifade bir işlevi tamamen sonlandırırken, yieldifadenin tüm durumlarını kaydederek işlevi durdurması ve daha sonra ardışık çağrılarda oradan devam etmesidir.

Jeneratör işlevi ile Normal işlev arasındaki farklar

Bir jeneratör işlevinin normal bir işlevden farkı şu şekildedir.

  • Üretici işlevi bir veya daha fazla yieldifade içerir .
  • Çağrıldığında, bir nesne (yineleyici) döndürür, ancak yürütmeyi hemen başlatmaz.
  • Gibi yöntemler __iter__()ve __next__()otomatik olarak uygulanır. Böylece kullanarak öğeleri yineleyebiliriz next().
  • Fonksiyon verildikten sonra, fonksiyon duraklatılır ve kontrol, arayana aktarılır.
  • Yerel değişkenler ve durumları birbirini takip eden aramalar arasında hatırlanır.
  • Son olarak, işlev sona erdiğinde, StopIterationsonraki aramalarda otomatik olarak yükseltilir.

İşte yukarıda belirtilen tüm noktaları açıklayan bir örnek. my_gen()Birkaç yieldifadeyle adlandırılmış bir jeneratör işlevimiz var .

 # A simple generator function 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

Yorumlayıcıda etkileşimli bir çalışma aşağıda verilmiştir. Çıktıyı görmek için bunları Python kabuğunda çalıştırın.

 >>> # It returns an object but does not start execution immediately. >>> a = my_gen() >>> # We can iterate through the items using next(). >>> next(a) This is printed first 1 >>> # Once the function yields, the function is paused and the control is transferred to the caller. >>> # Local variables and theirs states are remembered between successive calls. >>> next(a) This is printed second 2 >>> next(a) This is printed at last 3 >>> # Finally, when the function terminates, StopIteration is raised automatically on further calls. >>> next(a) Traceback (most recent call last):… StopIteration >>> next(a) Traceback (most recent call last):… StopIteration

Yukarıdaki örnekte dikkat edilmesi gereken ilginç bir şey, n değişkeninin değerinin her çağrı arasında hatırlanmasıdır.

Normal işlevlerden farklı olarak, yerel değişkenler, işlev ortaya çıktığında yok edilmez. Ayrıca, jeneratör nesnesi yalnızca bir kez yinelenebilir.

İşlemi yeniden başlatmak için, benzeri bir şey kullanarak başka bir jeneratör nesnesi oluşturmamız gerekiyor a = my_gen().

Unutulmaması gereken son bir şey, jeneratörleri doğrudan for döngüleri ile kullanabileceğimizdir.

Bunun nedeni, bir fordöngünün bir yineleyici alması ve next()işlev kullanarak üzerinde yineleme yapmasıdır . StopIterationYükseltildiğinde otomatik olarak sona erer . Bir for döngüsünün Python'da gerçekte nasıl uygulandığını öğrenmek için buraya bakın.

 # A simple generator function 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 # Using for loop for item in my_gen(): print(item)

Programı çalıştırdığınızda, çıktı:

 Bu önce yazdırılır 1 Bu, ikinci olarak yazdırılır 2 Bu, en son 3'te yazdırılır

Döngülü Python Jeneratörleri

Yukarıdaki örnek daha az kullanışlıdır ve sadece arka planda ne olduğu hakkında bir fikir edinmek için onu inceledik.

Normalde, jeneratör fonksiyonları, uygun bir sonlandırma koşulu olan bir döngü ile gerçekleştirilir.

Bir dizgiyi ters çeviren bir jeneratör örneğini ele alalım.

 def rev_str(my_str): length = len(my_str) for i in range(length - 1, -1, -1): yield my_str(i) # For loop to reverse the string for char in rev_str("hello"): print(char)

Çıktı

 Olleh

Bu örnekte, range()for döngüsünü kullanarak indeksi ters sırada almak için işlevi kullandık.

Not : Bu oluşturucu işlevi yalnızca dizelerle değil, aynı zamanda liste, tuple vb. Gibi diğer yinelenen türlerle de çalışır.

Python Üreteci İfadesi

Basit jeneratörler, jeneratör ifadeleri kullanılarak anında kolayca oluşturulabilir. Bina jeneratörlerini kolaylaştırır.

Anonim işlevler oluşturan lambda işlevlerine benzer şekilde, üretici ifadeleri anonim üreteç işlevleri oluşturur.

Oluşturucu ifadesi sözdizimi, Python'daki bir liste anlama ile benzerdir. Ancak köşeli parantezler yuvarlak parantezlerle değiştirilir.

Liste anlama ile üretici ifadesi arasındaki en büyük fark, liste anlamanın tüm listeyi üretirken, üretici ifadesi her seferinde bir öğe üretmesidir.

They have lazy execution ( producing items only when asked for ). For this reason, a generator expression is much more memory efficient than an equivalent list comprehension.

 # Initialize the list my_list = (1, 3, 6, 10) # square each term using list comprehension list_ = (x**2 for x in my_list) # same thing can be done using a generator expression # generator expressions are surrounded by parenthesis () generator = (x**2 for x in my_list) print(list_) print(generator)

Output

 (1, 9, 36, 100) 

We can see above that the generator expression did not produce the required result immediately. Instead, it returned a generator object, which produces items only on demand.

Here is how we can start getting items from the generator:

 # Initialize the list my_list = (1, 3, 6, 10) a = (x**2 for x in my_list) print(next(a)) print(next(a)) print(next(a)) print(next(a)) next(a)

When we run the above program, we get the following output:

 1 9 36 100 Traceback (most recent call last): File "", line 15, in StopIteration

Generator expressions can be used as function arguments. When used in such a way, the round parentheses can be dropped.

 >>> sum(x**2 for x in my_list) 146 >>> max(x**2 for x in my_list) 100

Use of Python Generators

There are several reasons that make generators a powerful implementation.

1. Easy to Implement

Generators can be implemented in a clear and concise way as compared to their iterator class counterpart. Following is an example to implement a sequence of power of 2 using an iterator class.

 class PowTwo: def __init__(self, max=0): self.n = 0 self.max = max def __iter__(self): return self def __next__(self): if self.n> self.max: raise StopIteration result = 2 ** self.n self.n += 1 return result

The above program was lengthy and confusing. Now, let's do the same using a generator function.

 def PowTwoGen(max=0): n = 0 while n < max: yield 2 ** n n += 1

Since generators keep track of details automatically, the implementation was concise and much cleaner.

2. Memory Efficient

A normal function to return a sequence will create the entire sequence in memory before returning the result. This is an overkill, if the number of items in the sequence is very large.

Generator implementation of such sequences is memory friendly and is preferred since it only produces one item at a time.

3. Represent Infinite Stream

Üreteçler, sonsuz bir veri akışını temsil etmek için mükemmel ortamlardır. Sonsuz akışlar bellekte depolanamaz ve üreticiler bir seferde yalnızca bir öğe ürettikleri için sonsuz bir veri akışını temsil edebilirler.

Aşağıdaki üretici işlevi tüm çift sayıları üretebilir (en azından teoride).

 def all_even(): n = 0 while True: yield n n += 2

4. Boru Hattı Oluşturucuları

Bir dizi işlemi boru hattı haline getirmek için birden fazla jeneratör kullanılabilir. Bu, en iyi bir örnek kullanılarak açıklanır.

Fibonacci serisindeki sayıları üreten bir jeneratörümüz olduğunu varsayalım. Ve sayıların karesini almak için başka bir jeneratörümüz var.

Fibonacci serisindeki sayıların karelerinin toplamını bulmak istersek, aşağıdaki şekilde jeneratör fonksiyonlarının çıktılarını birlikte boru hattı oluşturarak yapabiliriz.

 def fibonacci_numbers(nums): x, y = 0, 1 for _ in range(nums): x, y = y, x+y yield x def square(nums): for num in nums: yield num**2 print(sum(square(fibonacci_numbers(10))))

Çıktı

 4895

Bu boru hattı verimli ve okunması kolay (ve evet, çok daha havalı!).

Ilginç makaleler...