Ana içeriğe geç
Kesit Python
Temel · Değerler ve metinler

Operatörler derinlemesine

Aritmetik ve atama, zincirleme karşılaştırma, değer döndüren and/or ve kısa devre, in ve is, izin bayraklarıyla bit operatörleri, PCEP öncelik tablosu ve mors operatörü := ile ifadeleri doğru ve okunur yazarsın.

Uygulamada aç, 18 alıştırmayı çözÜcretsiz, hesap gerekmez, kurulum yok.

Bu sayfa dersin okuma sürümüdür. Metin bir kutuyu çalıştırmanı ya da adıma geçmeni söylüyorsa dersi uygulamada aç.

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)
Python'da ++ yokC, Java ya da JavaScript'ten gelenler 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)
Zincir sezgiye ters düşebilir1 < 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:

  1. Kısa devre (short-circuit): and ilk değer yanlışsa ikinciye hiç bakmaz; or ilk değer doğruysa ikinciye bakmaz. Bu sayede adet != 0 and toplam / adet > 100 sıfıra bölme hatası vermez.
  2. Değer döndürme: and ve or True/False değil, son baktıkları işleneni döndürür. or ilk doğru (truthy) değeri, hepsi yanlışsa sonuncuyu; and ilk yanlış (falsy) değeri, hepsi doğruysa sonuncuyu verir. not ise 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'i yalnız None (ve True/False) için kullanKüçük tam sayılar ve kısa metinler bellekte önbelleğe (cache) alındığından 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)
& ile and aynı şey değiland 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 **).

ÖncelikOperatörAçıklama
1 (en yüksek)()Parantez
2**Üs (sağdan sola!)
3+x, -x, ~xTekli (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 notKarşılaştırma, üyelik, kimlik
11notMantıksal DEĞİL
12andMantıksal VE
13orMantı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.

Gerçek işteIlgaz Bank'ın kullanıcı yetki tablosunda her personelin izinleri tek bir tam sayı sütununda tutulur: 1 = hesap görüntüleme, 2 = havale başlatma, 4 = havale onaylama, 8 = limit değiştirme. Denetim ekibi "havale başlatıp aynı zamanda onaylayabilen" riskli kullanıcıları (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.
Uzmanlaş

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.

Alıştırmalara geç (18)