10. Fonksiyonlar¶
Bir önceki bölümün tamamlanmasıyla birlikte Python’da “Temel Bilgiler” olarak adlandırabileceğimiz konuları bitirmiş olduk. Böylelikle artık Python programlama dilini “temel düzeyde” bildiğimizi rahatlıkla iddia edebiliriz...
Bu bölümden itibaren Python’un derinliklerine doğru yolculuğumuza başlayacağız. Bu yolculuktaki ilk durağımız “Python’da Fonksiyonlar” olacak... Peki nedir bu “fonksiyon” denen şey?
En basit tanımıyla fonksiyon, birbiriyle ilişkili deyimleri, kod parçalarını bir araya toplamamızı sağlayan bir “kod bloğu”dur. Python’daki fonksiyonlar bize işlerimizi otomatikleştirme imkanı sağlar. Fonksiyonların bu otomatikleştirme işini nasıl yerine getirdiğini bu bölümde inceleyeceğiz. Bu bölümü bitirip fonksiyonlar konusunu öğrendiğimizde, Python’da ileriye doğru çok önemli ve büyük bir adım atmış, deyim yerindeyse “level atlamış” olacağız...
Aslında fonksiyon bizim yabancısı olduğumuz bir kavram değil. Şimdiye kadar Python’da pek çok fonksiyon gördük ve kullandık. Örneğin, print() komutunun bir fonksiyon olduğunu ta en başta söylemiştik... Aynı şekilde, daha önceki derslerimizde gördüğümüz ve işlerimizi bir hayli kolaylaştırmış olan range(), len(), round(), str(), int(), list(), dict(), tuple() vb. de birer fonksiyondur. İşte şimdi biz de bu bölümde buna benzer fonksiyonları nasıl oluşturabileceğimizi öğreneceğiz... O halde isterseniz lafı daha fazla uzatmadan işe koyulalım...
10.1. Fonksiyon Tanımlamak¶
Giriş kısmında söylediğimiz gibi, fonksiyonlar işlerimizi otomatikleştirmemizi sağlar. Hatırlarsanız, bundan önceki derslerimizin birinde sum() adlı bir fonksiyondan söz etmiştik. Bu fonksiyon, mesela bir liste içindeki bütün sayıların toplamını veriyordu bize. Bu fonksiyonu şöyle kullanıyorduk:
>>> a = [2, 3, 4]
>>> sum(a)
9
Peki bu fonksiyon nasıl oluyor da işlerimizi otomatikleştirmemizi sağlıyor?
Bir an için Python’da fonksiyon diye bir şeyin olmadığını varsayalım. Yukarıdaki gibi, bir liste içindeki bütün sayıları toplamak istediğimizde şöyle bir şey yazmamız gerekecekti:
>>> lst = [2, 3, 4]
>>> a = 0
>>> for i in lst:
... a = a + i
... print(a)
Başka bir grup sayıyı toplamak istediğimizde ise bu kodları en baştan tekrar yazmak zorunda kalacaktık:
>>> lst2 = [10, 20, 56, 55]
>>> a2 = 0
>>> for v in lst2:
... a2 = a2 + v
... print(a2)
Eğer sum() fonksiyonu olmasaydı, program içinde ne zaman bir grup sayıyı toplamak istesek yukarıdaki kodları her defasında yeni baştan yazmanız gerekecekti... Ama Python’un bize sunduğu sum() fonksiyonu sayesinde bu işlemlerin hiçbirini yapmak zorunda kalmıyoruz. Programımız içinde bir grup sayıyı toplamak istediğimizde sadece sum() fonksiyonunu çağırmamız yeterli oluyor. Yani bu fonksiyon bir anlamda işlerimizi otomatik bir hale getirmemizi sağlıyor...
sum() oldukça faydalı bir fonksiyondur. Bu fonksiyon sayesinde, bir kere yazılmış bir kod parçasını farklı yerlerde ve programlarda tekrar tekrar kullanabiliyoruz. Zaten fonksiyonların ana görevlerinden biri de, bir kere yazılmış kodların tekrar tekrar kullanılabilmesini sağlamaktır. Yabancılar buna “code reusability” adını veriyor...
sum() fonksiyonu, bir grup sayıyı kolay bir şekilde toplamamızı sağlayan bir araçtır. Ancak Python’da tıpkı bu sum() fonksiyonuna benzer şekilde bir liste içindeki bütün sayıları çarpan, doğrudan kullanabileceğimiz özel bir fonksiyon bulunmaz. Ama biz istersek fonksiyonlar yardımıyla kendimiz için böyle bir fonksiyon yazabiliriz. Şimdi bunu nasıl yapacağımıza bakalım. Hem böylece fonksiyonlar konusuna da hızlı bir giriş yapmış olalım.
Python’da bir fonksiyonu kullanabilmek için öncelikle o fonksiyonu tanımlamamız gerekir. Bu tanımlama işini def adlı bir parçacık yardımıyla yapıyoruz. Python’da bir fonksiyon şöyle tanımlanır (Bu kodları dosyaya kaydedelim):
def fonksiyon_adı():
Madem biz çarpma yapan bir fonksiyon yazacağız, o halde fonksiyonumuzu şöyle tanımlayabiliriz:
def çarp():
Burada def parçacığı fonksiyonumuzu tanımlamamızı sağlıyor. “çarp()” ise fonksiyonumuzun adı... Bu arada “çarp” kelimesinin sonuna “()” işaretini ve ardından da iki nokta üst üsteyi koymayı asla unutmuyoruz. Şimdi yapmamız gereken şey fonksiyonumuzun içeriğini yazmak olacak:
def çarp():
a = 1
for i in [2, 3, 4]:
a = a * i
print(a)
Burada girintilere özellikle dikkat ediyoruz... Bu kodlarda yaptığımız şey, “[2, 3, 4]” listesinin tek tek bütün öğelerini “1” sayısıyla çarpıp, sonucu a değişkenine atamaktan ibaret... Bu işlem nihai olarak listedeki bütün sayıların birbiriyle çarpılmasını sağlayacaktır.
Böylece fonksiyonumuzu tanımlamış olduk. Şu anda yapmamız gereken tek bir şey kaldı. O da yazdığımız fonksiyonumuzu çağırmak:
çarp()
Bunu da kodlarımız arasına ekleyelim:
def çarp():
a = 1
for i in [2, 3, 4]:
a = a * 1
print(a)
çarp()
Yine girintilere çok dikkat ediyoruz... Ayrıca tanımladığımız fonksiyonun sınırlarına da dikkat edin. Bir fonksiyon, def parçacığıyla başlar, kendisinden sonra gelen girintili kod bloğuyla devam eder. Bu kod bloğunun dışında kalan her şey fonksiyonun da dışındadır. Mesela burada yazdığımız “çarp()” satırı fonksiyonun bir parçası değildir. Tanımladığımız fonksiyon sadece koyu harflerle gösterilen kodlardan ibarettir. “çarp()” satırının görevi ise tanımladığımız fonksiyonu çağırmaktır. Bu demek oluyor ki, Python’da bir fonksiyonu kullanabilmek için önce o fonksiyonu tanımlamamız, ardından da bu fonksiyonu çağırmamız gerekiyor. Yukarıdaki örnekte, koyu harflerle gösterilen kısım fonksiyonun tanımlandığı kısımdır... Bundan sonra gelen “çarp()” ifadesi ise fonksiyonumuzu çağırdığımız kısım oluyor... Bu önemli bilgiyi de öğrendiğimize göre yolumuza devam edebiliriz...
Bu arada, bu bölümün en başında örnek olarak verdiğimiz toplama işlemi yapan kodları da bir fonksiyon haline getirelim isterseniz:
def topla():
a = 0
for i in [2, 3, 4]:
a = a + i
print(a)
Son olarak fonksiyonumuzu çağırıyoruz:
topla()
Bunu da kodlarımızın arasına yerleştirelim:
def topla():
a = 0
for i in [2, 3, 4]:
a = a + 1
print(a)
topla()
Yukarıda tanımladığımız çarp() ve topla() adlı fonksiyonları bir dosyaya kaydedip çalıştırdığımızda Python bize ilgili işlemlerin sonucunu verecektir. Yani çarp() fonksiyonu “2”, “3” ve “4” sayılarının çarpımını, topla() fonksiyonu ise “2”, “3” ve “4” sayılarının toplamını gösterecektir. Burada fonksiyonların işimizi nasıl da kolaylaştırdığına dikkat edin. Yukarıda tanımladığımız çarp() fonksiyonu sayesinde, bir grup sayıyı çarpmak istediğimiz zaman, bütün kodları her defasında yeni baştan yazmak yerine sadece çarp() fonksiyonunu çağırmamız yeterli olacaktır...
Yalnız dikkat ederseniz, burada yazdığımız fonksiyonlar kısıtlı. Bu fonksiyonlar sadece “2”, “3” ve “4” sayıları üzerinde işlem yapabiliyor. Bunun nedeni bizim bu üç sayıyı fonksiyon içinde doğrudan tanımlamış olmamız... Eğer fonksiyonumuzu daha esnek bir hale getirmek istersek, örneğin çarp() fonksiyonunu şu şekilde yazmamız gerekir:
def çarp(liste):
a = 1
for i in liste:
a = i * a
print(a)
Burada, bir önceki yazımdan farklı olarak herhangi bir sayı belirtmedik. Onun yerine “liste” adlı bir değişken oluşturup bunu fonksiyona parametre olarak verdik... Bu sayede fonksiyonumuz herhangi bir sayı listesinin içeriğini çarpabilecek. Bir deneme yapalım:
sayılar = [24, 56, 77, 87]
çarp(sayılar)
Yazdığımız kodlar tam olarak şöyle görünecek:
def çarp(liste):
a = 1
for i in liste:
a = i * a
print(a)
sayılar = [24, 56, 77, 87]
çarp(sayılar)
Bu kodları bir dosyaya kaydedip çalıştırdığımızda şu sonucu alırız:
9003456
Gördüğünüz gibi fonksiyonlar, yazdığımız kodları bir araya toplamamızı, gruplandırmamızı da sağlamanın yanısıra, yapacağımız işlemler için bir şablon oluşturma vazifesi de görüyor. Pek çok kez kullanmamız gereken işlevleri bir şablon haline getirerek, bu şablon üzerinden, benzer işlemleri tek bir komutla halledebiliyoruz. İlerleyen derslerimizde “modüller” konusunu öğrendiğimizde bu fonksiyonları çok daha verimli bir biçimde kullanabileceğiz.
Fonksiyonlar, yazdığımız programlarda işlerimizi bir hayli kolaylaştırır. Fonksiyonlar olmasaydı bir program içinde defalarca yazmak zorunda kalacağımız kodları bir kez fonksiyon olarak tanımladıktan sonra program içinde tekrar tekrar kullanabiliriz. Eğer kodlarda bir değişiklik yapmamız gerekirse sadece fonksiyon içinde değişiklik yapmamız yeterli olacaktır. Öbür türlü, program içinde oraya buraya dağılmış kod parçalarını tek tek bulup düzeltmemiz gerekecekti... Özellikle arayüz tasarlarken fonksiyonlardan bolca yararlanacağız...
Fonksiyonlarla ilgili çok basit bir örnek verelim. Diyelim ki bir program yazdık ve programın bir yerinde kullanıcıdan sayı girmesini istiyoruz. Bunun, programımızın hata vermesine yol açma potansiyeli taşıyan bir işlem olduğunu varsayalım. Programın çalışması sırasında bir hatayla karşılaşıldığında kullanıcıya genel bir hata mesajı göstermek istersek şöyle bir kod yazabiliriz:
try:
soru = input("gerekli sayıyı giriniz: ")
except:
print("Beklenmeyen bir hata oluştu.")
print("Bu hatayı, hata takip sistemine raporlayınız!..")
Yine diyelim ki “except” bloğu içinde yazdığımız genel hata mesajını programımızın başka yerlerinde de aynen kullanmak istiyoruz. Yani mesela şöyle bir şey yapmak istiyoruz:
try:
soru = int(input("gerekli sayıyı giriniz: "))
except:
print("Beklenmeyen bir hata oluştu.")
print("Bu hatayı, hata takip sistemine raporlayınız!..")
#Burada programımıza ait başka kodlar yer alıyor...
try:
soru2 = int(input("başka bir sayı giriniz: "))
except:
print("Beklenmeyen bir hata oluştu.")
print("Bu hatayı, hata takip sistemine raporlayınız!..")
Burada dikkat ederseniz, genel hata mesajını göstermek istediğimiz her yerde bu mesajları tekrar tekrar yazmamız gerekiyor. İşte fonksiyonlar böyle durumlarda işlerimizi kolaylaştırabilir... Yukarıdaki kodları fonksiyonlar yardımıyla şu şekilde sadeleştirebiliriz:
def genelHata():
print("Beklenmeyen bir hata oluştu.")
print("Bu hatayı, hata takip sistemine raporlayınız!..")
try:
soru = int(input("gerekli sayıyı giriniz: "))
except:
genelHata()
#Burada programımıza ait başka kodlar yer alıyor...
try:
soru2 = int(input("başka bir sayı giriniz: "))
except:
genelHata()
Bu verdiğimiz çok basit bir örnektir, ama bu basit örnekte bile, fonksiyonların işimizi ne kadar kolaylaştırabileceğini görebiliyoruz. Burada kullandığımız fonksiyon öncelikle bizi aynı şeyleri tekrar tekrar yazma zahmetinden kurtarıyor. genelHata() adlı fonksiyonu bir kez tanımladıktan sonra, gereken yerlerde sadece bu fonksiyonu çağırarak işimizi halledebiliyoruz. Ayrıca eğer kullanıcıya göstermek istediğimiz hata mesajında değişiklik yapmak istersek, sadece fonksiyon içinde gerekli değişiklikleri yapmamız yeterli olacaktır. Eğer burada fonksiyon kullanmasaydık, hata mesajlarını kodlar içinde tek tek bulup değiştirmemiz gerekecekti...
Bu bölümde gördüğümüz örneklerden de bildiğimiz gibi, yazdığımız fonksiyonlara parametre atayarak bunları daha da esnek bir hale getirebiliyoruz:
def genelHata(eposta):
print("Beklenmeyen bir hata oluştu.")
print("Bu hatayı, {0} adresine raporlayınız!".format(eposta))
try:
soru = int(input("gerekli sayıyı giriniz: "))
except:
genelHata("abc123@abc.com")
try:
soru2 = int(input("başka bir sayı giriniz: "))
except:
genelHata("123fds@cbc.com")
Bu programın çalışması esnasında eğer ilk “try... except...” bloğu içinde bir hata meydana gelirse kullanıcıya “Bu hatayı abc123@abc.com adresine raporlayınız!” mesajı; eğer hata ikinci “try... except...” bloğu içinde meydana gelirse de “Bu hatayı 123fds@cbc.com adresine raporlayınız!” mesajı gösterilecektir.
Bu arada dikkat ederseniz, genelHata() adlı fonksiyondaki “eposta” parametresi aslında bir “yer tutucu” olmaktan öte bir anlam ifade etmiyor. Yani oraya istediğiniz bir kelimeyi yazabilirsiniz. Amacımız oraya bir parametre geleceğini belirtmek. Tabii ki parametrelerimizi adlandırırken anlamlı isimler yazmamız her zaman için işimizi kolaylaştıracaktır... Bu “parametre” meselesini bir sonraki bölümde biraz daha ayrıntılı olacak inceleyeceğiz.
Bu bölümün son sözü olarak fonksiyonların adlandırılması konusunda birkaç şey söyleyelim... Fonksiyon adlarını küçük harflerle yazmak adettendir. Yani:
def fonk():
Burada görüldüğü gibi, fonksiyon adlarını yazarken küçük harfleri kullanıyoruz. Eğer yazacağımız fonksiyonun adı birden fazla kelimeden oluşuyorsa, fonksiyon adını oluşturan kelimeleri birbirinden “_” işareti ile ayırıyoruz. Yani:
def fonksiyon_adı():
Daha nadir olarak, birden fazla kelimeden oluşan fonksiyon adlarının şu şekilde yazıldığını da görebiliriz:
def fonksiyonAdı():
Son olarak, fonksiyon adlarının büyük harflerle başladığı pek görülmez...
10.2. Fonksiyonların Parametreleri¶
Bir önceki bölümde fonksiyonları parametrelerle birlikte kullanarak epey esnek sonuçlar elde edebileceğimizi görmüştük. Bu arada, bizim burada “parametre” olarak adlandırdığımız şey bazı yerlerde “argüman” olarak da karşınıza çıkabilir. Bu iki terim özünde birbirinden farklı olsa da, çoğunlukla fonksiyonlar için birbiriyle eş anlamlı olarak kullanılmaktadır... Yani fonksiyonlar söz konusu olduğunda “parametre” ve “argüman” aynı şeyi ifade eder. Peki “argüman” ile “parametre” arasındaki fark nedir?
Şu örnekte “x” bir parametredir:
def fonk(x):
Şu örnekte ise “50” bir argümandır:
fonk(50)
Yani bir fonksiyonun tanımlanması sırasında, parantez içinde belirtilen değişkenlere “parametre” adı verilirken, tanımlanan fonksiyonun çağrılması esnasında, bu değişkenlere verilen değerlere ise “argüman” adı veriliyor. Dolayısıyla yukarıdaki fonksiyon hakkında şöyle bir cümle kurabiliriz:
“fonk” adlı fonksiyonun x parametresine argüman olarak 50 değerini verdik...
Ancak “parametre” ile “argüman” arasındaki bu farklılık çoğu zaman gözardı edilir ve bu iki kavram çoğunlukla birbiriyle eş anlamlı olarak kullanılır.
Dediğimiz gibi, bir önceki bölümde parametreleri temel olarak nasıl kullanabileceğimizi öğrenmiştik. Bu bölümde ise “parametre” kavramına biraz daha ayrıntılı olarak bakmaya çalışacağız.
Önce şu çok basit fonksiyona bir bakalım:
def bas(kelime):
print("merhaba", kelime)
Burada bas() adlı bir fonksiyon tanımladık. Bu fonksiyon tek bir parametre alıyor. Fonksiyonumuzda bu parametreyi bir karakter dizisi içinde kullanıyoruz. Bu fonksiyonu çalıştırdığımızda, bas() fonksiyonuna argüman olarak verdiğimiz kelime “merhaba” karakter dizisi ile birlikte ekrana basılacaktır. Mesela bu fonksiyonu “istihza” argümanıyla çağıralım:
bas("istihza")
Kodlarımız şöyle görünecek:
def bas(kelime):
print("merhaba", kelime)
bas("istihza")
Bu kodları bir dosyaya kaydedip çalıştırdığımızda şöyle bir çıktı elde ederiz:
merhaba istihza
Gördüğünüz gibi, bas() fonksiyonuna verdiğimiz “istihza” argümanı “merhaba” karakter dizisi ile birlikte ekrana basılıyor. Kodlarımızı biraz geliştirelim:
def bas(kelime):
print("merhaba", kelime)
soru = input("adınız nedir? ")
bas(soru)
Bu kodlar, “merhaba” karakter dizisi ile birlikte ekrana dökülecek kelimeyi doğrudan kullanıcıdan alıyor... Bu arada, yazdığımız fonksiyonun nerede başlayıp nerede bittiğine dikkat edelim. “soru = input(....” satırı ve ondan sonraki kısım fonksiyonumuzun dışında yer alıyor.
Dediğimiz gibi, bas() fonksiyonu tek bir argüman alıyor. Dolayısıyla bu fonksiyonu argümansız olarak çağıramayız:
def bas(kelime):
print("merhaba", kelime)
soru = input("adınız nedir? ")
bas()
Eğer bas() fonksiyonunu bu şekilde argümansız olarak yazıp çalıştırırsak aşağıdakine benzer bir hata alırız:
TypeError: bas() takes exactly 1 positional argument (0 given)
Bu hata mesajı şöyle çevrilebilir: “TipHatası: bas() tamı tamına 1 adet sıralı argüman alır (0 verilmiş)”
Demek ki bas() fonksiyonunu çalıştırabilmek için “1” adet argüman girmemiz gerekiyormuş... Ne bir eksik, ne bir fazla... Bu arada hata mesajı içinde geçen “sıralı argüman” (positional argument) kavramından da biraz sonra bahsedeceğiz.
Fonksiyonlara istediğimiz sayıda parametre verebiliriz. Ancak parametrelerimizi “makul” sayıda tutmak, fonksiyonun hangi parametrelere ihtiyaç duyduğunu hatırlayabilmek açısından önemlidir:
def deneme(bir, iki, liste, ölçüt):
sonuç = bir * iki
if bir and iki != ölçüt:
liste.append(sonuç)
else:
print("{0} ile çarpmaya izin vermiyoruz!".format(ölçüt))
print(liste)
Gördüğünüz gibi, fonksiyonlara birden fazla parametre verebiliyoruz... Burada tanımladığımız fonksiyon, “bir”, “iki”, “liste” ve “ölçüt” olmak üzere dört adet parametre alıyor. Yani bu fonksiyonu kullanabilmek için fonksiyonumuzu dört adet argüman ile çağırmamız gerekiyor. Fonksiyonumuzun içeriğine baktığımız zaman ilk olarak şu satırı görüyoruz:
sonuç = bir * iki
Buna göre, deneme() adlı fonksiyondaki “bir” ve “iki” adlı parametreler birbiriyle çarpılıp “sonuç” adlı değişkeni oluşturacak. Ardından gelen satırda bir “if... else...” bloğu görüyoruz. Eğer “bir” ve “iki” parametrelerinin değeri “ölçüt” parametresinin değerine eşit değilse, bir önceki satırda belirlediğimiz “sonuç” adlı değişkenin değerini “liste” parametresine ekliyoruz. Burada and adlı bool değerinin anlamına dikkat edin. “sonuç” adlı değişkenin listeye eklenebilmesi için hem “bir” değerinin, hem de “iki” değerinin “ölçüt”e eş olmaması gerekiyor.
Daha sonra else bloğunu tanımlıyoruz. Buna göre eğer “bir” ve “iki” parametrelerinden herhangi birinin değeri “ölçüt”e eşitse, kullanıcıya “ölçüt ile çarpmaya izin vermiyoruz!” şeklinde bir uyarı gösteriyoruz. Son satırda ise “liste” adlı parametrenin değerini ekrana basıyoruz...
Şimdi sıra geldi fonksiyonumuzu çağırmaya:
öğeler = []
deneme(23, 5, öğeler, 4)
Kodlarımızı topluca görelim:
def deneme(bir, iki, liste, ölçüt):
sonuç = bir * iki
if bir and iki != ölçüt:
liste.append(sonuç)
else:
print("{0} ile çarpmaya izin vermiyoruz!".format(ölçüt))
print(liste)
öğeler = []
deneme(23, 5, öğeler, 4)
Burada öncelikle “öğeler” adlı boş bir liste oluşturduk. Çünkü deneme() fonksiyonunun parametrelerinden birisi liste olmak zorunda... Ardından da deneme() fonksiyonunu çağırıyoruz. Fonksiyonumuzu dört adet argüman ile çağırdığımıza dikkat edin. Eğer argüman sayısı dörtten az veya fazla olursa programımız hata verecektir.
Bu kodları çalıştırdığımızda ekrana, fonksiyon içinde belirttiğimiz listenin öğeleri basılacaktır. Fonksiyonumuzu bir de şu argümanlarla çağıralım:
def deneme(bir, iki, liste, ölçüt):
sonuç = bir * iki
if bir and iki != ölçüt:
liste.append(sonuç)
else:
print("{0} ile çarpmaya izin vermiyoruz!".format(ölçüt))
print(liste)
öğeler = []
deneme(23, 5, öğeler, 5)
Bu defa şöyle bir çıktı alıyoruz:
5 ile çarpmaya izin vermiyoruz!
[]
Bu çıktıyı almamızının sebebi “iki” adlı parametrenin değerinin (5), “ölçüt” parametresinin değeriyle aynı olması (5). Fonksiyonumuz içinde böyle bir durum için else bloğunu yazmış ve böyle bir duruma izin vermediğimiz konusunda kullanıcıyı uyarmıştık...
10.3. Varsayılan Değerli Argümanlar¶
Bu bölümde “Varsayılan Değerli Argümanlar” diye bir şeyden söz edeceğiz. Bakalım neymiş bu “Varsayılan Değerli Argümanlar”...
Önceki derslerimizden range() fonksiyonunu biliyorsunuz. range() fonksiyonunu anlatırken bu fonksiyonun bazı varsayılan değerleri olduğunu söylemiştik. Yani bu range() fonksiyonu aslında üç argüman almasına rağmen biz bunu tek bir argüman ile de kullanabiliyorduk...
range() fonksiyonu şu parametrelerden oluşuyordu:
range(başlangıç_değeri, bitiş_değeri, atlama_değeri)
range() fonksiyonunu kullanabilmek için bu parametreler içinde sadece “bitiş_değeri” adlı parametreye bir argüman vermemiz yeterli olacaktır:
list(range(10))
Öteki iki parametrenin birer varsayılan değeri olduğu için o parametreleri belirtmesek de olur. Yine range() fonksiyonunu anlattığımız bölümde o parametrelerin varsayılan değerlerinin şöyle olduğunu söylemiştik:
başlangıç_değeri = 0
atlama_değeri = 1
Dolayısıyla, biz range(10) yazdığımızda Python bunu “range(0, 10, 1)” şeklinde algılayacak ve ona göre işlem yapacaktır.
Peki varsayılan değerli argümanların bize ne gibi bir faydası var? Varsayılan değerli argümanları şuna benzetebiliriz: Diyelim ki bilgisayarınıza bir program kuruyorsunuz. Eğer bu programı ilk defa kuruyorsanız, kurulumla ilgili bütün seçeneklerin ne işe yaradığını bilmiyor olabilirsiniz... Dolayısıyla eğer program size kurulumun her aşamasında bir soru sorarsa her soruya ne cevap vereceğinizi kestiremeyebilirsiniz. O yüzden, makul bir program, kullanıcının en az seçimle programı kullanılabilir hale getirmesine müsaade etmelidir. Yani bir programın kurulumu esnasında bazı seçeneklere programı yazan kişi tarafından bazı mantıklı varsayılan değerler atanabilir. Örneğin kullanıcı programı kurarken herhangi bir kurulum dizini belirtmezse program otomatik olarak varsayılan bir dizine kurulabilir... Veya programla ilgili, kullanıcının işine yarayacak bir özellik varsayılan olarak açık gelebilir. İşte biz de yazdığımız programlarda kullanacağımız fonksiyonlara bu şekilde varsayılan değerler atarsak programımızı kullanacak kişilere kullanım kolaylığı sağlamış oluruz... Örneğin range() fonksiyonunu yazan Python geliştiricileri bu fonksiyona bazı varsayılan değerler atayarak bizim için bu fonksiyonun kullanımını kolaylaştırmışlardır. Bu sayede range() fonksiyonunu her defasında üç argüman vermek yerine tek argüman ile çalıştırabiliyoruz...
Biz de kendi yazdığımız fonksiyonlarda böyle varsayılan değerler belirleyebiliriz. Mesela şu örneğe bir bakalım:
def böl(bir, iki, kip=True):
sonuç = bir / iki
if kip == True:
print(sonuç)
if kip == False:
print(int(sonuç))
Bu fonksiyonu şu argümanlarla çağıralım:
böl(10, 2)
Kodlarımız tam olarak şöyle görünecek:
def böl(bir, iki, kip=True):
sonuç = bir / iki
if kip == True:
print(sonuç)
if kip == False:
print(int(sonuç))
böl(10, 2)
Bu programı çalıştırdığımızda şu sonucu alırız:
5.0
Gördüğünüz gibi, sonuç içinde ondalık kısım da görünüyor... Eğer tamsayı şeklinde bir sonuç elde etmek istersek fonksiyonumuzu şöyle çağıracağız:
böl(10, 2, False)
Bu şekilde şöyle bir sonuç alırız:
5
Fonksiyonumuza baktığımız zaman, böl() fonksiyonunun üç adet parametre aldığını görüyoruz. Ama sonuncu parametreye varsayılan bir değer atadığımız için, fonksiyonumuzu kullanırken sadece iki argüman vermemiz yeterli olacaktır. Eğer üçüncü parametreyi belirtmezsek, bu parametrenin değeri “True” varsayılacaktır...
Bu arada, yukarıdaki fonksiyonu şu şekilde tanımlayabileceğimizi de biliyoruz:
def böl(bir, iki, kip=True):
sonuç = bir / iki
if kip:
print(sonuç)
if not kip:
print(int(sonuç))
Burada “if kip:” ifadesi “if kip == True:” ile eşanlamlıdır. “f not kip:” ise “if kip == False” ile aynı anlama gelir...
Hatta istersek fonksiyonumuz için şöyle bir yazım tarzı dahi belirleyebiliriz:
def böl(bir, iki, kip=True):
if kip == True:
print(bir / iki)
if kip == False:
print(bir // iki)
“//” ve “/” işleçlerinin farkını daha önceki derslerimizde görmüştük. “//” işlecini kullanarak bölme işlemi yaptığımızda elde ettiğimiz sonuç bir tamsayı oluyor. Yani bölme işleminin sonucu ondalık kısmı içermeyecek şekilde veriliyor...
Varsayılan değerli fonksiyonlar tanımlarken dikkat etmemiz gereken önemli bir kural var. Bu tür fonksiyonlarda varsayılan değerin, parametre sıralamasında en sonda gelmesi gerekiyor. Yani şöyle bir şey yazamayız:
def böl(kip = True, bir, iki):
Eğer varsayılan değerli argümanı en sona değil de başa veya ortaya alırsak Python bize şöyle bir hata verir:
SyntaxError: non-default argument follows default argument
Yani:
SözdizimiHatası: varsayılan değersiz argüman, varsayılan değerli argümandan sonra geliyor
Dolayısıyla kural olarak, varsayılan değere sahip argümanları parametre listesinin en sonuna yerleştirmeye özen göstermemiz gerekiyor...
10.4. Sıralı Argümanlar¶
Önceki bölümlerden birinde, parametre gerektiren bir fonksiyonu parametresiz olarak çağırdığımızda şöyle bir hata almıştık:
TypeError: bas() takes exactly 1 positional argument (0 given)
İşte bu dersimizde bu “sıralı argüman” (positional argument) ifadesinin neye karşılık geldiğini öğreneceğiz.
Esasında şimdiye kadar verdiğiniz fonksiyon örneklerinde çoğunlukla sıralı argümanlardan yararlandık. Sıralı argümanlar, bir fonksiyon içinde, bulunduğu konuma, yani sırasına göre belirtilen argümanlardır. Bu ne demek oluyor? Daha önce yaptığımız şu örneğe bakalım:
def deneme(bir, iki, liste, ölçüt):
sonuç = bir * iki
if bir and iki != ölçüt:
liste.append(sonuç)
else:
print("{0} ile çarpmaya izin vermiyoruz!".format(ölçüt))
print(liste)
öğeler = []
deneme(23, 5, öğeler, 4)
Burada deneme() fonksiyonunu çağırırken “bir”, “iki”, “liste” ve “ölçüt” parametrelerinin hangi sırayla dizildiğine dikkat etmeliyiz. Yani “iki” parametresine yazmamız gereken şeyi “liste” parametresinin yerine yazamayız. Bu tür parametrelere sıralı parametreler veya sıralı argümanlar adı verilir. Dolayısıyla, adlarından da anlaşılacağı gibi, bu argümanların fonksiyon içindeki sırası önemlidir. Mesela range() fonksiyonunu düşünelim:
for i in range(1, 10, 2):
print(i)
Burada range() fonksiyonuna verdiğimiz “1” parametresi saymaya kaçtan başlayacağımızı, “10” parametresi ise saymayı kaçta bitireceğimizi gösterir. En sonda verdiğimiz “2” parametresi ise kaçar kaçar sayacağımızı gösterir. Bu üç parametrenin, fonksiyon içinde bulunduğu sıra önemlidir. Yani bu sayıların sırasını değiştirirsek farklı sonuçlar elde ederiz...
Sıralı argümanların çok fazla bir özelliği yoktur. Burada dikkat etmemiz gereken tek şey, parametrelerin konumunu ve sırasını karıştırmamaktır. Çünkü farklı sıralandırmalar farklı sonuçlar ortaya çıkarabilir. Bu basit konuyu da gözden geçirdiğimize göre ilerlemeye devam edebiliriz.
10.5. İsimli Argümanlar¶
Bir önceki bölümde sıralı argümanları görmüştük. Karşımıza çıkacak fonksiyonlar ve bizim kendi yazacaklarımız genellikle sıralı argümanlara sahip fonksiyonlar olacaktır... Ancak bazı durumlarda sıralı argümanlar bize birtakım zorluklar çıkarabilir. Özellikle bir fonksiyonun çok sayıda sıralı argümana sahip olduğu durumlarda parametrelerin neler olduğunu, bunların sayısını ve sırasını hatırlamak hiç de kolay olmayabilir. İşte bu tür durumlarda, fonksiyonların argümanlarını isimleriyle çağırma yoluna gidebiliriz... Hemen bir örnek verelim:
def deneme(bir, iki, liste, ölçüt):
sonuç = bir * iki
if bir and iki != ölçüt:
liste.append(sonuç)
else:
print("{0} ile çarpmaya izin vermiyoruz!".format(ölçüt))
print(liste)
öğeler = []
deneme(ölçüt=23, bir=5, liste=öğeler, iki=4)
Gördüğünüz gibi, argümanları isimleriyle çağırarak, yani isimli argümanları kullanarak kendimizi argümanların sırasını ezberleme zahmetinden kurtarmış oluyoruz...
Burada da kullandığımız isimli argümanları isimsiz argümanlardan sonra getirmeye dikkat ediyoruz. Aksi halde Python bize bir hata mesajı gösterecektir. Dolayısıyla yukarıdaki fonksiyonu aşağıdaki şekilde çağırmamız mümkün değildir:
deneme(ölçüt=23, bir=5, liste=öğeler, 4)
Buradaki sorun, isimli argümanlar olan ölçüt, bir ve listenin; isimsiz bir argüman olan 4’ten önce gelmesidir... Böyle bir durumda programımız hata verecektir.
10.6. Rastgele Sayıda İsimsiz Argüman Verme¶
Hatırlarsanız birkaç bölüm önce şöyle bir fonksiyon tanımlamıştık:
def çarp(liste):
a = 1
for i in liste:
a = i * a
print(a)
Bu fonksiyon, liste halinde verilmiş sayıları birbiriyle çarpma vazifesi görüyordu. Biz bu fonksiyonu, Python’daki sum() adlı fonksiyona özenerek yazmıştık. Hatırlarsanız bu sum() fonksiyonu kendisine verilen liste halindeki sayıları birbiriyle toplayabiliyordu. Bizim tanımladığımız çarp() fonksiyonu da buna benzer şekilde, kendisine verilen liste halindeki sayıları birbiriyle çarpabiliyor... Ancak çarp() ve sum() adlı fonksiyonların önemli bir eksikliği var. Bildiğimiz gibi, sum() fonksiyonunu şu şekilde kullanıyoruz:
li = [2, 3, 4]
sum(li)
Aynı şekilde, yukarıda tanımladığımız çarp() fonksiyonunu da şöyle kullanıyoruz:
li = [2, 3, 4]
çarp(li)
Ancak biz bu iki fonksiyonu mesela şöyle kullanamıyoruz:
sum(2, 3, 4)
...veya...
çarp(2, 3, 4)
Bu fonksiyonları yukarıdaki şekilde çağırmayı denediğimizde Python bize hata mesajı gösterecektir.
çarp() fonksiyonu şöyle bir hata verir:
TypeError: çarp() takes exactly 1 positional argument (3 given)
Yani:
TipHatası: çarp() tamı tamına 1 adet sıralı argüman alır (3 adet verilmiş)
sum() fonksiyonu ise şu hatayı verir:
TypeError: sum expected at most 2 arguments, got 3
Yani:
TipHatası: sum en fazla 2 argüman almayı beklerken 3 argüman almış
Gördüğünüz gibi, ne sum() ne de çarp() fonksiyonu kendilerine verilen 2, 3 ve 4 sayıları üzerinde işlem yapabiliyor. Biz burada sum() fonksiyonuna müdahale edemeyiz, ama istersek çarp() fonksiyonuna böyle bir yetenek kazandırabiliriz.
Öncelikle şu basit fonksiyonu inceleyelim:
def bas(isim):
print("merhaba", isim)
Eğer tanımladığımız bu fonksiyonu “Fırat” parametresiyle çalıştırırsak şöyle bir sonuç alırız:
bas("Fırat")
merhaba Fırat
Bu fonksiyonu birden fazla argüman ile çalıştıramayız. Çünkü fonksiyonu tanımlarken yalnızca tek bir parametre belirledik (isim). Eğer birden fazla argümanla çalıştırmak istersek, istediğimiz argüman sayısı kadar parametreyi fonksiyon tanımı içine yerleştirmeliyiz:
def seksenler(bir, iki, üç):
print("bir {0}, bir {1}, bir de {2} alacağım!".format(bir, iki, üç))
seksenler("kalem", "pergel", "çikolata")
Peki ya parametre sayısını belirtmeden, istediğimiz kadar argümana izin vermek istersek ne olacak? Şu örneği daha önce de vermiştik:
def çarp(sayılar):
a = 1
for i in sayılar:
a = a * i
print(a)
li = [2, 3, 4]
çarp(li)
Gördüğünüz gibi, birden fazla sayıyı birbiriyle çarpabilmek için bunları önce bir liste haline getirmemiz gerekiyor (demet haline de getirebiliriz...). “çarp(2, 3, 4)” gibi bir şey yazdığımızda hata alacağımızı biliyoruz. İşte şimdi bu engeli nasıl aşacağımızı göreceğiz. Dikkatlice bakın:
def çarp(*sayılar):
a = 1
for i in sayılar:
a = a * i
print(a)
çarp(2, 3, 4)
Burada tek fark, parantez içindeki “sayılar” parametresinin solundaki ufacık bir “*” işaretinden ibaret. Bu yıldız işareti, fonksiyonumuzun alabileceği kadar parametreyi alabilmesini sağlar. Hatta bu işaret, fonksiyonumuzu parametresiz olarak da çağırmamıza olanak tanır. Bu arada ek bilgi olarak, bir fonksiyonun en fazla 255 parametre alabileceğini söyleyelim...
Yıldız işareti, fonksiyona argüman olarak verilen bütün değerlerin bir demet haline getirilmesini sağlar. Bunu şu şekilde teyit edebiliriz:
def çarp(*sayılar):
print(sayılar)
çarp(2, 3, 4)
Bu kodları çalıştırdığımızda şöyle bir çıktı elde ederiz:
(2, 3, 4)
Çıktıdaki parantez işaretlerinden de anladığımız gibi, bu fonksiyon bize bir demet veriyor... Aslında burada olan şey, basit bir “demetleme” (tuple packing) işleminden ibarettir. Yazdığımız fonksiyonlarda “*” işaretini kullandığımız zaman, Python fonksiyona argüman olarak verilen öğeleri bir demet haline getirir. Böylece demet haline gelmiş bu öğeler birbirleriyle işleme sokulabilir.
Python’daki bu özelliğin yapısını gösteren basit bir örnek verelim:
def test(arg, *arglar):
print("Bu fonksiyondaki sıralı argümanımız şudur: ", arg)
print("Bu fonksiyondaki isimsiz argümanlarımız şunlardır: ", *arglar)
test("bir", "iki", "üç", "dört", "beş")
Yukarıdaki fonksiyon bir adet sıralı argüman ve rastgele sayıda isimsiz argüman alıyor. Bu arada, “isimsiz argüman” sözü kafanızı karıştırmasın. Aslında isimsiz argüman dediğimiz şey “sıralı argüman”ın ta kendisidir... Ancak yıldızlı yapılarda sıra söz konusu olmadığı için, bu argümanları “sıralı argüman” yerine “isimsiz argüman” (non-keyword argument) olarak adlandırıyoruz. Eğer istersek yukarıdaki test fonksiyonunu şöyle de yazabiliriz:
def test(arg, *arglar):
print("Bu fonksiyondaki sıralı argümanımız şudur: ", arg)
for i in arglar:
print("Bu fonksiyondaki isimsiz argümanlardan biri şudur: ", i)
test("bir", "iki", "üç", "dört", "beş")
Bu yıldız işaretini yukarıda gösterdiğimiz şekilde fonksiyonun tanımlandığı sırada kullanabileceğimiz gibi, fonksiyonu çağırma aşamasında da kullanabiliriz. Yani şöyle bir şey yazabiliriz:
def test(arg, *arglar):
print("Bu fonksiyondaki sıralı argümanımız şudur: ", arg)
for i in arglar:
print("Bu fonksiyondaki isimsiz argümanlardan biri şudur: ", i)
li = ["bir", "iki", "üç", "dört", "beş"]
test(*li)
Burada test() adlı fonksiyonu çağırırken, argümanları doğrudan parantez içine yazmak yerine, bu argümanları “li” adlı bir liste içinde topladık. Ardından da test() fonksiyonu içinde bu “li” adlı listeyi yıldız işaretiyle birlikte kullanarak istediğimiz sonucu elde ettik.
Yıldız işaretini, yukarıda anlattıklarımızdan farklı bir işlevi yerine getirmek için de kullanabiliriz. Bu işlevin ne olduğunu isterseniz bir örnek üzerinde görelim...
Hatırlarsanız, isimli argümanları kullanarak şöyle bir şey yazabiliyorduk:
def topla(bir, iki):
print(bir + iki)
Tanımladığımız bu fonksiyonu şu şekilde çağırabiliriz:
topla(bir=3, iki=5)
Bildiğiniz gibi, isimli argümanları kullanmak, fonksiyon parametrelerini istediğimiz bir sırada kullanmamıza olanak veriyor. Yani yukarıdaki topla() adlı fonksiyonu şu şekilde de çağırabiliyoruz:
topla(iki=5, bir=3)
İsimli argümanlar bizi parametre sırasını ezberleme zahmetinden kurtarıyor. Ama elbette istersek biz yukarıdaki fonksiyonu şu şekilde de çağırabiliriz:
topla(3, 5)
Bu fonksiyonu bu şekilde çağırdığımızda, “bir” ve “iki” adlı parametreleri sıralı argüman olarak kullanmış olduk. Dolayısıyla, topla() fonksiyonuna verdiğimiz “3” argümanı “bir” parametresinin; “5” argümanı ise “iki” adlı parametrenin yerini tutmuş oldu... Gelelim yıldız işaretinin burada anlattığımız durumla ne ilgisi olduğuna...
Gördüğünüz gibi, tanımladığımız fonksiyonların parametrelerini hem isimli hem de sıralı argüman olarak kullanabiliyoruz. Ancak bazı durumlarda, kullanıcının sadece isimli argümanlar kullanmasına izin vermek istiyor olabiliriz... Mesela yukarıdaki örnekte şöyle bir kullanıma müsaade etmek istemiyor olabiliriz:
topla(3, 5)
Biz istiyoruz ki topla() fonksiyonu sadece isimli argümanlar ile çağrılabilsin... Eğer isteğimiz sadece isimli argümanlara müsaade etmek ise şöyle bir yol izleyebiliriz:
def topla(*, bir, iki):
print(bir + iki)
Kodlarımızı bu şekilde yazdığımızda, yıldız işaretinin sağında kalan bütün parametrelerin, fonksiyonun çağrılması esnasında isimli argüman olarak kullanılmaları gerekir. Dolayısıyla yukarıdaki topla() fonksiyonunu ancak şu şekilde kullanabiliriz:
topla(bir=4, iki=6)
Yani sadece şöyle bir yazım bizi hüsrana uğratacaktır:
topla(4, 6)
TypeError: topla() takes exactly 0 positional arguments (2 given)
Burada aldığımız hata mesajı bize şöyle diyor:
TipHatası: topla() tamı tamına 0 adet sıralı argüman alır (2 adet verilmiş)
Gördüğünüz gibi, topla() fonksiyonunu kullanabilmek için mutlaka isimli argümanları kullanmamız gerekiyor. Parametrelerin en başına yerleştirdiğimiz “*” işareti sayesinde, bu işaretin sağında kalan hiç bir parametreyi isimsiz olarak kullanamıyoruz...
Bu arada, hatırlarsanız, bu bölümün en başında şöyle bir fonksiyon yazmıştık:
def çarp(*sayılar):
a = 1
for i in sayılar:
a = a * i
print(a)
çarp(2, 3, 4)
Yıldızlı parametre sayesinde bu fonksiyonu çağırırken istediğimiz sayıda argüman kullanabiliyoruz. Ancak bu fonksiyonun bir problemi var. Bu fonksiyona argüman olarak tek tek öğeler değil de bir liste (veya demet) verirsek beklediğimiz çıktıyı alamıyoruz:
def çarp(*sayılar):
a = 1
for i in sayılar:
a = a * i
print(a)
li = [2, 3, 4]
çarp(li)
[2, 3, 4]
Gördüğünüz gibi, fonksiyonumuz 2, 3 ve 4 sayılarını birbirleriyle çarpmak yerine bu sayıları ekrana bastı... Ama bizim istediğimiz şey bu değil. Burada öyle bir kod yazalım ki, kullanıcı argümanları liste halinde de verse, tek tek de yazsa, çarp() fonksiyonumuz bütün sayıları birbiriyle çarpabilsin...
def çarp(*sayılar):
a = 1
try:
for k in sayılar[0]:
a = a * k
print(a)
except TypeError:
for v in sayılar:
a = a * v
print(a)
Artık çarp() fonksiyonuna argüman olarak ister liste verelim, ister tek tek öğe yazalım, fonksiyonumuz bütün öğeleri başarıyla çarpacak ve sonucu ekrana basacaktır... Yukarıdaki kodlarda neler olup bittiğini anlayabilmek için isterseniz bu kodları biraz daha yakından inceleyelim. Öncelikle “sayılar” adlı parametrenin ne çıktı verdiğini kontrol edelim:
def çarp(*sayılar):
print(sayılar)
Şimdi bu fonksiyonu bir liste ile çağıralım:
li = [2, 3, 4]
çarp(li)
Buradan şu çıktıyı alıyoruz:
([2, 3, 4],)
Gördüğünüz gibi, çıktımız tek öğeli bir demet... Bu demetin mevcut tek öğesi de bir liste... Bu listenin öğelerine erişmek için fonksiyonumuzu şöyle yazmamız gerekiyor:
def çarp(*sayılar):
for k in sayılar[0]:
print(k)
Şimdi fonksiyonumuzu çağırıyoruz:
li = [2, 3, 4]
çarp(li)
2
3
4
Gördüğünüz gibi, bu şekilde listenin bütün öğelerini tek tek aldık... Tek tek aldığımız bu öğeleri birbirleriyle çarpmak için şöyle bir şey yazmamız gerektiğini biliyoruz:
def çarp(*sayılar):
a = 1
for k in sayılar[0]:
a = a * k
print(a)
li = [2, 3, 4]
çarp(li)
Kodlarımızı bu şekilde yazdığımızda, eğer çarp() fonksiyonuna argüman olarak bir liste verirsek fonksiyonumuz sorunsuz bir şekilde çalışacaktır. Ama eğer argüman olarak tek tek sayılar yazarsak Python bize bir hata mesajı gösterecektir:
çarp(2, 3, 4)
TypeError: 'int' object is not iterable
Çünkü daha önce de gördüğümüz gibi, “int” (sayı) veri tipi döngülenebilen bir nesne değildir. Dolayısıyla “int” veri tipi üzerinde bir for döngüsü kuramıyoruz... O halde bizim burada yapmamız gereken, bu hatayı yakalayıp buna göre bir işlem yapmak... Yani kodlarımız taslak olarak şöyle görünmeli:
def çarp(*sayılar):
a = 1
try:
for k in sayılar[0]:
a = a * k
print(a)
except TypeError:
pass
Burada “pass” ile gösterdiğimiz yere uygun kodları yazacağız... O kısma, bu bölümün en başında gösterdiğimiz kodları yazmamız yeterli olacaktır:
def çarp(*sayılar):
a = 1
try:
for k in sayılar[0]:
a = a * k
print(a)
except TypeError:
for v in sayılar:
a = a * v
print(a)
Böylelikle, “TypeError” adlı hata alındığında, Python çarp() fonksiyonuna verilen argümanların döngülenebilen bir nesne olmadığını anlayacak ve bu argümanları alıp döngülenebilen bir nesne olan demete dönüştürecektir...
10.7. Rastgele Sayıda İsimli Argüman Verme¶
Bir önceki bölümde fonksiyonları kullanarak, nasıl rastgele sayıda isimsiz argüman verebileceğimizi öğrenmiştik. Bu bölümde ise rastgele sayıda isimli argüman verme konusunu inceleyeceğiz.
Bildiğiniz gibi isimli argümanlar, adından da anlaşılacağı gibi, bir isimle birlikte kullanılan argümanlardır. Örneğin şu fonksiyonda isimli argümanlar kullanılmıştır:
def adres_defteri(isim, soyisim, telefon):
defter = {}
defter["isim"] = isim
defter["soyisim"] = soyisim
defter["telefon"] = telefon
for k, v in defter.items():
print("{0}\t:\t{1}".format(k, v))
adres_defteri(isim="Fırat", soyisim="Özgül", telefon="02122121212")
Burada öncelikle “defter” adlı bir sözlük tanımladık. Adres bilgilerini bu sözlük içine kaydeceğiz. Daha sonra bu “defter” adlı sözlük içinde yer alacak alanları belirliyoruz. Yazdığımız kodlara göre defter adlı sözlük içinde “isim”, “soyisim” ve “telefon” olmak üzere üç farklı alan bulunacak. Fonksiyon parametresi olarak belirlediğimiz “isim”, “soyisim” ve “telefon” öğelerine karşılık gelen değerler, defter adlı sözlükteki ilgili alanlara yerleştirilecek.
Daha sonra gelen satırda şöyle bir kod görüyoruz:
for k, v in defter.items():
print("{0}\t:\t{1}".format(k, v))
Burada “defter” sözlüğünün items() metodunu kullanarak, sözlük içindeki “anahtar” ve “değer” çiftlerini birer demet halinde alıyoruz. Eğer yukarıdaki kodu şöyle yazacak olursanız neler olup bittiği biraz daha netleşecektir:
for öğeler in defter.items():
print(öğeler)
('soyisim', 'Özgül')
('adres', 'İstanbul')
('telefon', '02122121212')
('eposta', 'firatozgul@frtzgl.com')
('cep', '05994443322')
('isim', 'Fırat')
Gördüğünüz gibi, yukarıdaki kod bize defter adlı sözlüğün “anahtar” ve “değer” çiflerini içeren birer demet veriyor. “for k, v in defter.items()” satırı ile bu demetlerdeki öğelere tek tek erişebiliyoruz. Burada “k” harfi, demetlerin ilk öğelerini, “v” harfi ise ikinci öğelerini temsil ediyor. Alt satırdaki “print(“{0}t:t{1}”.format(k, v))” kodu yardımıyla yaptığımız şey, “k” ve “v” ile temsil edilen öğeleri biraz biçimlendirerek ekrana basmaktan ibaret... Bu satır içinde gördüğümüz “\t” harflerinin ne işe yaradığını biliyoruz. Bunlar karakter dizileri içine sekme (tab) eklemek için kullanılan kaçış dizileridir.
Bu fonksiyona göre, “isim”, “soyisim” ve “telefon” alanlarını doldurarak fonksiyonu çalışır hale getirebiliyoruz. Şimdi yukarıdaki fonksiyona şöyle bir ekleme yapalım:
def adres_defteri(isim, soyisim, telefon, **arglar):
defter = {}
defter["isim"] = isim
defter["soyisim"] = soyisim
defter["telefon"] = telefon
for i in arglar.keys():
defter[i] = arglar[i]
for k, v in defter.items():
print("{0}\t:\t{1}".format(k, v))
adres_defteri(isim="Fırat",
soyisim="Özgül",
telefon="02122121212",
eposta="firatozgul@frtzgl.com",
adres="İstanbul",
cep="05994443322")
Burada yaptığımız eklemeler sayesinde adres_defteri() adlı fonksiyona “isim”, “soyisim” ve “telefon” parametrelerini yerleştirdikten sonra istediğimiz sayıda başka isimli argümanlar da belirtebiliyoruz. Burada “**arglar” parametresinin bir sözlük (dictionary) olduğuna özellikle dikkat edin. Bu parametre bir sözlük olduğu için, sözlüklerin bütün özelliklerine de doğal olarak sahiptir. Yukarıdaki kodları şu şekilde yazarak, arka planda neler olup bittiğini daha açık bir şekilde görebiliriz:
def adres_defteri(isim, soyisim, telefon, **arglar):
print("isim:\n\t", isim)
print("soyisim:\n\t", soyisim)
print("telefon:\n\t", telefon)
print("öteki argümanlar:\n\t", arglar)
adres_defteri(isim="Fırat",
soyisim="Özgül",
telefon="02122121212",
eposta="firatozgul@frtzgl.com",
adres="İstanbul",
cep="05994443322")
Bu kodları çalıştırdığımızda şuna benzer bir çıktı alacağız:
isim:
Fırat
soyisim:
Özgül
telefon:
02122121212
öteki argümanlar:
{'adres': 'İstanbul',
'eposta': 'firatozgul@frtzgl.com',
'cep': '05994443322'}
Gördüğünüz gibi, “arglar” parametresi bize bir sözlük veriyor. Dolayısıyla şöyle bir kod yazmamız mümkün olabiliyor:
for i in arglar.keys():
defter[i] = arglar[i]
Bu kodlar yardımıyla “arglar” adlı sözlüğün öğelerini “defter” adlı sözlüğe ekliyoruz. Bu yapının kafanızı karıştırmasına izin vermeyin. Aslında yaptığımız şey çok basit. Sözlükler konusunu anlatırken verdiğimiz şu örneği hatırlayın:
sözlük = {}
sözlük["ayakkabı"] = 2
sözlük["elbise"] = 1
sözlük["gömlek"] = 4
Bu örnekte “sözlük” adlı sözlüğe “ayakkabı”, “elbise” ve “gömlek” adlı öğeler ekliyoruz. Bu öğelerin değerini sırasıyla “2”, “1” ve “4” olarak ayarlıyoruz. Dediğim gibi, yukarıdaki örnek “for i in arglar.keys()...” satırıyla yaptığımız şeyden farklı değildir. Şöyle düşünün:
defter = {}
defter["adres"] = "İstanbul"
defter["eposta"] = "firatozgul@frtzgl.com"
defter["cep"] = "05994443322"
“defter[i]” dediğimiz şey, “adres”, “eposta” ve “cep” öğelerine; “arglar[i]” dediğimiz şey ise “İstanbul”, “firatozgul@frtzgl.com” ve “05994443322” öğelerine karşılık geliyor.
Gördüğünüz gibi, çift yıldızlı parametreler fonksiyonlara istediğimiz sayıda isimli argüman ekleme imkanı tanıyor bize... Dediğim gibi, bu parametreler bir sözlük olduğu için yukarıdaki örneği şu şekilde de yazabilirsiniz:
def adres_defteri(isim, soyisim, telefon, **arglar):
defter = {}
defter["isim"] = isim
defter["soyisim"] = soyisim
defter["telefon"] = telefon
for i in arglar.keys():
defter[i] = arglar[i]
for k, v in defter.items():
print("{0}\t:\t{1}".format(k, v))
sözlük = {"eposta": "firatozgul@frtzgl.com",
"adres": "İstanbul",
"cep": "05994443322"}
adres_defteri(isim="Fırat",
soyisim="Özgül",
telefon="02122121212",
**sözlük)
Burada sözlüğü önceden tanımladığımıza ve bunu fonksiyonu çağırırken doğrudan argüman olarak eklediğimize dikkat edin. Ayrıca “sözlük” argümanını fonksiyona yazarken yine çift yıldızlı yapıyı kullanmayı da unutmuyoruz.
10.8. Gömülü Fonksiyonlar¶
Python’daki bazı fonksiyonlara “gömülü fonksiyonlar” adı verilir. Örneğin daha önceki derslerimizde gördüğümüz range(), sum(), len() gibi fonksiyonlar bu sınıfa girer. Bu fonksiyonlara “gömülü fonksiyonlar” denmesinin nedeni, adından da anlaşılacağı gibi, bu fonksiyonların Python’un içine tam anlamıyla gömülü olmasıdır... Gömülü fonksiyonların karşıtı kullanıcı tanımlı fonksiyonlardır. Kullanıcı tanımlı fonksiyonlar, yine adından anlaşılacağı gibi, kullanıcının bizzat kendisi tarafından tanımlanmış, “el yapımı” fonksiyonlardır... Örneğin bir önceki bölümde tanımlamış olduğumuz çarp() fonksiyonu kullanıcı tanımlı bir fonksiyondur. Kullanıcı tanımlı fonksiyonların aksine gömülü fonksiyonlar Python’u geliştiren kişiler tarafından tanımlanmış ve Python’un içine yerleştirilmiştir. Gömülü fonksiyonlar istediğimiz her an kullanılabilir vaziyettedir. Yani mesela len() fonksiyonunu, yazdığımız bir program içinde kullanabilmek için bu fonksiyonu programımız içinde yeniden tanımlamamıza gerek yoktur. Kullanıcı tanımlı fonksiyonları ise iki şekilde kullanabiliriz. Bu fonksiyonları ya mevcut program içinde bir yerlerde tanımlamış olmalıyız, ya da eğer bu fonksiyon başka bir dosya içinde tanımlanmışsa, o dosyayı (yani modülü) mevcut programımız içine aktarmalıyız. Bu “modül” ve “içe aktarma” kavramlarının kafanızı karıştırmasına izin vermeyin. Birkaç bölüm sonra bu kavramların ne anlama geldiğini ayrıntılı bir şekilde inceleyeceğiz. Burada amacımız sizi sadece “gömülü fonksiyonlar”ın varlığı konusunda bilgilendirmekten ibaret...
Python’daki bütün gömülü fonksiyonların listesini http://docs.python.org/dev/3.0/library/functions.html adresinde bulabilirsiniz. Orada da göreceğiniz gibi, listedeki gömülü fonksiyonların bir kısmını zaten biliyoruz...
Python’daki gömülü fonksiyonların özelliği, bu fonksiyonların kaynak kodlarının Python’la yazılmamış olmasıdır. Bu fonksiyonlar Python gelişticileri tarafından C dili kullanılarak yazılmıştır. Gömülü fonksiyonların tanımlandığı C dosyasına http://svn.python.org/view/python/trunk/Python/bltinmodule.c?view=markup&pathrev=52315 adresinden erişebilirsiniz.
Bu fonksiyonlar C dili ile yazıldığı için son derece hızlı ve performanslı çalışırlar. Dolayısıyla eğer yapmak istediğiniz bir işlem için uygun bir hazır fonksiyon varsa, gerekli işlevi yerine getiren fonksiyonu kendiniz yazmak yerine Python içindeki o hazır fonksiyonu kullanmalısınız.
10.9. Fonksiyonların Kapsamı ve global Deyimi¶
Bu bölümde Python’daki “isim alanı” (namespace) ve kapsam (scope) kavramından söz edeceğiz biraz... Kabaca ifade etmek gerekirse “isim alanı” denen şey, Python’daki herhangi bir nesnenin etki alanıdır. Python’daki nesnelerin birer kapsama alanı vardır. Örneğin Python’daki fonksiyonlar birer nesnedir. Esasında Python’daki her şey bir nesnedir. Şimdilik bu “nesne” meselesini fazla kafanıza takmayın. Bu konuyu yeri geldiğinde esaslı bir şekilde inceleyeceğiz... Ne diyorduk? Evet, Python’daki fonksiyonlar birer nesnedir ve bu nesnelerin de bir kapsama alanı vardır. Ya da başka bir deyişle fonksiyonlar belirli bir isim alanı içinde yer alır. Hemen ufak bir örnek verelim:
def deneme():
sayı = 5
print(sayı)
Burada “sayı” adlı değişken, deneme() adlı fonksiyonun isim alanı içinde yer alır. Yani bu değişkenin kapsamı deneme() adlı fonksiyonla sınırlıdır. “sayı” adlı değişken bu fonksiyonun dışında var olamaz. Peki bu ne anlama geliyor? Yukarıdaki kodlara şöyle bir ekleme yaparak durumu birazcık da olsa somutlaştıralım:
def deneme():
sayı = 5
print(sayı)
print(sayı)
Bu kod parçasını çalıştırdığımızda Python bize şöyle bir hata mesajı gösterecektir:
NameError: name 'sayı' is not defined
Yani:
İsimHatası: "sayı" ismi tanımlanmamış
Halbuki biz “sayı” adlı değişkeni deneme() adlı fonksiyonun içinde tanımlamıştık, değil mi? Evet, biz bu değişkeni “deneme” adlı fonksiyon içinde tanımlamıştık. Ama önümüzde şöyle bir gerçek var: Bu bölümün başında da söylediğimiz gibi, Python’daki nesnelerin bir kapsama alanı vardır. Yani bu nesneler bir isim alanı içinde yer alır. Dolayısıyla “sayı” adlı değişkenin kapsamı deneme() adlı fonksiyonun isim alanı ile sınırlıdır. Yani bu değişken deneme() adlı fonksiyonun dışında var olamaz... Bu “deneme” fonksiyonunun kapsama alanı da fonksiyonun içi ile sınırlıdır.
Şimdi şuna bir bakalım:
def deneme():
sayı = 5
print("deneme() fonksiyonu içindeki sayı: ", sayı)
sayı = 10
print("deneme() fonksiyonu dışındaki sayı: ", sayı)
deneme()
Gördüğünüz gibi, “sayı” adlı değişkeni fonksiyon dışında da kullanabilmek için, bu değişkeni fonksiyonun dışında da tanımlamamız gerekiyor. Yukarıda iki farklı değerle gösterilen “sayı” adlı değişkenler farklı isim alanları içinde yer aldığı için, aynı ada sahip olmalarına rağmen birbirlerine karışmıyorlar. Aslında “isim alanı” kavramı çok güzel bir özelliktir. Büyük programlar yazarken isim alanı kavramının çok işinize yaradığını göreceksiniz. Bu kavram sayesinde değişkenlerin birbirine karışmasını önleyebiliyoruz. Hele bir de aynı program üzerinde farklı kişiler çalışıyorsa, bu “isim alanı” özelliği hayat kurtarıcı olabilir...
Ancak bazı durumlarda bir isim alanı içinde yer alan bir değişkene o isim alanı dışından da erişebilmeniz gerekebilir. Mesela şöyle bir şey yazmak isteyebilirsiniz:
def sor():
sayı1 = int(input("bir sayı: "))
sayı2 = int(input("başka bir sayı: "))
def topla():
print(sayı1 + sayı2)
try:
sor()
except ValueError:
pass
else:
topla()
Burada sayı alma ve alınan bu sayıları toplama işlemleri için ayrı birer fonksiyon oluşturduk. Yukarıdaki yapıya göre, sor() fonksiyonu içinde geçen “sayı1” ve “sayı2” adlı değişkenleri topla() fonksiyonu içinde işleme tabi tutabilmemiz gerekiyor. Ancak burada bir problem var. Python’un yapısı gereğince, sor() ve topla() adlı fonksiyonlar farklı isim alanlarına, dolayısıyla da farklı kapsamlara sahip... Bundan ötürü, bu iki fonksiyon içindeki değişkenlere dışarıdan erişemiyoruz. Zaten yukarıdaki kodları çalıştırdığımızda Python bize bununla ilgili bir hata mesajı gösterecektir...
Yukarıdaki sorunu aşabilmek için Python bize global adlı bir araç sunuyor... global deyimi yardımıyla, bir isim alanı içindeki değişkenlere o isim alanı dışından da erişebiliyoruz. Mesela yukarıdaki örneği şöyle yazalım:
def sor():
global sayı1
global sayı2
sayı1 = int(input("bir sayı: "))
sayı2 = int(input("başka bir sayı: "))
def topla():
print(sayı1 + sayı2)
try:
sor()
except ValueError:
pass
else:
topla()
Bu kodların başına eklediğimiz “global sayı1” ve “global sayı2” ifadeleri sayesinde “sayı1” ve “sayı2” adlı değişkenlere fonksiyon dışından da erişilebilmesini sağlıyoruz. Yukarıdaki kodları çalıştırdığımızda artık programımız hatasız bir şekilde görevini yerine getirecektir. Sayı dışında bir değer girilmesi durumunda dahi programımızın çökmemesi için try... except... bloklarından yararlandığımıza dikkat ediyoruz...
Bu arada eğer istersek yukarıdaki “global sayı1” ve “global sayı2” ifadelerini birleştirebiliriz:
def sor():
global sayı1, sayı2
...
...
Gelin isterseniz tanıdık bir program üzerinde uygulayalım öğrendiklerimizi...
def sor():
global sayı1
global sayı2
sayı1 = int(input("bir sayı: "))
sayı2 = int(input("başka bir sayı: "))
def topla():
print(sayı1 + sayı2)
def çıkar():
print(sayı1 - sayı2)
def çarp():
print(sayı1 * sayı2)
def böl():
try:
print(sayı1 / sayı2)
except ZeroDivisionError:
print("Bir sayıyı sıfıra bölemezsiniz!")
print("""Topla >> 1
Çıkar >> 2
Çarp >> 3
Böl >> 4
Çıkış >> 5
""")
while True:
seçenek = input("Yapmak istediğiniz işlem: ")
if seçenek == "5":
break
else:
try:
sor()
except ValueError:
print("Yanlış seçenek!..")
else:
if seçenek == "1":
topla()
elif seçenek == "2":
çıkar()
elif seçenek == "3":
çarp()
elif seçenek == "4":
böl()
Gördüğünüz gibi, global deyimi epey işe yarıyor. Ancak size bu deyimle ilgili kötü bir haberim var. Bu deyim ne kadar faydalıymış gibi görünse de aslında çoğu zaman işleri karıştırabiliyor. Şöyle bir örnek verelim:
a = 5
def değişkeni_değiştir():
a = 10
print("bu değişken fonksiyon içinde: ", a)
değişkeni_değiştir()
print("bu değişken fonksiyon dışında: ", a)
Bu kodları çalıştırdığımızda şöyle bir çıktı alıyoruz:
bu değişken fonksiyon içinde: 10
bu değişken fonksiyon dışında: 5
Bir de şuna bakalım:
a = 5
def değişkeni_değiştir():
global a
a = 10
print("bu değişken fonksiyon içinde: ", a)
değişkeni_değiştir()
print("bu değişken fonksiyon dışında: ", a)
Bu kodlar ise bize şöyle bir çıktı veriyor:
bu değişken fonksiyon içinde: 10
bu değişken fonksiyon dışında: 10
Gördüğünüz gibi, fonksiyon içindeki işlem fonksiyonun dış bölgesini de etkiledi ve normalde değeri “5” olması gereken “a” adlı değişkeni değişikliğe uğrattı. global deyimi bu özelliğinden ötürü hiç beklenmedik bir anda programınızı allak bullak edebilir. Özellikle büyük boyutlu ve birden fazla kişinin üzerinde çalıştığı programlarda global deyimi züccaciye dükkanına fil girmiş gibi bir etki yaratabilir... O yüzden Python programcıları genellikle global deyimini kullanmaktan kaçınır. Hatta Python camiasında şöyle bir söz vardır: “Eğer yazdığınız programda global deyimini kullanmanız gerektiğini düşünüyorsanız, programınızı gözden geçirmenizin zamanı gelmiş demektir!”
Python’da global yerine benimseyebileceğimiz daha sağlıklı yollar vardır. Mesela sınıflı yapıları kullanmak gibi... Hiç endişe etmeyin. Yeri geldiğinde sınıflı yapıları da enine boyuna inceleyeceğimizden emin olabilirsiniz. Peki global deyimini hangi durumlarda kullanmamızın bir sakıncası yoktur? Esasında, dediğimiz gibi, en iyisi kişinin kendini global deyimine hiç alıştırmamasıdır. Ama eğer uğraştığınız kod ufak boyutlu bir şeyse ve sizden başka kimsenin bu kodlar üzerinde çalışmayacağından eminseniz, o anda karşılaştığınız bir sorunu çözmek için global deyimine başvurmayı tercih edebilirsiniz... Elbette global deyimini kullanmak günah değildir! Sadece, bunu kullanmak iyi bir programcılık tekniği olarak kabul edilmez...
10.10. return Deyimi¶
Bu bölümde inceleyeceğimiz bir başka önemli konu da return deyimi olacak. return kelime olarak “döndürmek” anlamına gelir. Ama “döndürmek” derken bir şeyi kendi etrafında çevirmeyi kastetmiyoruz. Buradaki “döndürmek” ifadesi daha çok “iade etmek” veya “vermek” kavramına yakındır... Peki fonksiyonlarla bu kavramın ne ilgisi olabilir? İsterseniz bunu bir örnekle görmeye çalışalım:
bool(5>2)
True
Burada bool() fonksiyonunu kullanarak “5” sayısının “2” sayısından büyük olup olmadığını sorguladık. bool() fonksiyonu da bize karşılık olarak True değerini verdi. İşte bu durum için, “bool() fonksiyonu True değerini döndürdü,” diyoruz... Şu örneğe bakalım:
li = [2, 3, 4]
sum(li)
9
Burada da sum() fonksiyonu bir sayı döndürüyor...
İşte return deyimi Python’da bu anlama geliyor. Yani kabaca bir fonksiyonun değer üretmesi, bir sonuç ortaya çıkarması “döndürmek” (return) olarak adlandırılıyor. Peki biz bu deyimi fonksiyonlarımız içinde nasıl kullanacağız? Daha doğrusu, fonksiyonlarımızın bir değer döndürmesini nasıl sağlayacağız? Esasında Python’daki bütün fonksiyonlar, biz belirtsek de belirtmesek de mutlaka bir değer döndürür. Şu örneği inceleyelim:
def bas(kelime):
print(kelime)
a = bas("www.istihza.com")
print(a)
Bu kodları çalıştırdığımızda şöyle bir sonuç elde edeceğiz:
www.istihza.com
None
İşte bu çıktıda gördüğümüz “None”, yazdığımız fonksiyonun dönüş değeridir. “www.istihza.com” ise fonksiyon içinde yazdığımız print() fonksiyonunun marifetidir ve onun yaptığı şey return‘un yaptığı şeyden farklıdır. return ile print() aynı şeyler değildir ve aynı işlevi görmezler.
Yukarıdaki örneğe göre, Python’daki bütün fonksiyonlar bir değer döndürüyor. Özel olarak bir dönüş değeri belirtilmezse, fonksiyonun döndüreceği değer “None” olacaktır. “Eee, ne olmuş?” dediğinizi duyar gibiyim... Konuyu biraz daha somutlaştırmak için şöyle bir örnek verelim:
def santtan_faha(sant):
fah = sant * (9/5) + 32
return fah
Burada kendisine verilen santigrat dereceyi fahrenhayt’a çeviren bir uygulama yazdık... Bu fonksiyonu şu şekilde çağırıyoruz:
print(santtan_faha(22))
Yani kodlarımız tam olarak şöyle görünecek:
def santtan_faha(sant):
fah = sant * (9/5) + 32
return fah
print(santtan_faha(22))
Buradan şu çıktıyı alıyoruz:
71.6
Demek ki 22 santigrat derece 71.6 fahrenhayt’a karşılık geliyormuş... Neyse... İşin bizi ilgilendiren asıl kısmı bu değil. Biz buradaki return deyiminin ne işe yaradığını anlamaya çalışıyoruz.
Burada fonksiyonu nasıl çağırdığımıza dikkat edin. Daha önceki fonksiyonlarımızda şöyle bir çağırma şekli kullanıyorduk:
santtan_faha(22)
Burada print() fonksiyonunu kullanmadığımıza özellikle dikkatinizi çekmek isterim. Peki neden önceki örneklerde print() kullanmadık da yukarıdaki örnekte print() kullandık? Önceki örneklerde print() kullanmamamızın nedeni, o örneklerde hep fonksiyon içinde print()‘i kullanmış olmamızdır. Yani önceki örneklerde yazdığımız fonksiyonlar zaten belli bir değeri ekrana basıyordu. Yukarıdaki fahrenhayt fonksiyonunda ise fonksiyon içinde herhangi bir print() işlemi yapmadık. Dolayısıyla fonksiyonumuz kendi başına herhangi bir değeri ekrana basma özelliğine sahip değil. Bu fonksiyonun görevi “fah” değişkenini “döndürmekten” ibaret... Mesela daha önce bahsettiğimiz, gömülü fonksiyonlardan biri olan sum() fonksiyonu da ekrana herhangi bir değer basmaz. Bu fonksiyonun görevi kendisine parametre olarak verilen döngülenebilir nesnenin öğelerinin toplamını “döndürmek”tir. Görelim:
li = [2, 3, 4]
sum(li)
Bu kodları doğrudan bir dosyaya kaydedip çalıştırdığımızda herhangi bir çıktı almayız. Çünkü sum() fonksiyonu kendi başına herhangi bir değeri ekrana basmaz. Bu fonksiyonun bir değeri ekrana basabilmesi için şöyle bir şey yapmamız gerekir:
print(sum(li))
Aynen sum() fonksiyonunda olduğu gibi, yukarıda tanımladığımız santtan_faha() adlı fonksiyon da ekrana herhangi bir değer basmıyor. Bu fonksiyon sadece “fah” adlı değişkenin değerini “döndürüyor”. Fonksiyonumuzun döndürdüğü bu değerle ne yapacağımız tamamen bize kalmış. Biz bu değeri istersek ekrana basarız, istersek bununla başka işlemler yaparız. Bu durumu biraz açıklayalım. Şu örneğe bakın:
def santtan_faha(sant):
fah = sant * (9.0/5.0) + 32
return fah
print(santtan_faha(22)-1)
Bu fonksiyon bize şu çıktıyı verir:
70.6
Burada santtan_faha() fonksiyonunun döndürdüğü değerden 1 çıkardık... Aynı işlemi, fonksiyonu şu şekilde tanımlayarak yapmaya çalışalım:
def santtan_faha(sant):
fah = sant * (9.0/5.0) + 32
print(fah)
print(santtan_faha(22)-1)
Gördüğünüz gibi bu defa Python bize bir hata mesajı veriyor:
TypeError: unsupported operand type(s) for -: 'NoneType' and 'int'
Yani:
TipHatası: - işaretinin desteklemediği işleyen türü/türleri: "NoneType" ve "int"
Bu hata mesajı bize, birbiriyle çıkarma işlemi yapmanın mümkün olmadığı öğeler kullandığımızı söylüyor... Peki bu nasıl oluyor?
Hatırlarsanız, Python’da bütün fonksiyonların bir değer döndürdüğünü, eğer biz özel olarak herhangi bir değer döndürmezsek, döndürülecek değerin “None” olacağını söylemiş ve bu durumu da şu şekilde teyit etmiştik:
def bas(kelime):
print(kelime)
a = bas("www.istihza.com")
print(a)
Burada bas() fonksiyonu “www.istihza.com” karakter dizisini ekrana basıyor, ama “None” değerini “döndürüyor”. Yukarıda hata verdiğini gördüğümüz santtan_faha() fonksiyonunda da özel olarak herhangi bir değer döndürmediğimiz için bu fonksiyon otomatik olarak “None” değerini döndürüyor. Dikkat ederseniz, bu defa “fah” değişkenini döndürmek yerine doğrudan print()‘i kullanarak ekrana bastık. Yani burada değeri döndürmedik, ama ekrana basmakla yetindik. Bu yüzden santtan_faha() fonksiyonunun dönüş değeri “None” oldu. Dolayısıyla biz print(santtan_faha(22)-1) komutuyla sanki şöyle bir işlem yapıyormuş gibi olduk:
print(None - 1)
Python’un etkileşimli kabuğunda “None - 1” komutunu verdiğinizde şu hatayı alacaksınız:
TypeError: unsupported operand type(s) for -: 'NoneType' and 'int'
Gördüğünüz gibi, biraz önceki fonksiyonumuzun verdiği hatayla aynı hatayı aldık... Çünkü orada olan şeyle burada olan şey tamamen aynıdır. “None” değerinden “1” değerini çıkaramayacağımız için Python bize böyle bir hata veriyor. Buradan return deyiminin ne işe yaradığını az çok anlamış olmalıyız. Python’da fonksiyon tanımlarken doğrudan fonksiyon içinde herhangi bir değeri print() ile ekrana basmak her zaman iyi bir yol olmayabilir. Çünkü daha önce de dediğimiz gibi, fonksiyonlar şablonlara benzer. O yüzden fonksiyonları ne kadar genel tutarsak o kadar işe yarar. Bir fonksiyonun görevi sadece bir değeri ekrana basmak olmamalı. Tanımladığımız fonksiyonlarda herhangi bir değeri doğrudan ekrana basmak yerine o değeri “döndürmeyi” (return) tercih edersek, fonksiyondan dönen değer ile istediğimiz işlemi yapabiliriz. Artık dönen bu değeri sonradan ekrana basmak veya bu değerle başka işlemler yapmak tamamen size kalmış bir şey olacaktır...
Mesela yukarıdaki fonksiyonla şöyle bir deneme yapalım:
def santtan_faha(sant):
fah = sant * (9.0/5.0) + 32
return fah
santigrat = 20
print("{0} santigrat {1} fahrenhayt eder".format(santigrat,
santtan_faha(santigrat)))
Bu fonksiyonu çalıştırdığımızda istediğimiz çıktıyı alabiliriz. Ama eğer fonksiyonu şöyle tanımlarsak sıkıntı yaşarız:
def santtan_faha(sant):
fah = sant * (9.0/5.0) + 32
print(fah)
santigrat = 20
print("{0} santigrat {1} fahrenhayt eder".format(santigrat,
santtan_faha(santigrat)))
Buradan alacağımız çıktı şöyle olacaktır:
68.0
20 santigrat None fahrenhayt eder
Gördüğünüz gibi, fonksiyonumuzun döndürdüğü “None” değeri, karakter dizisinin içine yerleşti...
Sonuç olarak, return deyimi bir fonksiyonu genel amaçlı bir hale getirmeye yardımcı olan bir araçtır. Bu deyim yardımıyla, tanımladığımız fonksiyonların bir değer döndürmesini sağlıyoruz. Döndürülen bu değeri daha sonra istediğimiz gibi kullanma özgürlüğüne sahibiz...
10.11. Fonksiyonların Belgelendirilmesi¶
Bu bölümde kodlarımızın çalışmasını değil ama okunaklılığını etkileyecek bir özellikten söz edeceğiz. Bahsedeceğimiz özellik fonksiyonların belgelendirilmesi... “Fonksiyonların belgelendirilmesi de ne demek oluyor?” diye düşünmüş olabilirsiniz. İsterseniz size şöyle bir örnek göstererek konuya ısınmanızı sağlayalım:
Sıkça değindiğimiz fonksiyonlardan biri olan sum() fonksiyonunu ele alalım ve daha önce öğrendiğimiz dir() fonksiyonu yardımıyla bu fonksiyonun içine bakalım:
>>> dir(sum)
['__call__', '__class__', '__delattr__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__',
'__lt__', '__module__', '__name__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__']
Bu şekilde sum() fonksiyonunun niteliklerini ekrana dökmüş olduk. Bunlar içinde bizi ilgilendiren, dördüncü sıradaki __doc__ niteliğidir. Şimdi bu __doc__ niteliğini nasıl kullanacağımıza ve bu niteliğin ne işe yaradığına bakalım. Bu komutu etkileşimli kabukta veriyoruz:
>>> print(sum.__doc__)
sum(iterable[, start]) -> value
Returns the sum of an iterable of numbers (NOT strings) plus the value
of parameter 'start' (which defaults to 0). When the iterable is
empty, returns start.
Gördüğünüz gibi, “print(sum.__doc__)” komutu bize sum() fonksiyonu hakkında kısa bir bilgi veriyor. Tabii eğer İngilizce bilmiyorsanız bu kısa açıklamaların size pek faydası olmayacaktır. Ama hiç endişe etmeyin! istihza.com bugünler için var!...
Burada sum() fonksiyonunun __doc__ niteliğini kullanarak, bu fonksiyon hakkında kısa da olsa bilgi edinebiliyoruz. Bu __doc__ niteliği bütün fonksiyonlarda vardır. Hatta kendi tanımladığımız fonksiyonlar dahi bu niteliğe sahiptirler. Hemen bir fonksiyon tanımlayarak bu durumu teyit edelim:
def boş_fonksiyon():
pass
Bu arada, bu fonksiyonu isterseniz etkileşimli kabukta tanımlayın. Böylece fonksiyonu daha rahat test edebilirsiniz. Fonksiyonları etkileşimli kabukta tanımlamak için şöyle bir yol izliyoruz:
>>>def boş_fonksiyon():
... pass
...
>>>
Gördüğünüz gibi def ile başlayan satırı yazıp enter’e bastığımızda etkileşimli kabuğun “>>>” işareti ”...” işaretine dönüşüyor. Burada dört kez boşluk tuşuna basıp girinti veriyoruz ve “pass” satırını yazıyoruz. Ardından enter’e bastığımızda işaretin yine ”...” olduğunu görüyoruz. Etkileşimli kabuk burada bizden başka kodlar bekliyor, ama bu fonksiyonda bizim yazacağımız başka kod yok. O yüzden tekrar enter’e basarak “>>>” işaretinin olduğu başlangıç konumuna geri dönüyoruz. Böylece etkileşimli kabukta fonksiyonumuzu tanımlamış olduk. Şimdi yine etkileşimli kabukta şu komutu vererek, yeni oluşturduğumuz bu “boş_fonksiyon” adlı fonksiyonun içine, yani niteliklerine bakabiliriz:
>>> dir(boş_fonksiyon)
['__annotations__', '__call__', '__class__', '__closure__', '__code__',
'__defaults__', '__delattr__', '__dict__', '__doc__', '__eq__', '__format__',
'__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__',
'__init__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__',
'__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__',
'__sizeof__', '__str__', '__subclasshook__']
Gördüğünüz gibi, bizim oluşturduğumuz fonksiyonun da __doc__ adlı bir niteliği var. Gelin isterseniz biraz önce sum() fonksiyonuna yaptığımız gibi, “boş_fonksiyon”a da bu __doc__ niteliğini uygulayalım:
>>> print(boş_fonksiyon)
None
Gördüğünüz gibi, fonksiyonumuz “None” değeri döndürdü. Çünkü fonksiyonumuzu henüz belgelendirmedik. O yüzden bu __doc__ niteliği boş görünüyor. Şimdi bu fonksiyonu nasıl belgelendireceğimize bakalım:
def boş_fonksiyon():
"""Kullanımı: boş_fonksiyon() Hiç bir işe yaramayan boş bir fonksiyondur.
None değerini döndürür."""
pass
print(boş_fonksiyon.__doc__)
Bu kodları çalıştırdığımızda ekrana şu satırlar dökülecektir:
Kullanımı: boş_fonksiyon() Hiç bir işe yaramayan boş bir fonksiyondur.
None değerini döndürür.
Böylece fonksiyonumuzun __doc__ niteliğini doldurmuş, yani fonksiyonumuzu belgelendirmiş olduk. Artık bu fonksiyon hakkında bilgi almak isteyenler bu fonksiyonun __doc__ niteliğini kullanarak fonksiyonun ne yaptığı hakkında kısa bir bilgi sahibi olabilecekler. Burada fonksiyonumuzu nasıl belgelendirdiğimize dikkat edin. Bunun için “belgelendirme dizileri”nden (docstring) yararlandık. Fonksiyonu def parçacığı yardımıyla tanımladıktan hemen sonra getirdiğimiz açıklayıcı bilgilere “belgelendirme dizisi” adı veriyoruz. Python’da genel kural olarak belgelendirme dizileri üç tırnak içinde gösterilir. Belgelendirme dizilerini çift veya tek tırnakla da göstermek mümkün olsa da üç tırnak kullanmak adettendir. Ayrıca üç tırnak kullandığımızda uzun metinleri daha kolay yazabiliriz. Çünkü bildiğiniz gibi, üç tırnak kullanıldığında “enter” tuşuna basarak cümleleri rahatlıkla kaydırabiliyoruz...
Bu arada belgelendirme dizilerini yazarken girintilere dikkat ediyoruz. def parçacığı ile fonksiyonu tanımlayıp enter tuşuna bastıktan sonra belgelendirme dizisini girintili olarak yazmaya başlamamız gerekiyor. Yani şöyle bir şey yazamıyoruz:
def boş_fonksiyon():
"""belgelendirme dizisi..."""
Fonksiyonumuzu şöyle yazmamız gerekiyor:
def boş_fonksiyon():
"""belgelendirme dizisi..."""
Dediğimiz gibi, bir fonksiyonun belgelendirme dizisine ulaşmak için __doc__ niteliğini kullanıyoruz. Alternatif olarak bu dizilere ulaşmak için help() adlı özel bir fonksiyondan da yararlanabiliriz:
>>> print(help(enumerate))
En başta da söylediğimiz gibi, belgelendirme dizilerinin kodlarımızın çalışması üzerinde hiç bir etkisi yoktur. Ayrıca bunları yazmak mecburiyetinde de değiliz. Bu özellik kodlarımızı daha anlaşılır ve okunaklı kılmaya yarar. Bu sayede, yazdığımız fonksiyonu okuyanlar veya kullanacak olanlar fonksiyonun nasıl kullanılacağı hakkında bilgilenme imkanı edinmiş olurlar. Elbette yazdığımız belgelendirme dizilerinin faydası, bu dizilerin ne kadar düzgün yazıldığına bağlıdır...
Ayrıca belgelendirme dizilerinin sadece fonksiyonlara has bir özellik olmadığını da belirtelim. Bir sonraki bölümde de göreceğiniz gibi, belgelendirme dizileri modüllerin de sahip olduğu bir özelliktir. Dolayısıyla modülleri belgelendirmek için yine bu belgelendirme dizilerinden yararlanacağız...