4. Daha Fazla Kontrol Akışı Aracı¶
As well as the while
statement just introduced, Python uses a few more
that we will encounter in this chapter.
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.
Aynı değeri birkaç sabitle karşılaştırıyorsanız veya belirli türleri veya nitelikleri kontrol ediyorsanız, match
deyimini de yararlı bulabilirsiniz. Daha fazla ayrıntı için pass İfadeleri bölümüne bakınız.
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:
# Create a sample collection
users = {'Hans': 'active', 'Éléonore': 'inactive', '景太郎': 'active'}
# 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
and continue
Statements¶
The break
statement breaks out of the innermost enclosing
for
or while
loop:
>>> for n in range(2, 10):
... for x in range(2, n):
... if n % x == 0:
... print(f"{n} equals {x} * {n//x}")
... break
...
4 equals 2 * 2
6 equals 2 * 3
8 equals 2 * 4
9 equals 3 * 3
The continue
statement continues with the next
iteration of the loop:
>>> for num in range(2, 10):
... if num % 2 == 0:
... print(f"Found an even number {num}")
... continue
... print(f"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. else
Clauses on Loops¶
In a for
or while
loop the break
statement
may be paired with an else
clause. If the loop finishes without
executing the break
, the else
clause executes.
In a for
loop, the else
clause is executed
after the loop finishes its final iteration, that is, if no break occurred.
In a while
loop, it’s executed after the loop’s condition becomes false.
In either kind of loop, the else
clause is not executed if the
loop was terminated by a break
. Of course, other ways of ending the
loop early, such as a return
or a raised exception, will also skip
execution of the else
clause.
This is exemplified in the following for
loop,
which searches for prime numbers:
>>> 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
(Yes, this is the correct code. Look closely: the else
clause belongs to
the for
loop, not the if
statement.)
One way to think of the else clause is to imagine it paired with the if
inside the loop. As the loop executes, it will run a sequence like
if/if/if/else. The if
is inside the loop, encountered a number of times. If
the condition is ever true, a break
will happen. If the condition is never
true, the else
clause outside the loop will execute.
When used with a loop, the else
clause has more in common with the else
clause of a try
statement than it does with that of if
statements: a try
statement’s else
clause runs when no exception
occurs, and a loop’s else
clause runs when no break
occurs. For more on
the try
statement and exceptions, see Özel Durumları İşleme.
4.6. 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.7. pass
İfadeleri¶
match
bir ifadeyi alır ve değerini bir veya daha fazla case bloğu olarak verilen ardışık kalıplarla karşılaştırır. Bu, C, Java veya JavaScript’teki (ve diğer birçok dildeki) bir switch ifadesine yüzeysel olarak benzer, ancak Rust veya Haskell gibi dillerdeki kalıp eşleştirmeye daha çok benzer. Yalnızca eşleşen ilk kalıp yürütülür ve ayrıca bileşenleri (sıra öğeleri veya nesne nitelikleri) değerden değişkenlere çıkarabilir.
En basit form, bir konu değerini bir veya daha fazla sabitle karşılaştırır:
def http_error(status):
match status:
case 400:
return "Bad request"
case 404:
return "Not found"
case 418:
return "I'm a teapot"
case _:
return "Something's wrong with the internet"
Son bloğa dikkat edin: “değişken adı” _
bir wildcard görevi görür ve asla eşleşmez. Hiçbir durum eşleşmezse, dallardan hiçbiri yürütülmez.
|
(“or”) kullanarak birkaç sabiti tek bir kalıpta birleştirebilirsiniz:
case 401 | 403 | 404:
return "Not allowed"
Kalıplar paket açma atamaları gibi görünebilir ve değişkenleri bağlamak için kullanılabilir:
# point is an (x, y) tuple
match point:
case (0, 0):
print("Origin")
case (0, y):
print(f"Y={y}")
case (x, 0):
print(f"X={x}")
case (x, y):
print(f"X={x}, Y={y}")
case _:
raise ValueError("Not a point")
Bunu dikkatle inceleyin! İlk kalıpta iki sabit vardır ve yukarıda gösterilen sabit kalıbının bir uzantısı olarak düşünülebilir. Ancak sonraki iki kalıp bir sabit ve bir değişkeni birleştirir ve değişken özneden (point
) bir değer bağlar. Dördüncü kalıp iki değeri yakalar, bu da onu kavramsal olarak (x, y) = point
paket açma atamasına benzer hale getirir.
Verilerinizi yapılandırmak için sınıfları kullanıyorsanız, sınıf adını ve ardından bir yapıcıya benzeyen, ancak nitelikleri değişkenlere yakalama yeteneğine sahip bir argüman listesi kullanabilirsiniz:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def where_is(point):
match point:
case Point(x=0, y=0):
print("Origin")
case Point(x=0, y=y):
print(f"Y={y}")
case Point(x=x, y=0):
print(f"X={x}")
case Point():
print("Somewhere else")
case _:
print("Not a point")
Konumsal parametreleri, nitelikleri için bir sıralama sağlayan bazı yerleşik sınıflarla (örneğin veri sınıfları) kullanabilirsiniz. Ayrıca sınıflarınızda __match_args__
niteliğini ayarlayarak kalıplardaki nitelikler için belirli bir konum tanımlayabilirsiniz. Bu özellik (“x”, “y”) olarak ayarlanırsa, bahsi geçen kalıpların hepsi eş değerdir (ve hepsi y
niteliğini var
değişkenine bağlar):
Point(1, var)
Point(1, y=var)
Point(x=1, y=var)
Point(y=var, x=1)
Kalıpları okumak için önerilen bir yol, hangi değişkenlerin neye ayarlanacağını anlamak için onlara bir atamanın soluna koyacağınız şeyin genişletilmiş bir biçimi olarak bakmaktır. Yalnızca bağımsız isimler (yukarıdaki var
gibi) bir eşleştirme deyimi tarafından atanır. Noktalı isimlere (foo.bar
gibi), nitelik isimlerine (yukarıdaki x=
ve y=
gibi) veya sınıf isimlerine (yukarıdaki Point
gibi yanlarındaki “(…)” ile tanınan) asla atama yapılmaz.
Patterns can be arbitrarily nested. For example, if we have a short
list of Points, with __match_args__
added, we could match it like this:
class Point:
__match_args__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
match points:
case []:
print("No points")
case [Point(0, 0)]:
print("The origin")
case [Point(x, y)]:
print(f"Single point {x}, {y}")
case [Point(0, y1), Point(0, y2)]:
print(f"Two on the Y axis at {y1}, {y2}")
case _:
print("Something else")
Bir kalıba “guard” olarak bilinen bir if
cümlesi ekleyebiliriz. Eğer guard yanlış ise, match
bir sonraki case bloğunu denemeye devam eder. Değer yakalamanın koruma değerlendirilmeden önce gerçekleştiğine dikkat edin:
match point:
case Point(x, y) if x == y:
print(f"Y=X at {x}")
case Point(x, y):
print(f"Not on the diagonal")
Bu açıklamanın diğer bazı kilit özellikleri:
Paket açma atamaları gibi, tuple ve liste kalıpları da tamamen aynı anlama sahiptir ve aslında rastgele dizilerle eşleşir. Önemli bir istisna, yineleyicilerle veya string’lerle eşleşmezler.
Sıra kalıpları genişletilmiş paket açmayı destekler:
[x, y, *rest]
ve(x, y, *rest)
paket açma atamalarına benzer şekilde çalışır.*
öğesinden sonraki ad_
de olabilir, bu nedenle(x, y, *_)
öğesi, kalan öğeleri bağlamadan en az iki öğeden oluşan bir diziyle eşleşir.Eşleme kalıpları:
{"bandwidth": b, "latency": l}
bir sözlükten"bandwidth"
ve"latency"
değerlerini yakalar. Sıra kalıplarının aksine, ekstra anahtarlar göz ardı edilir.**rest
gibi bir paket açma da desteklenir. (Ancak**_
gereksiz olacağından buna izin verilmez)Alt kalıplar
as
anahtar sözcüğü kullanılarak yakalanabilir:case (Point(x1, y1), Point(x2, y2) as p2): ...
girdinin ikinci elemanını
p2
olarak yakalayacaktır (girdi iki noktadan oluşan bir dizi olduğu sürece)Çoğu sabit eşitlikle karşılaştırılır, ancak
True
,False
veNone
tekilleri özdeşlikle karşılaştırılır.Kalıplar adlandırılmış sabitler kullanabilir. Bunlar, yakalama değişkeni olarak yorumlanmalarını önlemek için noktalı isimler olmalıdır:
from enum import Enum class Color(Enum): RED = 'red' GREEN = 'green' BLUE = 'blue' color = Color(input("Enter your choice of 'red', 'blue' or 'green': ")) match color: case Color.RED: print("I see red!") case Color.GREEN: print("Grass is green") case Color.BLUE: print("I'm feeling the blues :(")
Daha ayrıntılı bir açıklama ve ek örnekler için, öğretici bir formatta yazılmış olan PEP 636 sayfasına bakabilirsiniz.
4.8. Fonksiyonların Tanımlanması¶
Fibonacci serisini rastgele bir sınıra kadar yazan bir fonksiyon oluşturabiliriz:
>>> def fib(n): # write Fibonacci series less than n
... """Print a Fibonacci series less than 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ı olmadanNone
döndürür. Bir fonksiyonun sonundan düşmek deNone
değerini döndürür.The statement
result.append(a)
calls a method of the list objectresult
. A method is a function that ‘belongs’ to an object and is namedobj.methodname
, whereobj
is some object (this may be an expression), andmethodname
is the name of a method that is defined by the object’s type. Different types define different methods. Methods of different types may have the same name without causing ambiguity. (It is possible to define your own object types and methods, using classes, see Sınıflar) The methodappend()
shown in the example is defined for list objects; it adds a new element at the end of the list. In this example it is equivalent toresult = result + [a]
, but more efficient.
4.9. İş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.9.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:
reply = input(prompt)
if reply in {'y', 'ye', 'yes'}:
return True
if reply 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.9.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.9.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.9.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.9.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.9.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.9.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'
The third function kwd_only_arg
only allows keyword arguments as indicated
by a *
in the function definition:
>>> 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.9.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.9.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))
Normalde variadic argümanlar biçimsel parametreler listesinde en sonda yer alır, çünkü fonksiyona aktarılan geri kalan tüm girdi argümanlarını toplarlar. *args
parametresinden sonra gelen tüm biçimsel parametreler ‘keyword-only’ (yalnızca-anahtar-kelime) argümanlarıdır, yani konumsal argümanlar yerine sadece anahtar kelimeler olarak kullanılabilirler.
>>> def concat(*args, sep="/"):
... return sep.join(args)
...
>>> concat("earth", "mars", "venus")
'earth/mars/venus'
>>> concat("earth", "mars", "venus", sep=".")
'earth.mars.venus'
4.9.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.9.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.9.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.9.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).
Annotations are stored in the __annotations__
attribute of the function as a dictionary and have no effect on any other part of the
function. Parameter annotations are defined by a colon after the parameter name, followed
by an expression evaluating to the value of the annotation. Return annotations are
defined by a literal ->
, followed by an expression, between the parameter
list and the colon denoting the end of the def
statement. The
following example has a required argument, an optional argument, and the return
value annotated:
>>> 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.10. 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 delowercase_with_underscores
kullanmaktır. İlk yöntem argümanının adı olarak her zamanself
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