4. Daha Fazla Kontrol Akışı Aracı
*********************************

Az önce tanıtılan "while" deyiminin yanı sıra Python, bazı
değişikliklerle birlikte diğer dillerden bilinen olağan akış kontrol
deyimlerini kullanır.


4.1. "if" İfadeleri
===================

Belki de en iyi bilinen deyim türü "if" deyimidir.  Örneğin:

   >>> x = int(input("Please enter an integer: "))
   Please enter an integer: 42
   >>> if x < 0:
   ...     x = 0
   ...     print('Negative changed to zero')
   ... elif x == 0:
   ...     print('Zero')
   ... elif x == 1:
   ...     print('Single')
   ... else:
   ...     print('More')
   ...
   More

Sıfır veya daha fazla "elif" bölümü olabilir ve "else" bölümü isteğe
bağlıdır.  '"elif"' anahtar sözcüğü 'else if' ifadesinin kısaltmasıdır
ve aşırı girintiden kaçınmak için kullanışlıdır.  Bir "if" ... "elif"
... "elif" ... dizisi, diğer dillerde bulunan "switch" veya "case"
deyimlerinin yerine geçer.


4.2. "for" İfadeleri
====================

Python'daki "for" deyimi, C veya Pascal'da alışkın olduğunuzdan biraz
farklıdır.  Her zaman sayıların aritmetik ilerlemesi üzerinde yineleme
yapmak (Pascal'daki gibi) veya kullanıcıya hem yineleme adımını hem de
durma koşulunu tanımlama yeteneği vermek (C gibi) yerine, Python'un
"for" deyimi, herhangi bir dizinin (bir liste veya bir dize) öğeleri
üzerinde, dizide göründükleri sırayla yineler.  Örneğin (kelime oyunu
yapmak istemedim):

   >>> # Measure some strings:
   ... words = ['cat', 'window', 'defenestrate']
   >>> for w in words:
   ...     print(w, len(w))
   ...
   cat 3
   window 6
   defenestrate 12

Aynı koleksiyon üzerinde yineleme yaparken bir koleksiyonu değiştiren
kodun doğru yazılması zor olabilir.  Bunun yerine, koleksiyonun bir
kopyası üzerinde döngü yapmak veya yeni bir koleksiyon oluşturmak
genellikle daha kolaydır:

   # Strategy:  Iterate over a copy
   for user, status in users.copy().items():
       if status == 'inactive':
           del users[user]

   # Strategy:  Create a new collection
   active_users = {}
   for user, status in users.items():
       if status == 'active':
           active_users[user] = status


4.3. "range()" Fonksiyonu
=========================

Bir sayı dizisi üzerinde yineleme yapmanız gerekiyorsa, yerleşik
"range()" fonksiyonu kullanışlı olur.  Aritmetik ilerlemeler üretir:

   >>> for i in range(5):
   ...     print(i)
   ...
   0
   1
   2
   3
   4

Verilen bitiş noktası asla oluşturulan dizinin bir parçası değildir;
"range(10)" 10 değer üretir, 10 uzunluğundaki bir dizinin öğeleri için
yasal indisler.  Aralığın başka bir sayıdan başlamasına izin vermek
veya farklı bir artış (negatif bile olsa; bazen buna 'adım' denir)
belirtmek mümkündür:

   >>> list(range(5, 10))
   [5, 6, 7, 8, 9]

   >>> list(range(0, 10, 3))
   [0, 3, 6, 9]

   >>> list(range(-10, -100, -30))
   [-10, -40, -70]

Bir dizinin indisleri üzerinde yineleme yapmak için "range()" ve
"len()" öğelerini aşağıdaki gibi birleştirebilirsiniz:

   >>> a = ['Mary', 'had', 'a', 'little', 'lamb']
   >>> for i in range(len(a)):
   ...     print(i, a[i])
   ...
   0 Mary
   1 had
   2 a
   3 little
   4 lamb

Ancak bu tür durumların çoğunda "enumerate()" fonksiyonunu kullanmak
uygundur, bkz Döngü Teknikleri.

Sadece bir aralık yazdırırsanız garip bir şey olur:

   >>> range(10)
   range(0, 10)

Birçok yönden "range()" tarafından döndürülen nesne bir listeymiş gibi
davranır, ancak aslında öyle değildir. Üzerinde yineleme yaptığınızda
istenen dizinin ardışık öğelerini döndüren bir nesnedir, ancak listeyi
gerçekten oluşturmaz, böylece yerden tasarruf sağlar.

Böyle bir nesnenin *iterable* olduğunu, yani arz tükenene kadar
ardışık öğeler elde edebilecekleri bir şey bekleyen fonksiyonlar ve
yapılar için bir hedef olarak uygun olduğunu söylüyoruz.  Daha önce
"for" deyiminin böyle bir yapı olduğunu görmüştük, bir yinelenebilir
alan bir fonksiyon örneği ise "sum()":

   >>> sum(range(4))  # 0 + 1 + 2 + 3
   6

Daha sonra yinelenebilirleri döndüren ve argüman olarak
yinelenebilirleri alan daha fazla fonksiyon göreceğiz. Veri Yapıları
bölümünde, "list()" hakkında daha ayrıntılı olarak tartışacağız.


4.4. "break" ve "continue" İfadeleri ve "else" Döngülerdeki Cümleler
====================================================================

C'de olduğu gibi "break" deyimi, en içteki "for" veya "while"
döngüsünü keser.

Döngü deyimleri bir "else" cümlesine sahip olabilir; bu cümle döngü
yinelenebilirin tükenmesiyle sonlandığında ("for" ile) veya koşul
yanlış olduğunda ("while" ile) çalıştırılır, ancak döngü bir "break"
deyimiyle sonlandırıldığında çalıştırılmaz.  Bu, asal sayıları arayan
aşağıdaki döngü ile örneklendirilmiştir:

   >>> for n in range(2, 10):
   ...     for x in range(2, n):
   ...         if n % x == 0:
   ...             print(n, 'equals', x, '*', n//x)
   ...             break
   ...     else:
   ...         # loop fell through without finding a factor
   ...         print(n, 'is a prime number')
   ...
   2 is a prime number
   3 is a prime number
   4 equals 2 * 2
   5 is a prime number
   6 equals 2 * 3
   7 is a prime number
   8 equals 2 * 4
   9 equals 3 * 3

(Evet, bu doğru koddur.  Yakından bakın: "else" cümlesi "for"
döngüsüne aittir, **değil** "if" deyimine)

Bir döngü ile kullanıldığında, "else" ifadesinin "try" deyiminin
"else" cümlesiyle, "if" deyimlerininkinden daha fazla ortak noktası
vardır: "try" deyiminin "else" cümlesi herhangi bir istisna
oluşmadığında çalışır ve bir döngünün "else" cümlesi herhangi bir
"break" oluşmadığında çalışır. "try" deyimi ve istisnalar hakkında
daha fazla bilgi için Özel Durumları İşleme bölümüne bakınız.

Yine C'den ödünç alınan "continue" deyimi, döngünün bir sonraki
yinelemesiyle devam eder:

   >>> for num in range(2, 10):
   ...     if num % 2 == 0:
   ...         print("Found an even number", num)
   ...         continue
   ...     print("Found an odd number", num)
   ...
   Found an even number 2
   Found an odd number 3
   Found an even number 4
   Found an odd number 5
   Found an even number 6
   Found an odd number 7
   Found an even number 8
   Found an odd number 9


4.5. "pass" İfadeleri
=====================

"pass" deyimi hiçbir şey yapmaz. Sözdizimsel olarak bir deyim
gerektiğinde ancak program hiçbir eylem gerektirmediğinde
kullanılabilir. Örneğin:

   >>> while True:
   ...     pass  # Busy-wait for keyboard interrupt (Ctrl+C)
   ...

Bu genellikle minimal sınıflar oluşturmak için kullanılır:

   >>> class MyEmptyClass:
   ...     pass
   ...

"pass" 'in kullanılabileceği bir başka yer de, yeni kod üzerinde
çalışırken bir fonksiyon veya koşul gövdesi için bir yer tutucu olarak
daha soyut bir düzeyde düşünmeye devam etmenizi sağlamaktır.  "pass"
sessizce göz ardı edilir:

   >>> def initlog(*args):
   ...     pass   # Remember to implement this!
   ...


4.6. Fonksiyonların Tanımlanması
================================

Fibonacci serisini rastgele bir sınıra kadar yazan bir fonksiyon
oluşturabiliriz:

   >>> def fib(n):    # write Fibonacci series up to n
   ...     """Print a Fibonacci series up to n."""
   ...     a, b = 0, 1
   ...     while a < n:
   ...         print(a, end=' ')
   ...         a, b = b, a+b
   ...     print()
   ...
   >>> # Now call the function we just defined:
   ... fib(2000)
   0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597

Anahtar kelime "def" bir fonksiyon *tanımını* tanıtır.  Bunu fonksiyon
adı ve parantez içine alınmış resmi parametreler listesi takip
etmelidir. Fonksiyonun gövdesini oluşturan ifadeler bir sonraki
satırdan başlar ve girintili olmalıdır.

Fonksiyon gövdesinin ilk ifadesi isteğe bağlı olarak bir string
literal olabilir; bu string literal fonksiyonun dokümantasyon stringi
veya *docstring* 'dir. (Docstringler hakkında daha fazla bilgi
Dokümantasyon Stringler'i bölümünde bulunabilir.) Otomatik olarak
çevrimiçi veya basılı dokümantasyon üretmek veya kullanıcının
etkileşimli olarak kodda gezinmesini sağlamak için docstringleri
kullanan araçlar vardır; yazdığınız koda docstringler eklemek iyi bir
uygulamadır, bu yüzden bunu alışkanlık haline getirin.

Bir fonksiyonun *çalıştırılması*, fonksiyonun yerel değişkenleri için
kullanılan yeni bir sembol tablosu ortaya çıkarır.  Daha açık bir
ifadeyle, bir fonksiyon içindeki tüm değişken atamaları değeri yerel
sembol tablosunda saklar; oysa değişken referansları önce yerel sembol
tablosuna, sonra çevreleyen fonksiyonların yerel sembol tablolarına,
daha sonra global sembol tablosuna ve son olarak da yerleşik isimler
tablosuna bakar. Bu nedenle, global değişkenlere ve çevreleyen
fonksiyonların değişkenlerine bir fonksiyon içinde doğrudan değer
atanamaz (global değişkenler için bir "global" deyiminde veya
çevreleyen fonksiyonların değişkenleri için bir "nonlocal" deyiminde
isimlendirilmedikçe), ancak bunlara referans verilebilir.

Bir fonksiyon çağrısının gerçek parametreleri (argümanları),
çağrıldığında çağrılan fonksiyonun yerel sembol tablosunda tanıtılır;
bu nedenle, argümanlar *call by value* (burada *value* her zaman bir
nesne *referans*'dır, nesnenin değeri değildir) kullanılarak
aktarılır. [1] Bir fonksiyon başka bir fonksiyonu çağırdığında veya
kendini tekrarlı olarak çağırdığında, bu çağrı için yeni bir yerel
sembol tablosu oluşturulur.

Bir fonksiyon tanımı, fonksiyon adını geçerli sembol tablosundaki
fonksiyon nesnesiyle ilişkilendirir.  Yorumlayıcı, bu adın işaret
ettiği nesneyi kullanıcı tanımlı bir fonksiyon olarak tanır.  Diğer
isimler de aynı fonksiyon nesnesine işaret edebilir ve fonksiyona
erişmek için kullanılabilir:

   >>> fib
   <function fib at 10042ed0>
   >>> f = fib
   >>> f(100)
   0 1 1 2 3 5 8 13 21 34 55 89

Diğer dillerden geliyorsanız, "fib" 'in bir fonksiyon değil, değer
döndürmediği için bir prosedür olduğuna itiraz edebilirsiniz.
Aslında, "return" ifadesi olmayan fonksiyonlar bile, oldukça sıkıcı
olsa da, bir değer döndürürler.  Bu değer "None" olarak adlandırılır
(yerleşik bir isimdir).  Normalde "None" değerinin yazılması, yazılan
tek değer olacaksa yorumlayıcı tarafından bastırılır. Eğer gerçekten
istiyorsanız "print()" kullanarak görebilirsiniz:

   >>> fib(0)
   >>> print(fib(0))
   None

Fibonacci serisindeki sayıların listesini döndürebilecek bir fonksiyon
yazmak gayet basittir, onun yerine şunu yazdırarak:

   >>> def fib2(n):  # return Fibonacci series up to n
   ...     """Return a list containing the Fibonacci series up to n."""
   ...     result = []
   ...     a, b = 0, 1
   ...     while a < n:
   ...         result.append(a)    # see below
   ...         a, b = b, a+b
   ...     return result
   ...
   >>> f100 = fib2(100)    # call it
   >>> f100                # write the result
   [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

Bu örnek, her zamanki gibi, bazı yeni Python özelliklerini
göstermektedir:

* Bir "return" deyimi bir fonksiyondan bir değerle döner. "return"
  deyimi bir ifade argümanı olmadan "None" döndürür. Bir fonksiyonun
  sonundan düşmek de "None" değerini döndürür.

* "result.append(a)" ifadesi "result" liste nesnesinin bir *metodunu*
  çağırır.  Bir yöntem, bir nesneye 'ait' olan ve "obj.methodname"
  olarak adlandırılan bir işlevdir; burada "obj" bir nesnedir (bu bir
  ifade olabilir) ve "methodname" nesnenin türü tarafından tanımlanan
  bir yöntemin adıdır. Farklı türler farklı yöntemler tanımlar.
  Farklı türlerdeki yöntemler, belirsizliğe neden olmadan aynı ada
  sahip olabilir.  (*classes* kullanarak kendi nesne türlerinizi ve
  yöntemlerinizi tanımlamak mümkündür, bkz Sınıflar) Örnekte
  gösterilen "append()" yöntemi liste nesneleri için tanımlanmıştır;
  listenin sonuna yeni bir öğe ekler.  Bu örnekte "result = result +
  [a]" ile eşdeğerdir, ancak daha verimlidir.


4.7. İşlev Tanımlama hakkında daha fazla bilgi
==============================================

Değişken sayıda argüman içeren fonksiyonlar tanımlamak da mümkündür.
Birleştirilebilen üç form vardır.


4.7.1. Varsayılan Değişken Değerleri
------------------------------------

En kullanışlı biçim, bir veya daha fazla bağımsız değişken için
varsayılan bir değer belirtmektir. Bu, izin vermek üzere
tanımlandığından daha az sayıda bağımsız değişkenle çağrılabilen bir
fonksiyon oluşturur.  Örneğin:

   def ask_ok(prompt, retries=4, reminder='Please try again!'):
       while True:
           ok = input(prompt)
           if ok in ('y', 'ye', 'yes'):
               return True
           if ok in ('n', 'no', 'nop', 'nope'):
               return False
           retries = retries - 1
           if retries < 0:
               raise ValueError('invalid user response')
           print(reminder)

Bu fonksiyon çeşitli yollarla çağrılabilir:

* sadece zorunlu argümanı vererek: "ask_ok('Gerçekten çıkmak istiyor
  musun?')"

* isteğe bağlı değişkenlerden birini vermek: "ask_ok('OK to overwrite
  the file?', 2)"

* ya da bütün değişkenleri vermek: "ask_ok('OK to overwrite the
  file?', 2, 'Come on, only yes or no!')"

Bu örnek ayrıca "in" anahtar sözcüğünü de tanıtır. Bu, bir dizinin
belirli bir değer içerip içermediğini test eder.

Varsayılan değerler *tanımlayan* kapsamdaki fonksiyon tanımlama
noktasında değerlendirilir, böylece

   i = 5

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

   i = 6
   f()

"5" çıktısını verecektir.

**Önemli uyarı:** Varsayılan değer yalnızca bir kez değerlendirilir.
Varsayılan değer liste, sözlük veya çoğu sınıfın örnekleri gibi
değiştirilebilir bir nesne olduğunda bu durum fark yaratır.  Örneğin,
aşağıdaki fonksiyon sonraki çağrılarda kendisine aktarılan argümanları
biriktirir:

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

   print(f(1))
   print(f(2))
   print(f(3))

Bu şu çıktıyı verecektir

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

Varsayılan değerin sonraki çağrılar arasında paylaşılmasını
istemiyorsanız, bunun yerine fonksiyonu şu şekilde yazabilirsiniz:

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


4.7.2. Anahtar Kelime Değişkenleri
----------------------------------

Fonksiyonlar ayrıca "kwarg=value" şeklinde *anahtar kelime
argümanları* kullanılarak da çağrılabilir.  Örneğin, aşağıdaki
fonksiyon:

   def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
       print("-- This parrot wouldn't", action, end=' ')
       print("if you put", voltage, "volts through it.")
       print("-- Lovely plumage, the", type)
       print("-- It's", state, "!")

bir gerekli argüman ("voltage") ve üç isteğe bağlı argüman ("state",
"action" ve "type") kabul eder.  Bu fonksiyon aşağıdaki yollardan
herhangi biriyle çağrılabilir:

   parrot(1000)                                          # 1 positional argument
   parrot(voltage=1000)                                  # 1 keyword argument
   parrot(voltage=1000000, action='VOOOOOM')             # 2 keyword arguments
   parrot(action='VOOOOOM', voltage=1000000)             # 2 keyword arguments
   parrot('a million', 'bereft of life', 'jump')         # 3 positional arguments
   parrot('a thousand', state='pushing up the daisies')  # 1 positional, 1 keyword

ancak aşağıdaki tüm çağrılar geçersiz olacaktır:

   parrot()                     # required argument missing
   parrot(voltage=5.0, 'dead')  # non-keyword argument after a keyword argument
   parrot(110, voltage=220)     # duplicate value for the same argument
   parrot(actor='John Cleese')  # unknown keyword argument

Bir fonksiyon çağrısında, anahtar kelime argümanları konumsal
argümanları takip etmelidir. Aktarılan tüm anahtar sözcük argümanları
fonksiyon tarafından kabul edilen argümanlardan biriyle eşleşmelidir
(örneğin "actor" "parrot" fonksiyonu için geçerli bir argüman
değildir) ve sıraları önemli değildir.  Buna isteğe bağlı olmayan
argümanlar da dahildir (örneğin "parrot(voltage=1000)" da geçerlidir).
Hiçbir argüman birden fazla değer alamaz. İşte bu kısıtlama nedeniyle
başarısız olan bir örnek:

   >>> def function(a):
   ...     pass
   ...
   >>> function(0, a=0)
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: function() got multiple values for argument 'a'

"**name" biçiminde bir son biçimsel parametre mevcut olduğunda,
biçimsel parametreye karşılık gelenler dışındaki tüm anahtar kelime
argümanlarını içeren bir sözlük alır (bkz. Mapping Types --- dict).
Bu, biçimsel parametre tuple listesinin ötesindeki konumsal
argümanları içeren bir "*name" biçimindeki bir biçimsel parametre ile
birleştirilebilir (bir sonraki alt bölümde açıklanmıştır).  ("*name",
"**name" 'den önce gelmelidir.) Örneğin, aşağıdaki gibi bir fonksiyon
tanımlarsak:

   def cheeseshop(kind, *arguments, **keywords):
       print("-- Do you have any", kind, "?")
       print("-- I'm sorry, we're all out of", kind)
       for arg in arguments:
           print(arg)
       print("-" * 40)
       for kw in keywords:
           print(kw, ":", keywords[kw])

Şöyle denebilir:

   cheeseshop("Limburger", "It's very runny, sir.",
              "It's really very, VERY runny, sir.",
              shopkeeper="Michael Palin",
              client="John Cleese",
              sketch="Cheese Shop Sketch")

ve tabii ki yazdıracaktır:

   -- Do you have any Limburger ?
   -- I'm sorry, we're all out of Limburger
   It's very runny, sir.
   It's really very, VERY runny, sir.
   ----------------------------------------
   shopkeeper : Michael Palin
   client : John Cleese
   sketch : Cheese Shop Sketch

Anahtar sözcük bağımsız değişkenlerinin yazdırılma sırasının,
fonksiyon çağrısında sağlandıkları sırayla eşleşmesinin garanti
edildiğini unutmayın.


4.7.3. Özel parametreler
------------------------

Varsayılan olarak, argümanlar bir Python fonksiyonuna ya pozisyona
göre ya da açıkça anahtar kelimeye göre aktarılabilir. Okunabilirlik
ve performans için, argümanların geçirilme şeklini kısıtlamak
mantıklıdır, böylece bir geliştiricinin öğelerin konumla mı, konumla
ya da anahtar sözcükle mi yoksa anahtar sözcükle mi geçirildiğini
belirlemek için yalnızca fonksiyon tanımına bakması gerekir.

Bir fonksiyon tanımı aşağıdaki gibi görünebilir:

   def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
         -----------    ----------     ----------
           |             |                  |
           |        Positional or keyword   |
           |                                - Keyword only
            -- Positional only

burada "/" ve "*" isteğe bağlıdır. Kullanılırsa, bu semboller,
argümanların fonksiyona nasıl geçirilebileceğine göre parametre türünü
gösterir: yalnızca konumsal, konumsal veya anahtar sözcük ve yalnızca
anahtar sözcük. Anahtar sözcük parametreleri, adlandırılmış
parametreler olarak da adlandırılır.


4.7.3.1. Konumsal veya Anahtar Kelime Argümanları
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Eğer "/" ve "*" fonksiyon tanımında mevcut değilse, argümanlar bir
fonksiyona pozisyon veya anahtar kelime ile aktarılabilir.


4.7.3.2. Yalnızca Konumsal Parametreler
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Bu konuya biraz daha detaylı bakacak olursak, belirli parametreleri
*positional-only* olarak işaretlemek mümkündür. Eğer *konumsal-sadece*
ise, parametrelerin sırası önemlidir ve parametreler anahtar kelime
ile aktarılamaz. Yalnızca konumsal parametreler bir "/" (ileri eğik
çizgi) önüne yerleştirilir. "/" sadece konumsal parametreleri diğer
parametrelerden mantıksal olarak ayırmak için kullanılır. Fonksiyon
tanımında "/" yoksa, sadece konumsal parametre yoktur.

"/" işaretini takip eden parametreler *konumsal veya anahtar sözcük*
veya *sadece anahtar sözcük* olabilir.


4.7.3.3. Yalnızca Anahtar Sözcük İçeren Değişkenler
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Parametrelerin anahtar sözcük argümanıyla geçirilmesi gerektiğini
belirterek parametreleri *anahtar sözcüğe özel* olarak işaretlemek
için, argüman listesine ilk *anahtar sözcüğe özel* parametreden hemen
önce bir "*" yerleştirin.


4.7.3.4. Fonksiyon Örnekleri
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

"/" ve "*" işaretlerine çok dikkat ederek aşağıdaki örnek fonksiyon
tanımlarını göz önünde bulundurun:

   >>> def standard_arg(arg):
   ...     print(arg)
   ...
   >>> def pos_only_arg(arg, /):
   ...     print(arg)
   ...
   >>> def kwd_only_arg(*, arg):
   ...     print(arg)
   ...
   >>> def combined_example(pos_only, /, standard, *, kwd_only):
   ...     print(pos_only, standard, kwd_only)

İlk fonksiyon tanımı, "standard_arg", en bilinen biçimdir, çağırma
kuralına herhangi bir kısıtlama getirmez ve argümanlar konum veya
anahtar kelime ile aktarılabilir:

   >>> standard_arg(2)
   2

   >>> standard_arg(arg=2)
   2

İkinci fonksiyon "pos_only_arg", fonksiyon tanımında bir "/" olduğu
için sadece konumsal parametreleri kullanacak şekilde
sınırlandırılmıştır:

   >>> pos_only_arg(1)
   1

   >>> pos_only_arg(arg=1)
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: pos_only_arg() got some positional-only arguments passed as keyword arguments: 'arg'

Üçüncü fonksiyon "kwd_only_args" sadece fonksiyon tanımında "*" ile
belirtilen anahtar kelime argümanlarına izin verir:

   >>> kwd_only_arg(3)
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: kwd_only_arg() takes 0 positional arguments but 1 was given

   >>> kwd_only_arg(arg=3)
   3

Sonuncusu ise aynı fonksiyon tanımında üç çağrı kuralını da kullanır:

   >>> combined_example(1, 2, 3)
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: combined_example() takes 2 positional arguments but 3 were given

   >>> combined_example(1, 2, kwd_only=3)
   1 2 3

   >>> combined_example(1, standard=2, kwd_only=3)
   1 2 3

   >>> combined_example(pos_only=1, standard=2, kwd_only=3)
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: combined_example() got some positional-only arguments passed as keyword arguments: 'pos_only'

Son olarak, "name" konumsal argümanı ile "name" anahtarına sahip
"**kwds" arasında potansiyel bir çakışma olan bu fonksiyon tanımını
düşünün:

   def foo(name, **kwds):
       return 'name' in kwds

Anahtar kelime "'name'" her zaman ilk parametreye bağlanacağı için
"True" döndürmesini sağlayacak olası bir çağrı yoktur. Örneğin:

   >>> foo(1, **{'name': 2})
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: foo() got multiple values for argument 'name'
   >>>

Ancak "/" (yalnızca konumsal argümanlar) kullanıldığında, "name" bir
konumsal argüman olarak ve "'name'" anahtar kelime argümanlarında bir
anahtar olarak izin verdiği için mümkündür:

   def foo(name, /, **kwds):
       return 'name' in kwds
   >>> foo(1, **{'name': 2})
   True

Başka bir deyişle, yalnızca konumsal parametrelerin adları "**kwds"
içinde belirsizlik olmadan kullanılabilir.


4.7.3.5. Özet
~~~~~~~~~~~~~

Kullanım durumu, fonksiyon tanımında hangi parametrelerin
kullanılacağını belirleyecektir:

   def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):

Rehber olarak:

* Parametrelerin adının kullanıcı tarafından kullanılamamasını
  istiyorsanız sadece pozisyonel seçeneğini kullanın. Bu, parametre
  adlarının gerçek bir anlamı olmadığında, fonksiyon çağrıldığında
  bağımsız değişkenlerin sırasını zorlamak istediğinizde veya bazı
  konumsal parametreler ve rastgele anahtar sözcükler almanız
  gerektiğinde kullanışlıdır.

* Adların bir anlamı olduğunda ve fonksiyon tanımının adlarla açık
  olmasıyla daha anlaşılır olduğunda veya kullanıcıların geçirilen
  argümanın konumuna güvenmesini önlemek istediğinizde yalnızca
  anahtar sözcük kullanın.

* Bir API için, parametrenin adı gelecekte değiştirilirse API
  değişikliklerinin bozulmasını önlemek için yalnızca konumsal
  kullanın.


4.7.4. Keyfi Argüman Listeleri
------------------------------

Son olarak, en az kullanılan seçenek, bir fonksiyonun rastgele sayıda
argümanla çağrılabileceğini belirtmektir.  Bu argümanlar bir tuple
içinde paketlenecektir (bkz Veri Grupları ve Diziler).  Değişken
argüman sayısından önce, sıfır veya daha fazla normal argüman
olabilir.

   def write_multiple_items(file, separator, *args):
       file.write(separator.join(args))

Normally, these *variadic* arguments will be last in the list of
formal parameters, because they scoop up all remaining input arguments
that are passed to the function. Any formal parameters which occur
after the "*args" parameter are 'keyword-only' arguments, meaning that
they can only be used as keywords rather than positional arguments.

   >>> def concat(*args, sep="/"):
   ...     return sep.join(args)
   ...
   >>> concat("earth", "mars", "venus")
   'earth/mars/venus'
   >>> concat("earth", "mars", "venus", sep=".")
   'earth.mars.venus'


4.7.5. Argüman Listelerini Açma
-------------------------------

Tersi durum, argümanlar zaten bir liste veya tuple içinde olduğunda,
ancak ayrı konumsal argümanlar gerektiren bir fonksiyon çağrısı için
paketten çıkarılması gerektiğinde ortaya çıkar.  Örneğin, yerleşik
"range()" fonksiyonu ayrı *start* ve *stop* argümanları bekler.  Eğer
bunlar ayrı olarak mevcut değilse, argümanları bir listeden veya
tuple'dan çıkarmak için fonksiyon çağrısını "*"-operatörü ile yazın:

   >>> list(range(3, 6))            # normal call with separate arguments
   [3, 4, 5]
   >>> args = [3, 6]
   >>> list(range(*args))            # call with arguments unpacked from a list
   [3, 4, 5]

Aynı şekilde, sözlükler "**"-operatörü ile anahtar sözcük argümanları
sunabilir:

   >>> def parrot(voltage, state='a stiff', action='voom'):
   ...     print("-- This parrot wouldn't", action, end=' ')
   ...     print("if you put", voltage, "volts through it.", end=' ')
   ...     print("E's", state, "!")
   ...
   >>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
   >>> parrot(**d)
   -- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !


4.7.6. Lambda İfadeleri
-----------------------

Küçük anonim fonksiyonlar "lambda" anahtar sözcüğü ile
oluşturulabilir. Bu fonksiyon iki argümanının toplamını döndürür:
"lambda a, b: a+b". Lambda fonksiyonları, fonksiyon nesnelerinin
gerekli olduğu her yerde kullanılabilir.  Sözdizimsel olarak tek bir
ifadeyle sınırlıdırlar.  Anlamsal olarak, normal bir fonksiyon tanımı
için sadece sözdizimsel şekerdirler.  İç içe işlev tanımları gibi,
lambda işlevleri de içeren kapsamdaki değişkenlere başvurabilir:

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

Yukarıdaki örnekte bir fonksiyon döndürmek için bir lambda ifadesi
kullanılmıştır.  Başka bir kullanım da küçük bir fonksiyonu argüman
olarak geçirmektir:

   >>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
   >>> pairs.sort(key=lambda pair: pair[1])
   >>> pairs
   [(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]


4.7.7. Dokümantasyon Stringler'i
--------------------------------

Belge dizelerinin içeriği ve biçimlendirilmesiyle ilgili bazı kurallar
aşağıda verilmiştir.

İlk satır her zaman nesnenin amacının kısa ve öz bir özeti olmalıdır.
Öz olması için, nesnenin adı veya türü açıkça belirtilmemelidir, çünkü
bunlar başka yollarla elde edilebilir (adın bir fonksiyonun
çalışmasını açıklayan bir fiil olması durumu hariç).  Bu satır büyük
harfle başlamalı ve nokta ile bitmelidir.

Belgeleme string'inde daha fazla satır varsa, ikinci satır boş olmalı
ve özeti açıklamanın geri kalanından görsel olarak ayırmalıdır.
Sonraki satırlar, nesnenin çağrı kurallarını, yan etkilerini vb.
açıklayan bir veya daha fazla paragraftan oluşmalıdır.

Python ayrıştırıcısı, Python'daki çok satırlı dize değişmezlerinden
girintiyi çıkarmaz, bu nedenle belgeleri işleyen araçların istenirse
girintiyi çıkarması gerekir.  Bu, aşağıdaki kural kullanılarak
yapılır. Dizenin ilk satırından *sonraki* boş olmayan ilk satır, tüm
dokümantasyon dizesi için girinti miktarını belirler.  (İlk satırı
kullanamayız, çünkü genellikle dizenin açılış tırnaklarına bitişiktir,
bu nedenle girintisi dize değişmezinde belirgin değildir)  Bu
girintiye "eşdeğer" boşluk daha sonra dizenin tüm satırlarının
başlangıcından çıkarılır.  Daha az girintili satırlar oluşmamalıdır,
ancak oluşurlarsa başlarındaki tüm boşluklar çıkarılmalıdır.  Beyaz
boşlukların eşdeğerliği sekmelerin genişletilmesinden sonra test
edilmelidir (normalde 8 boşluğa kadar).

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

   >>> def my_function():
   ...     """Do nothing, but document it.
   ...
   ...     No, really, it doesn't do anything.
   ...     """
   ...     pass
   ...
   >>> print(my_function.__doc__)
   Do nothing, but document it.

       No, really, it doesn't do anything.


4.7.8. Fonksiyon Ek Açıklamaları
--------------------------------

Fonksiyon ek açıklamaları kullanıcı tanımlı fonksiyonlar tarafından
kullanılan tipler hakkında tamamen isteğe bağlı meta veri bilgileridir
(daha fazla bilgi için **PEP 3107** ve **PEP 484** sayfalarına
bakınız).

*İşaretlemeler*, fonksiyonun "__annotations__" özelliğinde bir sözlük
olarak saklanır ve fonksiyonun diğer bölümleri üzerinde hiçbir etkisi
yoktur.  Parametre ek açıklamaları, parametre adından sonra iki nokta
üst üste işareti ve ardından ek açıklamanın değerine göre
değerlendirilen bir ifade ile tanımlanır.  Dönüş ek açıklamaları,
parametre listesi ile "def" ifadesinin sonunu belirten iki nokta
arasında bir "->" ifadesi ve ardından bir ifade ile tanımlanır.
Aşağıdaki örnekte bir gerekli argüman, bir isteğe bağlı argüman ve
dönüş değeri ek açıklamalıdır:

   >>> def f(ham: str, eggs: str = 'eggs') -> str:
   ...     print("Annotations:", f.__annotations__)
   ...     print("Arguments:", ham, eggs)
   ...     return ham + ' and ' + eggs
   ...
   >>> f('spam')
   Annotations: {'ham': <class 'str'>, 'return': <class 'str'>, 'eggs': <class 'str'>}
   Arguments: spam eggs
   'spam and eggs'


4.8. Intermezzo: Kodlama Stili
==============================

Artık daha uzun, daha karmaşık Python parçaları yazmak üzere
olduğunuza göre, *kodlama stili* hakkında konuşmak için iyi bir zaman.
Çoğu dil farklı stillerde yazılabilir (ya da daha özlü bir ifadeyle
*biçimlendirilebilir*); bazıları diğerlerinden daha okunaklıdır.
Başkalarının kodunuzu okumasını kolaylaştırmak her zaman iyi bir
fikirdir ve güzel bir kodlama stili benimsemek buna çok yardımcı olur.

Python için **PEP 8**, çoğu projenin bağlı olduğu stil kılavuzu olarak
ortaya çıkmıştır; okunabilir ve göze hoş gelen bir kodlama stilini
teşvik eder.  Her Python geliştiricisi bir noktada onu okumalıdır;
işte sizin için çıkarılan en önemli noktalar:

* 4 aralıklı girinti kullanın ve sekme kullanmayın.

  4 boşluk, küçük girinti (daha fazla iç içe geçme derinliği sağlar)
  ve büyük girinti (okunması daha kolay) arasında iyi bir uzlaşmadır.
  Sekmeler karışıklığa neden olur ve en iyisi dışarıda bırakmaktır.

* Satırları 79 karakteri geçmeyecek şekilde sarın.

  Bu, küçük ekranlı kullanıcılara yardımcı olur ve daha büyük
  ekranlarda birkaç kod dosyasının yan yana olmasını mümkün kılar.

* Fonksiyonları ve sınıfları ve fonksiyonların içindeki büyük kod
  bloklarını ayırmak için boş satırlar kullanın.

* Mümkün olduğunda, yorumları kendi başlarına bir satıra koyun.

* Docstrings kullanın.

* Operatörlerin etrafında ve virgüllerden sonra boşluk kullanın, ancak
  doğrudan parantez yapılarının içinde kullanmayın: "a = f(1, 2) +
  g(3, 4)".

* Sınıflarınızı ve fonksiyonlarınızı tutarlı bir şekilde adlandırın;
  buradaki kural, sınıflar için "UpperCamelCase", fonksiyonlarını;
  metotlar için de "lowercase_with_underscores" kullanmaktır.  İlk
  yöntem argümanının adı olarak her zaman "self" kullanın (sınıflar ve
  yöntemler hakkında daha fazla bilgi için Sınıflara İlk Bakış
  bölümüne bakın).

* Kodunuz uluslararası ortamlarda kullanılacaksa süslü kodlamalar
  kullanmayın.  Python'un varsayılanı, UTF-8 veya hatta düz ASCII her
  durumda en iyi sonucu verir.

* Aynı şekilde, farklı bir dil konuşan kişilerin kodu okuması veya
  muhafaza etmesi için en ufak bir şans varsa, tanımlayıcılarda ASCII
  olmayan karakterler kullanmayın.

-[ Dipnotlar ]-

[1] Aslında, *nesne referansı ile çağırma* daha iyi bir tanımlama
    olacaktır, çünkü değiştirilebilir bir nesne aktarılırsa, çağıran,
    çağırılanın üzerinde yaptığı tüm değişiklikleri (bir listeye
    eklenen öğeler) görecektir.
