Operatör: küçük sembol, büyük sonuç
Anadolu Market'in sepet ekranında tek bir satır şunu karar verir: "Tutar 500 TL'yi geçiyor ve müşteri premium ya da kampanya kodu girmişse kargo bedava." Bu satırda yanlış yere konmuş bir parantez, binlerce siparişte bedava kargo vermek ya da hiç vermemek demektir. Operatörler (operators), değerler üzerinde işlem yapan sembollerdir: +, ==, and, in, &... Sayılar dersinde aritmetiği, koşullar dersinde karşılaştırma ve mantık operatörlerine giriş yaptın. Bu ders bütün operatör ailesini bir arada, öncelik ve değer döndürme ayrıntılarıyla ele alıyor; Python Institute PCEP sınavının en çok soru sorduğu konulardan biridir.
Aritmetik ve atama operatörleri
Hatırlatma: + - * / bildiğin dört işlem, // tam bölme (floor division), % kalan (modulo), ** üs alma. / her zaman float döndürür, // iki int arasında int döndürür.
Artırılmış atama (augmented assignment) operatörleri "değişkeni kendisiyle işleme sok ve geri yaz" kısaltmasıdır: x += 3, x = x + 3 ile aynı sonucu verir. Hepsinin karşılığı vardır: -=, *=, /=, //=, %=, **=, hatta bit operatörleri için &=, |=.
stok = 120
stok -= 15 # satış
stok += 40 # tedarik geldi
stok //= 2 # yarısı başka depoya
print("Stok:", stok)
puan = 3
puan **= 2
puan %= 5
print("Puan:", puan)
etiket = "AM-"
etiket += "00417" # metinlerde de çalışır
etiket *= 2
print(etiket)
sayac++ yazmayı dener. Python'da bu sözdizimi hatasıdır; sayac += 1 yaz. ++sayac ise hata bile vermez: iki tekli artı işaretidir ve değeri değiştirmez. Sinsi bir tuzaktır.// ve % negatif sayılarda
Python'da // sonucu her zaman aşağıya (eksi sonsuza doğru) yuvarlar; % de bu kurala uyacak şekilde bölenin işaretini alır. Kural her zaman şudur: a == (a // b) * b + a % b.
print(7 // 2, 7 % 2) # 3 1
print(-7 // 2, -7 % 2) # -4 1 (−3.5 aşağıya: −4)
print(7 // -2, 7 % -2) # -4 -1
print(int(-7 / 2)) # -3 (int sıfıra doğru keser!)
# Pratik fayda: haftanın günlerinde geriye sarma
gunler = ["Pzt", "Sal", "Çar", "Per", "Cum", "Cmt", "Paz"]
bugun = 1 # Salı
print("3 gün önce:", gunler[(bugun - 3) % 7])
C ve Java'da -7 % 2 sonucu -1'dir; Python'da 1. Bu fark sayesinde Python'da % 7 negatif sayılarda bile geçerli bir indeks verir.
Karşılaştırma ve zincirleme
==, !=, <, <=, >, >= her zaman True ya da False döndürür. Python'un az dilde bulunan bir güzelliği zincirleme karşılaştırmadır (chained comparison): 0 < x < 10, matematikteki gibi okunur ve 0 < x and x < 10 anlamına gelir; x yalnızca bir kez hesaplanır.
tutar = 750 print(500 < tutar <= 1000) # True print(1 < 3 > 2) # True: 1 < 3 and 3 > 2 print(1 == 1.0, "a" < "b", [1, 2] < [1, 3]) print(3 == 3 == 3.0, 1 < 2 < 2)
1 < 3 > 2 geçerlidir ama okuması zordur; zinciri yalnızca aynı yönde (a < b < c) kullan. x == y == 0 "ikisi de sıfır mı" demektir, "x, y==0 sonucuna eşit mi" değil.Mantıksal operatörler: and, or, not
and iki koşul da doğruysa, or en az biri doğruysa doğrudur, not tersine çevirir. İki önemli ayrıntı var:
- Kısa devre (short-circuit):
andilk değer yanlışsa ikinciye hiç bakmaz;orilk değer doğruysa ikinciye bakmaz. Bu sayedeadet != 0 and toplam / adet > 100sıfıra bölme hatası vermez. - Değer döndürme:
andveorTrue/False değil, son baktıkları işleneni döndürür.orilk doğru (truthy) değeri, hepsi yanlışsa sonuncuyu;andilk yanlış (falsy) değeri, hepsi doğruysa sonuncuyu verir.notise her zaman bool döndürür.
print("" or "misafir") # misafir
print("Ayşe" or "misafir") # Ayşe
print(0 and 1 / 0) # 0 (1/0 hiç çalışmadı!)
print(5 and "ok") # ok
print([] or {} or None) # None (hepsi falsy, sonuncusu)
print(not "", not 42) # True False
def pahali_kontrol():
print(" (pahalı kontrol çalıştı)")
return True
print(False and pahali_kontrol()) # pahalı kontrol hiç çağrılmaz
print(True or pahali_kontrol())
Bu davranış günlük kodda varsayılan değer vermek için kullanılır: ad = girilen_ad or "Misafir". Ama dikkat: 0 ve boş metin de falsy'dir; sayısal değerlerde if x is None kontrolü daha güvenlidir.
Üyelik ve kimlik: in ve is
in / not in bir değerin koleksiyonda olup olmadığını sorar: metinde alt metin, listede eleman, sözlükte anahtar. is / is not ise iki adın aynı nesneyi gösterip göstermediğini sorar; == ise değerlerin eşitliğini.
sepet = ["çay", "şeker"]
stok = {"çay": 40, "kahve": 0}
print("çay" in sepet, "kahve" not in sepet)
print("kahve" in stok, 40 in stok) # sözlükte anahtar aranır
print("AM-" in "AM-00417")
a = [1, 2, 3]
b = a
c = [1, 2, 3]
print(a == c, a is c, a is b) # True False True
indirim = None
print(indirim is None) # None için doğru yol
is bazen "tesadüfen" doğru çalışır ve hatayı gizler. Değer karşılaştırması her zaman == ile yapılır. Python 3.8'den beri x is 5 gibi bir literalle is karşılaştırması SyntaxWarning üretir.Bit operatörleri ve izin bayrakları
Bilgisayar sayıları ikili (binary) tutar. Bit operatörleri (bitwise operators) sayıların bitleri üzerinde çalışır: & (VE), | (VEYA), ^ (ÖZEL VEYA, XOR), ~ (tümleyen: ~x == -x - 1), << (sola kaydır: 2 ile çarpar), >> (sağa kaydır: 2'ye tam böler). İş hayatında en sık karşılaştığın yer izin bayraklarıdır (flags): her bit bir yetkiyi temsil eder ve birden fazla izin tek bir sayıda saklanır. Linux dosya izinleri (chmod 755) tam olarak böyle çalışır.
OKU, YAZ, SIL = 1, 2, 4 # 0b001, 0b010, 0b100
analist = OKU # 0b001
editor = OKU | YAZ # 0b011 -> 3
print("editör:", editor, bin(editor))
print("Editör yazabilir mi?", editor & YAZ == YAZ)
print("Editör silebilir mi?", bool(editor & SIL))
editor |= SIL # izin ekle
print("Sil eklendi:", bin(editor))
editor &= ~YAZ # izin kaldır
print("Yaz kaldırıldı:", bin(editor))
print("Değişen bitler:", bin((OKU | YAZ) ^ editor))
print(1 << 3, 40 >> 2, ~5)
and mantıksal doğruluğa bakar ve işlenen döndürür; & bit bit işlem yapar. 2 and 4 sonucu 4'tür, 2 & 4 sonucu 0. Ayrıca &, =='den daha yüksek önceliklidir; bu yüzden editor & YAZ == YAZ doğru çalışır ama okunurluk için parantez eklemek iyi bir alışkanlıktır.Operatör önceliği
Birden fazla operatör yan yana gelince Python hangisini önce yapacağına öncelik (precedence) tablosuyla karar verir. Yukarıdaki satırlar önce işlenir; aynı satırdakiler soldan sağa gider (tek istisna **).
| Öncelik | Operatör | Açıklama |
|---|---|---|
| 1 (en yüksek) | () | Parantez |
| 2 | ** | Üs (sağdan sola!) |
| 3 | +x, -x, ~x | Tekli (unary) işaret, bit tümleyen |
| 4 | *, /, //, % | Çarpma ailesi |
| 5 | +, - | Toplama, çıkarma |
| 6 | <<, >> | Kaydırma |
| 7 | & | Bit VE |
| 8 | ^ | Bit XOR |
| 9 | | | Bit VEYA |
| 10 | == != < <= > >= in not in is is not | Karşılaştırma, üyelik, kimlik |
| 11 | not | Mantıksal DEĞİL |
| 12 | and | Mantıksal VE |
| 13 | or | Mantıksal VEYA |
| 14 (en düşük) | := | Mors (walrus) ataması |
print(2 + 3 * 4) # 14 print(2 ** 3 ** 2) # 512: 3 ** 2 önce, sonra 2 ** 9 print((2 ** 3) ** 2) # 64 print(-3 ** 2) # -9: ** tekli eksiden önce print(not 1 == 2) # True: not (1 == 2) print(True or False and False) # True: and, or'dan önce
PCEP sınavında -3 ** 2, 2 ** 3 ** 2 ve and/or karışımı klasik sorulardır. Gerçek kodda kural basit: emin değilsen parantez koy. Parantez hiçbir şeyi yavaşlatmaz, okuyanın zihnini rahatlatır.
Mors operatörü :=
Python 3.8 ile gelen := (assignment expression, görünüşü mors bıyığına benzediği için walrus) bir ifadenin içinde atama yapar: değeri hem değişkene koyar hem de döndürür. En çok, bir değeri hesaplayıp hemen kontrol etmek istediğin yerlerde işe yarar.
kodlar = [" AM-417 ", "AM-9", " AM-10022 ", ""]
# Uzunluğu bir kez hesapla, hem koşulda hem sonuçta kullan
uzunlar = [(k.strip(), n) for k in kodlar if (n := len(k.strip())) >= 6]
print(uzunlar)
satirlar = ["120", "85", "", "40"]
i = 0
while (satir := satirlar[i]) != "":
print("Okunan:", satir)
i += 1
Önceliği en düşük operatör olduğu için koşul içinde genellikle parantez gerekir: (n := len(x)) > 5. Parantezsiz n := len(x) > 5, n'ye True/False atar. Okunurluğu artırdığı yerde kullan; her atamayı mors'a çevirmek kodu zorlaştırır.
(izin & 6) == 6 sorgusuyla bulur. Aynı bit mantığı Linux dosya izinlerinde, ağ maskelerinde (IP alt ağları) ve oyun motorlarındaki çarpışma katmanlarında da karşına çıkacak.Operatörler aslında metot çağrısıdır
a + b yazdığında Python a.__add__(b) çağırır; a == b için __eq__, x in y için y.__contains__(x). Bu yüzden + sayılarda toplar, metinlerde birleştirir, listelerde ekler. Sınıflar dersinde kendi sınıfların için bu "sihirli metotları" (dunder methods) yazıp para1 + para2 gibi okunur kod üreteceksin. Standart kütüphanedeki operator modülü de operatörlerin fonksiyon hâlini verir: operator.add(2, 3), sorted(liste, key=operator.itemgetter(1)).
Kısa devre ile korumalı erişim
musteri and musteri.get("adres") and musteri["adres"].get("il") zinciri, ara değerlerden biri None/boş olduğunda hata vermeden durur. Okunur ama uzar; orta seviyede dict.get zincirleri ve try/except ile alternatiflerini karşılaştıracaksın.
Kayan noktada == kullanma
0.1 + 0.2 == 0.3 False'tur (sayılar dersinde gördün). Para ve ölçüm karşılaştırmalarında math.isclose(a, b) ya da abs(a - b) < 1e-9 kullan; para için Decimal tercih et.
Bit hileleri
n & 1 sayının tek olup olmadığını, n & (n - 1) == 0 (n > 0 iken) 2'nin kuvveti olup olmadığını söyler; n.bit_count() (3.10+) açık bit sayısını verir. Bunlar mülakatlarda sık sorulur, ama iş kodunda okunurluk için enum.Flag sınıfını tercih et: Izin.OKU | Izin.YAZ hem bit verimliliği hem anlamlı adlar verir.