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 StopIteration
dö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 yield
ifade yerine bir return
ifadeyle.
Bir işlev en az bir yield
ifade içeriyorsa (başka yield
veya return
ifadeler içerebilir ), bir üretici işlev haline gelir. Her ikisi de yield
ve return
bir işlevden bir değer döndürür.
Aradaki fark, bir return
ifade bir işlevi tamamen sonlandırırken, yield
ifadenin 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
yield
ifade 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 yineleyebiliriznext()
. - 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,
StopIteration
sonraki aramalarda otomatik olarak yükseltilir.
İşte yukarıda belirtilen tüm noktaları açıklayan bir örnek. my_gen()
Birkaç yield
ifadeyle 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 for
döngünün bir yineleyici alması ve next()
işlev kullanarak üzerinde yineleme yapmasıdır . StopIteration
Yü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ı!).