Çok teşekkür ederim , bu kadar zahmetli bir iş olacağını düşünmemiştim. Bu kodları parçalara ayırarak okumak kolay fakat bir araya geldiklerinde zorlanabiliyor insan. Kodları kabaca bi’ açıklayabilirseniz minnettar kalırım.
Tabi açıklayayım:
def range_(min_ : float, max_: float, step_: float)
Burada range_
isimli bir fonksiyon tanımlandı. Bu fonksiyon 3 tane parametre alıyor. Bu parametrelerin veri tiplerinin float
olması gerekiyor. Daha doğrusu sadece step_
'in veri tipi float
olsa, diğerlerinin veri tipi int
olsa da olur. Ama belki 0.5 ile 9.3 arasındaki sayıları 0.35 artış miktarı ile aramak istersiniz diye bu parametrelerin de tipi float olarak belirlendi.
result = []
min_
ile max_
arasındaki sayıları result
isimli bir listede tutacağız.
decimal = len(str(step_).split(".")[-1])
Burada virgülden sonra kaç basamak var onu hesaplıyoruz. Mesela step_
sayısı 0.1
ise, str(0.1)
, '0.1'
yapar. Bu string dosyasını split(".")
fonksiyonu ile .
işaretinden ayırıyoruz. Ayırdığımız veri iki elemanlı bir liste haline geliyor. Bu listenin son elemanı "1"
. Bu stringin uzunluğunu ölçüyoruz, bu örnek için o da bir 1 çıkıyor. Yani noktadan sonra kaç basamak var onu hesaplıyoruz ve decimal
isimli bir değişkene kaydediyoruz.
while round(min_, decimal) < round(max_, decimal):
Burada yuvarlanmış min_
değeri, yuvarlanmış max_
değerinden küçük olduğu sürece çalışan bir döngü var. Yuvarlama step_
değerinin virgülden sonra kaç basamaklı olduğuna göre yapılır.
result.append(round(min_, decimal))
result
listesine yuvarlanmış min_
sayısını ekleriz.
min_ += step_
min_
sayısını, arttırma miktarı olan step_
kadar arttırız.
return result
Ve son olarak result
listesini geri döndeririz.
Ben de böyle bir şeyler yazdım.
def ondalik_dizi(bas, son, aralik):
bas *= 10
son *= 10
gecici = list(range(bas, son, aralik))
kalici = []
for i in gecici:
kalici.append(i/10)
print(kalici)
ondalik_dizi(0,1,1)
[0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
Yalnız ondalik_dizi
fonksiyon sadece integer değerler ile çalışıyor. Ve ondalıkta sadece tek basamak gidebiliyor.
Evet haklısınız. Maksat yatmadan önce küçük bir şeyler yazayım dedim.
Elinize sağlık.
def frange(start, stop, step):
while start < stop:
yield start
start += step
@aib, Biraz araştırma yaptım, isim alanında yield
keyword u kullanılan fonksiyonlar generator
olarak geçiyormuş ve generator
fonksiyonlarda return
kullanılamazmış. Yalnız yield
keyword unun tam olarak ne işe yaradığını anlayamadım. Basitçe açıklar mısınız?
Siz yazınca ben de merak ettim. yukarıdaki adreste bir tür nesne olduğu yazıyor. Tahminimce range()
fonksiyonunun verdiği gibi bir aralık nesnesi geri döndürüyor. Aşağıdaki bağlantıda range()
fonksiyonunu bir aralık nesnesi olduğu yazıyor.
https://belgeler.yazbel.com/python-istihza/gomulu_fonksiyonlar.html#range
Kısaca anladığım yield
kullanıldığında bir aralık nesnesi dönüyor. Nasıl range()
fonksiyonunu bir listeye atayıp değerlerini yazdırabiliyorsak yield
ifadesinin kullanıldığı kullanıcı tanımlı fonksiyonunu da liste gibi bir veri türüne atayıp içeriğini yazdırabiliriz. Tabi print(*dongu_kurulabilir_veri)
şeklinde de olabilir.
Belki de gömülü bir fonksiyon olan range()
fonksiyonunun da içerisinde yield
yazıyordur
Teşekkürler, bu akşam da bir şeyler kattık kendimize.
def parcala(bas, son, adim, duyarlilik=1):
liste = []
if bas < son:
while bas < son:
liste.append(round(bas, duyarlilik))
bas += adim
elif bas > son:
while bas > son:
liste.append(round(bas, duyarlilik))
bas -= adim
else: print("Aralığın ilk ve son değerleri farklı olmalıdır..!")
return liste
In [45]: parcala(1, 3, 0.1, 2)
Out[45]:
[1,
1.1,
1.2,
1.3,
1.4,
1.5,
1.6,
1.7,
1.8,
1.9,
2.0,
2.1,
2.2,
2.3,
2.4,
2.5,
2.6,
2.7,
2.8,
2.9]
Burada round fonksiyonu olmadan yazdığımda çıktıda virgülden sonra çok fazla hane görünüyor. Round ile virgülden sonraki haneyi duyarlılık değişkeni ile sınırlamaya çalıştığımda tam olarak istediğim çıktıyı alamadım fakat böyle daha iyi sonuç aldım.
round()
fonksiyonu ve duyarlilik
değişkeni olmadığında çıktı şöyle oluyor;
In [46]: parcala(1, 3, 0.1)
Out[46]:
[1,
1.1,
1.2000000000000002,
1.3000000000000003,
1.4000000000000004,
1.5000000000000004,
1.6000000000000005,
1.7000000000000006,
1.8000000000000007,
1.9000000000000008,
2.000000000000001,
2.100000000000001,
2.200000000000001,
2.300000000000001,
2.4000000000000012,
2.5000000000000013,
2.6000000000000014,
2.7000000000000015,
2.8000000000000016,
2.9000000000000017]
Iterable dedigimiz, birden fazla deger iceren (for...in
loop’una girebilen) degerleri donduren fonksiyonlari coroutine olarak yazmamizi saglayan bir keyword. Bu coroutine’ler dogalari geregi calismaya ara verip devam edebiliyorlar, bu sayede sonsuz veya pahali listeleri parca parca olusturmak mumkun oluyor:
def foo():
i = 0
while True:
yield i
i += 1
Ayni zamanda cagri hic bir zaman sonlanmamis gibi oldugu icin yerel degiskenler (i
) stack frame gibi bir yerde otomatik olarak saklaniyorlar; state bilgisi kaydetmek/yuklemek icin ekstra kod yazmaya gerek kalmiyor.
Generator’larda return
kullaniliyor; (return
ile veya kodu bitmek suretiyle) return eden generator’lar sonlanmis sayiliyor.
O zaman ciktiyi duzeltmek lazim. Kayan noktali sayilarin dogasi bu. Duz print
de gostermek icin kotu bir yontem
Peki hocam coroutine ifadeler sonlanmamış gibi olduğu için içerisindeki yerel değişkenler saklandığından dolayı bellek kullanımı hususunda sorun yaşatabilme potansiyeli oluyor mu? (farklı büyük projeler için belleğin aşırı kullanımı gibi)
Siz ne gibi durumlarda return yerine yield’i tercih ediyorsunuz. İç değişkenin saklanıyor olması avantajından dolayı mı? yield ifadesini tercih etmem gereken durumları kavramaya çalışıyorum
Soruyu @aib’e sormuşsunuz ama izninizle sorunuza cevap vereyim. Yazdıklarımda bir eksiklik veya bir yanlışlık varsa düzeltilir.
yield
ile return
arasındaki farkı şöyle anlatmaya çalışayım:
a = [i for i in range(1000000)]
Bu yukarıdaki ifade enter tuşuna basılır basılmaz hafızada yer kaplamaya başlayacak. İsterseniz, sistem tepsisini açın ve bu ifadeyi yazıp enter tuşuna basmadan önceki cpu ve hafıza kullanım oranıyla, enter tuşuna bastıktan sonraki cpu ve hafıza kullanım oranını karşılaştırın.
İfadeyi şöyle değiştirelim:
a = (i for i in range(1000000))
Bir de bu ifadeyi yazıp enter tuşuna basın ve hafıza kullanım oranına bir daha bakın.
Yukarıdaki kodlardan ilki, ikincisinden daha fazla kaynak tüketecektir. Aynı durum return
ve yield
deyimleri için de geçerli. Mesela:
def f():
return [i for i in range(1000000)]
yukarıdaki paylaştığım ilk kodlara benzer.
def g():
yield from range(1000000)
Bu ifade ise yukarıda paylaşılan ikinci kodlara benzer.
return
ifadesini şu şekilde kullansaydık, yield
deyimi içeren fonksiyon ile aynı oranda kaynak kullanacaktı.
def f():
return (i for i in range(1000000))
Bu fonksiyon ile yukarıda yield
deyim kullanılan fonksiyon arasında hiç bir fark yok.
# fonksiyonu çağıralım.
call = f()
# Burada bir generator `call` isimli bir değişkene kaydedildi.
print(next(call))
# Bu yukarıdaki kod bize üreteçin ilk değerini verecektir.
print(next(call))
# Bu yukarıdaki kod ise üreteçin ikinici değerini verecektir.
# next() fonksiyonunu üretecin içindeki eleman sayısı kadar kullanabiliriz.
Aşağıdaki örneği bir çalıştırın isterseniz:
def f():
yield from range(5)
yield from "hello"
yield 2
a = f()
print(next(a))
print(next(a))
print(next(a))
print(next(a))
print(next(a))
print(next(a))
print(next(a))
Bu f()
fonksiyonundaki yield
deyimi, range(5)
deki her bir elemanı, "hello"
stringindeki her bir elemanı ve 2
sayısını bir üreteç içine dahil eder. Dolayısıyla yield
yinelenebilir bir veri setindeki i
verisini sırası geldiğinde kullanır. return
ifadesinde ise veri setinin tamamı kullanılır ve bu da hafızanın daha çok kullanılması anlamına geliyor.
Yinelenebilir verinin büyük olduğu durumlarda yield
deyimini kullanmak return
deyimini kullanmaya göre daha avantajlıdır. Ancak yukarıdaki örneklerden birinde şöyle bir ifade vardı:
def f():
return (i for i in range(1000000))
return
deyimini yukarıdaki gibi kullanırsanız, return
ifadesini yield
gibi kullanmış olursunuz. Yani return
edilen şey bir üreteçtir. Ve bu üreteç ne kadar büyük bir sayı dizisini içeriyor olursa olsun, sayı dizisi hafızada yer tutmaz.
Izninle ben de cevapliyorum.
E zaten saklaman gerekmiyor muydu? Coroutine kullanmasan saklanmayacak miydi o data? O zaman niye coroutine kullandin? Yerel degiskenler her zaman saklaniyor.
Bellek kullanimi ne kadar kisitli ki bir coroutine’in yerel degiskenlerini dusunuyorsun? Coroutine olmayan fonksiyonlarin yerel degiskenleri icin benzer bir dusunceye gittin mi?
Ne kadar buyuklukte? O buyuklukte proje yazana kadar mevcut hardware’in bellegi artmayacak mi? RAM fiyatlari yariya dusmeyecek mi?
Buyuk bir projede manasiz asiri bir bellek kullanimi varsa o projedeki daha deneyimli biri uyarir zaten, duruma gore dogru tavsiyeyi de verir. Ben simdi RAM butcesini bilmeden, programcilarin yogunlugunu bilmeden bir sey diyemiyorum.
Hafiza kullanimi veya calisma hizi gibi gereksiz parametreleri optimize etmeye calismayin.
Hangisi daha kolay, anlasilir olacaksa.
yield
’i kullanmayi ogren. Sonra kullanabilecegin yerleri gormeyi ogren. Oralarda kullan. Bazen kullanma. Tercih etmen gereken durumlari zaten anlarsin.
Teşekkürler, bu vesile ile yield’i de aklımızın bir köşesine attık. Bilgi eksikliğimiz yüzünden sorular bazen saçma oluyorsa kusura bakmayın.
Hiç saçma olmuyor, lütfen sormaktan çekinmeyin. Hepimiz öğrenciyiz, sorular çok öğretici olabiliyor.
Aynen.
Teknik dogruluga, dogru dusunce sekillerine yoneltici cumleler kurmaya odaklanmaktan uslubu kaciriyorum cogu zaman. Her soruya tek tek -alinti yaparak- cevap vermemin yeterli bir saygi gostergesi oldugu yanilgisina dusuyorum. Yukaridaki gibi hiyar, ama her soruyu mumkun oldugunca dogru yoneltmeyle cevaplamaya calisan yazilar cikiyor ortaya. Beni de boyle sevin
bir şey sorabilirmiyimm neden yield in yanında from ifadesi var aklıma takıldı biraz anlatırmısınız