Jose: Merhaba arkadaşlar tekrar hoş
geldiniz ve bu videoda Python'da jeneratörler hakkında konuşuyoruz.
Bu bölüm için gerçekten
heyecanlıyım çünkü biraz daha gelişmiş şeylerden
bahsedeceğiz ve jeneratörler Python'da
birçok kursun bahsetmediği konulardan
biri ama performans için çok önemli ve ayrıca Python'da e-senkron
geliştirme, daha sonra öğreneceğimiz e-senkron geliştirme bu jeneratörler üzerine
inşa edilmiştir.
Jeneratörleri anladığınızda
Python'daki pek çok şey çok daha netleşmeye başlar.
Peki jeneratör nedir?
Python'da bir üreteç bir fonksiyondur.
Bu özel bir işlevdir.
Yürütmeler arasında içinde bulunduğu durumu
hatırlayan bir fonksiyona
bakın; böylece fonksiyonu birden çok kez çalıştırabilirsiniz
ve son çalıştırdığınızda ne yaptığını
hatırlayacaktır.
Bu kulağa oldukça tuhaf geliyor, bir örnekle açıklayayım.
Çok basit bir şekilde 100 rakamdan oluşan bir
liste oluşturmak istediğinizi
düşünün, 100'ü içermeyen sıfırdan 99'a kadar rakamlardan oluşan yeni bir liste
elde etmek istiyorsunuz.
Yani bunun gibi bir fonksiyon oluşturabilirsiniz.
Açıkçası menzil ve benzeri şeyleri kullanmıyorsunuz, menzili biliyorsunuz.
Aralık kullanmayalım, bunun yerine sıfırdan bir liste oluşturalım.
Sen olsan nasıl yapardın?
Bunu yapmanın iyi bir yolu boş bir listeyle başlamaktır.
Diyelim ki buna nums adını verelim ve boş bir listeye eşit yapalım.
Ayrıca sıfıra eşit bir indeksle başlayın.
Sonra i 100'den küçükken
nums dot append i deyin.
Yani başlangıca sıfır ekleyeceksiniz.
Sonra i'yi bir artıracaksınız, bu da
onu bir yapacak.
Bir ekleyeceksiniz, tekrarlayacaksınız, iki ekleyeceksiniz,
sonra üç ekleyeceksiniz ve 99'a ulaşana kadar böyle devam edeceksiniz.
Ve bu fonksiyonun sonunda sayı
listeniz olan nums değerini döndürün.
Eğer yüz sayı basarsak ve bunu çalıştırırsak.
Burada yüz rakamdan oluşan
listemizi içerdiğini görebiliriz.
Elbette bunun için liste kavramayı kullanabiliriz.
Range fonksiyonunu kullanabiliriz,
ancak şimdilik bunun havalı bir yol
olduğunu varsayalım, çünkü aslında açıkladığım
şey aslında range fonksiyonunun içeride nasıl çalıştığıdır.
Bu yüzden bir liste oluşturuyoruz,
ilk yüz sayıyla dolduruyoruz ve sonra geri dönüyoruz.
Şimdi bir listede yüz numaramız var.
Bu listenin tamamı bilgisayarınızın belleğinde işlevin bu
dönüş değeri olarak saklanır.
Şimdi yüz sayı çok büyük değil,
oldukça küçük bir alan,
ancak diyelim ki 10 milyon saklamak istedik, milyon
yüz sayı gibidir, ancak bir milyon
sayı gibi bir şey olurdu.
Bir milyon sayıdan oluşan bir liste oluşturur
ve bilgisayarınızın belleğine
giden bu değişkende depolarsınız ve muhtemelen
aldığı bellek miktarı daha büyüktür, çünkü bu
sayıların her biri bilgisayarınızın
belleğinin küçük bir miktarını kaplar.
Eğer 10 milyon sayı istiyorsanız, o zaman daha büyüktür.
Şimdi 10 milyon sayıya sahip olmak harika bir örnek değil,
ancak çok uzun bir listeniz olduğunu hayal edin.
Örneğin, bunlar genellikle asal sayıların
hesaplanmasında kullanılır ve büyük verilerde
ve bunun gibi şeylerde bazen büyük
listeler isteyebilirsiniz.
Dolayısıyla, bu sorunu aşmak için bir jeneratör
kullanılır çünkü varsayım 100 sayıya birden ihtiyacınız
olmadığıdır.
İhtiyacınız olan şey sıfırdır, böylece onunla bir şeyler yapabilirsiniz.
Bir tane de onunla bir şeyler yapabilmeniz için.
İki tane de onunla bir şeyler yapabilmeniz için.
Örneğin, bu yüz sayıyla gidip hepsini
ikiyle çarpabilir ve başka bir listede saklayabiliriz.
Bunu nasıl yapacaksın?
Yüzlük sayılarda x için daha az anlama,
x çarpı iki gibi bir şey yaparsınız.
Burada yaptığınız şey, bu listeyi izole
etmek ve her sayıyı ikiyle çarpmak için kullanmaktır.
Bu iki ile çarpma işleminin, bir şeyi
hesaplamak için bu sayılarda yapmak
istediğimiz bir işlem olduğunu düşünelim.
Bir başka örnek de, çok sayıda kullanıcınız
varsa ve bu kullanıcıların sisteminizle ne kadar etkileşim
içinde olduklarını hesaplamak istiyorsanız,
bu gibi bir şey olabilir.
Esasen her bir öğenin üzerinden geçmek ve
bir işlem gerçekleştirmek istiyorsunuz.
Buradaki mesele, tüm unsurlara aynı anda ihtiyacınız olmamasıdır.
Onlara teker teker ihtiyacınız var.
İşte jeneratörler bunun için kullanılır.
Eleman listesinin tamamına sahip olmak yerine,
yaptığınız şey, listenin tamamını
bellekte saklamadan üretecin
size listenin ilk elemanını vermesidir.
Üreteci bir sonraki çağırışınızda,
size en son verdiği öğeyi hatırlar ve size ikinci
öğeyi vereceğini bilir.
Sonra tekrar çalıştırırsınız ve size üçüncü elementi verir
ve bu böyle devam eder.
Listeyi asla hafızasında tutmaz, her
zaman sadece size verdiği son numarayı hatırlar, böylece
size bir sonraki numarayı verebilir.
Yeni bir sayı istediğiniz her
seferinde fonksiyonu eğlendirmeniz gerekir
ve bu yüzden ona üreteç denir, çünkü sayılar,
dizeler veya kullanıcılar ya da üretmek istediğiniz her neyse onu üretir.
Bunu "nums" yerine şu şekilde yaparsınız. append(i)", "yield i" diyorsunuz,
çünkü i döndürmek istediğimiz sayıdır, artık bu nums
eşittir köşeli parantezine
ihtiyacımız yok ve artık return segmentine
de ihtiyacımız yok.
Şimdi buradaki fonksiyona jeneratör fonksiyonu deniyor.
Yield, return'e çok benzer, ancak onu çağırdığınızda
size i verir ve sonra burada olduğunu hatırlar, beşinci
satırdan hemen önce durur.
Dördüncü
satırdan hemen sonra, ortada.
Fonksiyonu bir sonraki çağırışınızda, devam edecektir,
yani yapacağı şey i'yi bir artıracak,
döngüyü yeniden çalıştıracak ve bir verecek, size
bir verecek ve sonra duracaktır.
Bir dahaki sefere çağırdığınızda,
artmaya devam edecek bir tane alacağım
ve döngüyü tekrarlayacak ve sonra size iki tane verecek ve sonra duracak ve bu böyle devam edecek.
Sonunda sayılar tükendiğinde,
hiçbir sonuç vermeyecektir.
Ve sonra bittiğini anlarsınız.
Peki, bunu nasıl yapacağız?
Öncelikle yüz sayının çıktısını alalım.
Artık bize nasıl bir liste vermediğine dikkat
edin, şimdi bize yüz sayı olarak adlandırılan
bir üreteç nesnesi veriyor, dolayısıyla
fonksiyonumuzun adı.
Ayrıca bize nesnemizin bellek adresini de verir.
Nasıl bir fonksiyon tanımladığımıza dikkat
edin, ancak print bize bunun bir nesne olduğunu söylüyor,
çünkü Python arka planda yield
kullandığınızı, bir jeneratör oluşturduğunuzu
fark ediyor ve bunları bir python nesnesine dönüştürüyor.
Bu nesneleri bir fonksiyon olmadan
nasıl yapacağınızı daha sonra anlatacağım.
O halde jeneratörümüzü bir değişkende saklayalım.
Buna "g" diyeceğiz, bu küçük bir programdaki
jeneratör için oldukça popüler bir isim.
Çok sayıda jeneratörünüz varsa elbette
daha açıklayıcı bir isim seçmeniz gerekecektir.
Ardından, üretecin bize vereceği bir
sonraki değeri yazdıracağız.
Bir üreteci bu şekilde başlattığınızda, işlevi
çalıştırmadığına, hiç çalıştırmadan en baştan
başladığına dikkat edin.
Bir sonraki değeri sorduğunuzda, verime
kadar çalışacak ve size
verdiğiniz değeri verecektir.
Bir sonraki değeri sorma şekliniz, fonksiyonu
tekrar çalıştırmamanız, ancak "g" nin bir sonraki değerini söylemenizdir.
Sırada jeneratörleri etkileyen yerleşik bir işlev var
ve ona verime kadar gitmesini söylüyor.
Tamam, eğer bunu çalıştırırsak, şimdi sıfır elde ettiğimizi görebilirsiniz,
çünkü bu elde edilen ilk sayıdır.
Şimdi jeneratörümüz, "g", burada 4. satırda
durdu. 4 ile 5 arasında 5, yani
başka bir sayı istersek, elbette bir programın sonunda üretecin yok edildiğine
dikkat edin, bu nedenle bu programı
tekrar çalıştırırsak bu yine sıfır olacaktır
ve bu şimdi bir olacaktır.
Program yürütmeleri boyunca bunu hatırlamaz,
Python bu şekilde çalışmaz.
Şimdi ona ilk sayıyı ve ardından bir sonraki
sayıyı sorabiliriz.
İlk sayıdan sonra dört ile beş arasında
durdurulacaktır.
Sonraki dediğimizde bir sonraki verime kadar devam eder, bu da
bu döngüyü bir kez tekrarlamak anlamına gelir ve bize bir tane verir.
Tamam.
Çok önemli, fonksiyon nerede durduğunu hatırlar.
Yani herhangi bir noktada, fonksiyonun çalışmasını durdurabilir
ve ardından devam etmek için next'i çağırabilirsiniz.
Yani, sadece sayı üretmek için değil, bir fonksiyonun çalışmasını geçici
olarak durdurmak istediğinizde de kullanışlıdır
ve daha sonra istediğiniz zaman next'i
çağırarak çalıştırmaya devam
edebilirsiniz.
Bu belki şu anda çok anlamlı bir şey
değil, ancak senkronize bir programlama yaptığımızda
ihtiyaç duyacağımız zaman için bunu hatırlayın.
Peki ya menzil işlevi?
Daha önce bunun esasen aralık fonksiyonunun
içeride nasıl çalıştığını açıkladığını söylemiştim ve bu doğru, ancak aralık
fonksiyonu size doğrudan
bir üreteç vermez.
Burada, "aralık nesnem"
gibi bir şey yapalım, aralık
10 olacak.
Bu bir üreteç gibi davranır, ancak "my range object"
ifadesinin next'ini çağırırsanız bir
hata alırsınız ve şuna bakın.
Bunun nedeni, range nesnesinin bu şekilde davranmasına rağmen
tam olarak bir üreteç olmamasıdır,
bir liste oluşturmaz ve daha sonra üzerinde
yineleme yapmanıza izin verir, daha verimli olmak için bu üreteç gibi
davranır.
"Menzil nesnem", kursun
çok daha başlarında yazdırdığımızı
ve sıfırdan ona kadar garip
bir menzil aldığımızı hatırlayabilirsiniz.
Bu sadece "repr" fonksiyonunun çağrılmasıdır ve "repr" sonucunu
yazdırdığımızda görebileceğiniz gibi, sıfır ila
on aralığını da görürüz.
Yani "aralık nesnem" buna çok benzeyen bir şeydir,
birkaç video sonra bir aralık nesnesi
gibi davranmasını sağlamak için bir nesne üzerinde nasıl yineleme yapabileceğimizi
tam olarak öğreneceğiz.
Böylece, bu üreteçler hakkında bilgi sahibi olduk ve aslında üreteçler
hakkında bahsetmek istediğim bir şey
daha var: liste fonksiyonu bir üreteci bir listeye dönüştürebilir.
Böylece "g" listesini yazdırabiliriz.
Tam burada "sonraki", sonra tekrar "sonraki" ve sonra
"liste" dediğimiz şey.
Şimdi "next" fonksiyonlarımızdaki iki şey için sıfır ve bir çıktılarını
aldık ve daha sonra üretecin bir
listesini oluşturduk, ancak bu liste
elbette kaldığımız yerden devam ediyor.
Jeneratörün kendisi ne olduğunu hatırlar,
bu nedenle liste sıfırdan değil ikiden başlar.
Dolayısıyla, jeneratörlerde hatırlamak gerçekten önemlidir,
nerede olduklarını hatırlarlar.
Yani, bir tane oluşturduğunuzda, onu kullanmaya başladığınız andan itibaren, ona
"next" demeye başladığınız
andan itibaren, nerede olduğunu hatırlar
ve geriye gidemezsiniz. Tamam.
Şimdi, önümüzdeki birkaç videoda jeneratörler
hakkında daha fazla şey öğreneceğiz.
Üreteç sınıflarını, bir üreteç
gibi davranan bir sınıfı nasıl oluşturabileceğinizi
ve ayrıca Python'daki yineleyiciler ve yinelenebilirleri
öğreneceğiz ve aralık hedefleri böyle
bir örnektir.
Daha sonra filter, map,
any and all ve enumerate gibi bazı
fonksiyonel programlama örneklerine geçeceğiz.
Bu yüzden, bu bölüm için oldukça
heyecanlıyım, jeneratörlerin Python geliştirmenizde,
özellikle de kursta ilerledikçe ihtiyaç duyacağınız
bir şey olacağından eminim.
Bu yüzden, bir sonraki videoda görüşmek üzere.