4. Akış Denetimi

Bir önceki bölümde tanıtılan while deyiminin yanısıra Python'da diğer yazılımlama dillerinde de bulunan genel akış denetim deyimleri (bazı farklarla birlikte) vardır.

4.1. if Deyimi

Belki de en iyi bilinen deyim türü if deyimidir. Örnek:

>>> x = int(raw_input("Lütfen bir sayı girin: "))
>>> if x < 0:
...      x = 0
...      print 'Negatif sayı sıfırlandı'
... elif x == 0:
...      print 'Sıfır'
... elif x == 1:
...      print 'Bir'
... else:
...      print 'Birden büyük'
...

Sıfır veya daha fazla elif deyimi olabilir, ve else deyimi seçimliktir. elif deyimi else if deyiminin kısaltılmışıdır ve aşırı girintileri engellemesi açısından faydalıdır. Bir if ... elif ... elif ... deyimleri dizisi diğer dillerde bulunan switch veya case deyimlerinin yerine kullanılabilir.

4.2. for Deyimi

for deyimi Pascal veya C dillerinde görülenden biraz farklıdır. Python'daki for deyimi herhangi bir sıranın (liste, dizge, vs.) elemanları üzerinde sırayla yinelenir. Örnek:

>>> # Bazı dizgelerin boylarını ölçelim:
... a = ['kedi', 'pencere', 'kertenkele']
>>> for x in a:
...     print x, len(x)
...
kedi 4
pencere 7
kertenkele 10

Üzerinde yinelenilen sırada değişiklik yapmak güvenli değildir (bu sadece listelerde olabilir). Eğer böyle bir şey yapacaksanız bu iş için dilim gösterimi ile listenin bir kopyasını kullanabilirsiniz:

>>> for x in a[:]: # tüm listenin bir kopyasını oluştur
... if len(x) > 8: a.insert(0, x)
...
>>> a
['kertenkele', 'kedi', 'pencere', 'kertenkele']

4.3. range() işlevi

Eğer bir sayı sırası üzerinde tekrarlamalar yapmak isterseniz, belirli bir sıraya göre üretilen sayılardan oluşan bir liste oluşturan range() yerleşik işlevini kullanabilirsiniz. Örnek:

>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Verilen bitiş noktası asla üretilen listenin bir parçası olmaz; range(10) ifadesi 10 elemanı olan bir liste oluşturur. Listenin başlayacağı sayıyı ve artış miktarını da belirlemek mümkündür. Artış miktarı negatif de olabilir.

>>> range(5, 10)
[5, 6, 7, 8, 9]
>>> range(0, 10, 3)
[0, 3, 6, 9]
>>> range(-10, -100, -30)
[-10, -40, -70]

range() ve len() işlevlerini bir arada kullanarak da bir listenin elemanları üzerinde döngüler kurabilirsiniz:

>>> a = ['Python', 'yazılımlama', 'öğrenmek', 'çok', 'kolay !']
>>> for i in range(len(a)):
...     print i, a[i]
...
0 Python
1 yazılımlama
2 öğrenmek
3 çok
4 kolay

4.4. Döngülerde break, continue ve else Deyimleri

break deyimi, C'de olduğu gibi, içinde kaldığı en küçük for veya while döngüsünden çıkılmasına ve döngü deyiminin tamamen sona ermesine neden olur.

continue deyimi döngü içindeki diğer deyimlerin atlanıp bir sonraki yineleme işleminin başlamasına sebep olur.

Döngülerde else ifadesi de kullanılabilir; else bloğu döngü bittiğinde (for için) veya devamlılık koşulu geçersiz olduğunda (while için) işletilir; fakat döngü break deyimi ile sona erdiyse işletilmez. Bunu asal sayılar bulan aşağıdaki örnekte görebilirsiniz:

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...            print n, 'asal sayı değil. çarpanlar:', x, '*', n/x
...            break
...     else:
...          # çarpan bulunmadan döngü biter ise
...          print n, 'asal sayıdır'
...
2 asal sayıdır
3 asal sayıdır
4 asal sayı değil. çarpanlar: 2 * 2
5 asal sayıdır
6 asal sayı değil. çarpanlar: 2 * 3
7 asal sayıdır
8 asal sayı değil. çarpanlar: 2 * 4
9 asal sayı değil. çarpanlar: 3 * 3

4.5. pass Deyimi

pass deyimi hiçbir şey yapmaz. Python sözdizim kurallarına göre bir ifadenin gerekli olduğu, fakat yazılımın bir şey yapması gerekmediği zaman kullanılabilir:

>>> while 1:
...       pass # klavyeden CTRL+C ile kesilene kadar sürer
...

4.6. İşlev Tanımlama

Herhangi bir değere kadar Fibonacci serisi yazan bir işlev yazalım:

>>> def fib(n):     # n'e kadar Fibonacci serisini yazdır
...     "n'e kadar Fibonacci serisini yazdır"
...     a, b = 0, 1
...     while b < n:
...         print b,
...         a, b = b, a+b
...
>>> # Tanımladığımız işlevi çağıralım:
... fib(2000)
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597

def anahtar kelimesi bir işlev tanımını başlatır. Bu deyimden sonra bir işlev adı ve parantez içinde bağımsız değişkenler yazılır. İşlevin gövdesini oluşturan yazılım satırları sonraki satırdan itibaren girintili olarak yazılır. İşlev gövdesinin ilk satırı bir dizge de olabilir; bu dizge işlevin belgelenmesinde kullanılır (docstring).

İşlevlerin belgelenmesinde kullanılan dizgeleri (docstring) otomatik olarak çevrim içi ya da basılı belgeler oluşturmak için kullanan yazılımlar vardır. Ayrıca bazı geliştirme ortamları bunları yazılım yazarken kolaylık sağlaması için etkileşimli olarak yazılımcıya sunar. Yazdığınız işlevlere bunları eklemeyi bir alışkanlık haline getirmeniz faydalı olur.

Bir işlevin çağrılması (çalıştırılması) bu işlevdeki yerel değişkenlerin olduğu bir simge tablosu oluşturur. İşlev içerisinde bütün değer atama işlemlerinde değerler yerel simge tablosuna kaydedilir. Bir değişkene başvuru durumunda ise önce yerel (local), sonra genel (global) ve en son yerleşik (built-in) simge tablosunda arama yapılır. Bu yüzden genel değişkenlere doğrudan değer atama yapılamaz (eğer global ifadesi içinde kullanılmamışlar ise); ancak bunlara başvuru yapılabilir (reference).

İşlev çağırıldığında işlevin bağımsız değişkenleri yerel simge tablosuna eklenir; yani bağımsız değişkenler işleve değeri ile çağrı (call by value) kullanılarak iletilir (yani bağımsız değişkene yapılan değişiklikler yereldir, çağıran işlevdeki bağımsız değişkenlerde bir değişme olmaz).[68]

Bir işlev başka bir işlevi çağırdığında bu çağrı için yeni bir yerel simge tablosu oluşturulur.

Bir işlev tanımı işlev adının yürürlükte olan simge tablosuna eklenmesine sebep olur. İşlevin adı yorurumlayıcı tarafından kullanıcı tanımlı işlev veri türü olarak tanınır. Bu değer başka bir isime atanabilir ve bu da bir işlev olarak kullanılabilir. Bu genel bir isim değiştirme yolu olabilir:

>>> fib
<function object at 10042ed0>
>>> f = fib # f de fib işlevi olur
>>> f(100)
1 1 2 3 5 8 13 21 34 55 89

fib'in bir işlev olmayıp bir yordam (procedure) olduğunu düşünebilirsiniz. Python'da yordamlar, çağıran işleve değer geri döndürmeyen işlevlerdir. Aslında yordamlar da bir değer geri döndürürler, ama bu sıkıcı bir konudur. Bu değere None denir ve yerleşik bir değişkendir. Yorumlayıcı yazılacak tek değer bu ise normalde None yazmaz. Bunu görmeyi çok istiyorsanız şunu deneyin:

>>> print fib(0)
None

Fibonacci serisini yazdırmak yerine, bunu bir liste şeklinde geri döndüren işlev yazmak basittir:

>>> def fib2(n): # n e kadar fibonacci serisi geri döndürür
...     " n e kadar fibonacci serisi içeren liste geri döndürür"
...     sonuc = []
...     a, b = 0, 1
...     while b < n:
...         sonuc.append(b)    # değeri listeye ekle
...         a, b = b, a+b
...     return sonuc
...
>>> f100 = fib2(100)    # işlevi çağır
>>> f100                # sonucu yazdır
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

Bu örnekte de bazı yeni Python özelliklerini görüyoruz:

  • return deyimi bir işlevden değer geri döndürür. Bağımsız değişkeni olmayan bir return deyimi None geri döndürür. Sona eren bir yordam (procedure) da None geri döndürür.

  • sonuc.append(b) ifadesi sonuç liste nesnesinin bir yöntemini çağırmaktadır. Bir yöntem bir nesneye `ait olan' ve nesne.yöntemAdı şeklinde adlandırılan bir işlevdir. nesne.yöntemAdı ifadesinde nesne herhangi bir nesne (bir ifade de olabilir) ve yöntemAdı da nesnenin türüne bağlı bir yöntemdir. Farklı veri türleri farklı yöntemlere sahiptir. Farklı veri türlerinin ayni isimli yöntemleri olabilir. Sonraki bölümlerde anlatılacağı gibi, kendi veri türlerinizi ve yöntemlerinizi oluşturmanız mümkündür. Yukarıdaki örnekte görülen append() yöntemi liste nesneleri için tanımlıdır ve bir listenin sonuna yeni elemanlar ekler. Bu örnekte bu sonuc = sonuc + [b] ifadesinin yaptığını yapar; ancak daha verimlidir.

4.7. İşlev Tanımları Üzerine Daha Fazla Bilgi

Değişken sayıda bağımsız değişken alan işlevler tanımlamak da mümkündür. Bunun için kullanılan üç yöntem olup bunlar birleştirilerek kullanılabilir.

4.7.1. Bağımsız Değişken Değerlerini Önceden Belirleme

İşlev bağımsız değişkenlerine öntanımlı değerler atamak da mümkündür. Böylece çağıran işlev bu bağımsız değişkenleri sağlamazsa bunlar önceden belirlenmiş öntanımlı değerlerini alır. Örnek:

def onay_al(prompt, denemeler=4, sikayet='Evet veya hayır, lütfen !'):
    while True:
        ok = raw_input(prompt)
        if ok in ('e', 'evet'): return 1
        if ok in ('h', 'hayır'): return 0
        denemeler = denemeler - 1
        if denemeler < 0: raise IOError, 'kararsız kullanıcı'
        print sikayet

Bu işlev onay_al('Programdan çıkmak istiyor musunuz?') ya da onay_al('Dosyayı silmek istiyor musunuz?', 2) şeklinde çağırılabilir.

İşlevin öntanımlı bağımsız değişkenleri işlevin tanımlandığı anda, o an yürürlükte olan etki alanı içinde değerlendirilir. Yani:

i = 7
def f(arg = i):
    print arg

i = 6
f()
7

Uyarı

İşlevin öntanımlı bağımsız değişkenleri sadece bir defa değerlendirilir. Bu durum bağımsız değişkenin liste gibi değiştirilebilir bir nesne olduğu durumlarda farklılık yaratır. Örneğin aşağıdaki işlev ardarda çağırıldığında bağımsız değişkenlerinin değerlerini biriktirir:

def f(a, L = []):
    L.append(a)
    return L
print f(1)
print f(2)
print f(3)

Bu şu çıktıyı verir:

[1]
[1, 2]
[1, 2, 3]

Eğer öntanımlı bağımsız değişken değerlerinin birbirini izleyen çağrılarla paylaşılmasını istemiyorsanız yukarıdaki işlevi şu şekilde yazabilirsiniz:

def f(a, L = None):
    if L is None:
        L = []
    L.append(a)
    return L

4.7.2. Bağımız değişken olarak anahtar kelime

İşlevler anahtar kelime = değer şeklindeki anahtar kelimelerle de çağırılabilir. Örneğin şu işlev:

def otomobil(yakit,  hareket=' uçar', model='Anadol'):
    print "Eğer", yakit, "koyarsan bu", model, hareket

aşağıdaki gibi çağırılabilir:

otomobil('roket yakıtı')
otomobil(hareket = 'dans eder', yakıt = 'zeytin yağı' )
otomobil('ispirto', model = 'Kartal')
otomobil('su','bozulur','Şahin')

Şu çağrılar ise hatalıdır:

otomobil()                           # gerekli bağımsız değişken eksik
otomobil(yakıt = 'su','zeytin yağı') # anahtar kelimeden sonra gelen
                                     # anahtar kelime olmayan bağımsız değişken
otomobil('mazot', yakit = 'benzin')  # aynı bağımsız değişken için iki değer
otomobil(sehir = 'İzmir')            # bilinmeyen anahtar kelime

Genel olarak, bağımsız değişken listesinin başında konuma bağlı bağımsız değişkenler bulunur ve anahtar kelime bağımsız değişkenler onları izler; anahtar kelime adları da işlevin bağımsız değişkenlerinden seçilir. Bağımsız değişkenin öntanımlı değerlerinin olup olmaması önemli değildir. Bir bağımsız değişken birden fazla değer alamaz; konuma bağlı bağımsız değişken isimleri aynı çağrıda anahtar kelime olarak kullanılamaz. İşte bundan dolayı hatalı olan bir örnek:

>>> def function(a):
...     pass
...
>>> function(0, a=0)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: keyword parameter redefined

Eğer işlev tanımındaki son bağımsız değişken **isim şeklinde ise bu bağımsız değişken adları herhangi bir bağımsız değişken olmayan anahtar kelime şeklindeki bağımsız değişkenlerin bulunduğu bir sözlük olur. Bu *isim (bu konu Sözlükler (Çağrışımlı Listeler) bölümünde anlatılacaktır.) şeklindeki bir bağımsız değişken ile de kullanılabilir, ki bu bağımsız değişken listesi içinde bulunmayan konuma bağlı bağımsız değişkenleri içeren bir demet (daha sonra Demetler (tuples) bölümünde incelenecek bir veri türüdür) olur. *isim bağımsız değişkeni **isim bağımsız değişkeninden önce gelmelidir. Buna örnek işlev:

def kasapdukkani(etCinsi,*argumanlar, **anahtarKelimeler):
    print "--", etCinsi, "var mi ?"
    print "-- Maalesef", etCinsi, "kalmadı."
    for arg in argumanlar:
        print arg
    print '-'*40
    anahtarlar = anahtarKelimeler.keys()
    anahtarlar.sort()
    for ak in anahtarlar:
        print ak, ':', anahtarKelimeler[ak]

Şu şekilde çağrılabilir:

kasapdukkani('martı eti',"Çok lezzetli.",
           "Çok satılıyor.",
           musteri = 'Martı Murat',
           kasap = 'Dev İsmail')

ve doğal olarak şu çıktıyı verir:

-- martı eti var mi ?
-- Maalesef martı eti kalmadı.
Çok lezzetli.
Çok satılıyor.
----------------------------------------
kasap : Dev İsmail
musteri : Martı Murat

anahtarKelimeler isimli sözlüğün içeriği yazdırılmadan önce anahtar kelime isimleri listesinin sort() yönteminin çağırıldığına dikkat edin; bu yapılmaz ise bağımsız değişkenlerin hangi sıra ile yazılacağı tanımlanmamış olur.

4.7.3. Keyfî Bağımsız Değişken Listeleri

Son olarak, en ender kullanılan seçenek de keyfî sayıdaki bağımsız değişken ile çağrılabilen bir işlev tanımlamaktır. Bu bağımsız değişkenler bir demet (değişmez liste [tuple]) içine alınır. Keyfî bağımsız değişken listesinden önce sıfır ya da daha fazla normal bağımsız değişken bulunabilir. Örnek:

def fprintf(file, format, *args):
    file.write(format % args)

4.7.4. Lambda Biçemli İşlevler

Yoğun istek üzerine işlevsel dillerde ve Lisp'te bulunan bazı özellikler Python'a eklenmiştir. lambda anahtar kelimesi ile küçük anonim işlevler yazılabilir. İşte iki bağımsız değişkeninin toplamını geri döndüren bir işlev: lambda a, b: a+b.

Lambda işlevleri bir işlev nesnesine ihtiyaç duyulan her yerde kullanılabilir. Sözdizim (syntax) açısından bunlar tek bir ifade ile sınırlandırılmıştır. Anlambilim (semantics) açısından ise normal işlev tanımlamasına getirilen bir sözdizim güzelliğidir. İç içe tanımlanmış işlevlerde olduğu gibi lambda işlevleri de kendilerini kapsayan etki alanindaki değişkenlere erişebilir:

>>> def artirici_yap(n):
...     return lambda x: x + n
...
>>> f = artirici_yap(42)
>>> f(0)
42
>>> f(1)
43

4.7.5. Belgelendirme Dizgeleri

Belgelendirmede kullanılan dizgelerin şekli ve içeriği ile ilgili şartlar yeni yeni oluşmaktadır.

İlk satır daima nesnenin amacının kısa ve öz tanımı olmalıdır. Kısa olması için, nesnenin adından ve türünden bahsedilmemeli; zira bunlar başka yollarla da öğrenilebilir. Bu satır büyük harf ile başlayıp nokta ile bitmelidir.

Eğer belgelendirme dizgesinde birden fazla satır var ise ikinci satır boş olup özet ile açıklamamın devamını birbirinden ayırmalıdır. Diğer satırlar bir ya da daha fazla satır olabilir. Bunlarla nesnenin özellikleri, çağrı şekilleri, yan etkileri vs. açıklanabilir.

Python çözümleyicisi (parser) çok satırlı dizgelerdeki girintileri yok etmez; yani belgeleri işleyen yazılımlar gerekirse bunları atabilir. İlk satırdan sonra gelen ve boş olmayan ilk satırdaki girinti miktarı belgelendirme dizgesinin devamındaki girinti miktarını belirler. Bu girinti miktarına ``eşdeğer'' boşluk diğer satırların başından atılır. Daha az girintili satırlar olmamalı; ama olursa da bunların önündeki boşluğun tamamı atılmalı. Boşluğun eşdeğerliği sekmelerin genişletilmesinden (1 sekme = 8 boşluk) sonra sınanmalıdır.

İşte çok satırlı bir belgelendirme dizgesi örneği:

>>> def benimFonksiyon():
...     """Sadece belgeler.
...
...     Başka birşey yapmaz. Gerçekten !.
...     """
...     pass
...
>>> print benimFonksiyon.__doc__
Sadece belgeler.

   Başka birşey yapmaz. Gerçekten !


[68] Aslında nesne başvurusu ile çağrı daha iyi bir tanım olur, çünkü işleve değiştirilebilir bir nesne aktarılırsa çağıran çağrılanın o nesneye uyguladığı tüm değişiklikleri görür (listeye eklenen elemanlar gibi).