---
title: İşleçler
source: 'https://academia.sh/tr/kurslar/python-temelleri/islecler'
course: 'Python Temelleri'
language: tr
updated: '2026-08-17T18:10:28+00:00'
license: 'CC BY-SA 4.0'
---

# İşleçler

On bir işleç biçimi 10 ayrı özel yöntemi çağırır ve biri karşılıksız kalır: `n + m` `__add__`, `n += m` `__iadd__` çağırır; `x in n` `__contains__` bulamazsa yineleme protokolüne düşer ve tek çağrı yerine beş çağrı ödenir.

Önceki ders `x += ek` yazımının tipe göre iki ayrı iş yaptığını ölçtü: üç tipte adı yeni bir
nesneye bağladı, birinde nesneyi yerinde değiştirdi. Yaptığı işin adı konmadı. Bu ders o adı
koyar.

Programlama Temelleri kursu işleçleri öncelik, birleşme yönü ve kısa devre değerlendirme
başlıklarıyla kurmuş, aynı işlecin farklı tiplerde farklı anlam taşıyabildiğini de
göstermişti. **Bunlar tekrarlanmaz.** Orada kurulan şey kavramdı — bir işlecin anlamı
işlenenin tipine bağlıdır. Burada ölçülen şey o bağın **düzeneğidir**: hangi işleç hangi
adı arıyor, aradığını bulamazsa nereye düşüyor ve hiçbir yerde bulamazsa ne oluyor.

## İşleç Bir Yöntem Çağrısıdır

İlk dersin kurgusu bu ders için genişletilir. `Izci` var olan yöntemlerini korur; üzerine
çıkarma, çarpma, sağdan çarpma, küçüklük, işaret değiştirme ve kesişim eklenir. Her yöntem
çağrıldığında yine adını kaydeder.

- **DT24** — Ölçülen on bir biçimin tamamı iki `Izci` nesnesi ya da bir `Izci` ile bir tam
  sayı üzerinde çalışır; ölçüm işlecin **sonucuna** değil, çağırdığı yöntem adına bakar.
- **DT25** — Son biçim (`n / m`) bilerek karşılıksız bırakılmıştır: `Izci` bölme için
  hiçbir yöntem tanımlamaz. O satırdaki ünlem önekli kayıt, bir çağrıyı değil bir istisnayı
  gösterir.

```python
"""Isleclerin cagirdigi ozel yontemler; Izci her katilimini kaydeder."""

KAYIT = []


def kaydet(ad):
    KAYIT.append(ad)


class Izci:
    """Ortak kurgunun islec bolumu; bu derste alti yontem eklenmistir."""

    def __init__(self, ogeler=(1, 2, 3)):
        self.ogeler = list(ogeler)

    def __iter__(self):
        kaydet("__iter__")
        self._i = 0
        return self

    def __next__(self):
        kaydet("__next__")
        if self._i >= len(self.ogeler):
            raise StopIteration
        deger = self.ogeler[self._i]
        self._i += 1
        return deger

    def __contains__(self, x):
        kaydet("__contains__")
        return x in self.ogeler

    def __add__(self, o):
        kaydet("__add__")
        return Izci(self.ogeler + list(o.ogeler))

    def __iadd__(self, o):
        kaydet("__iadd__")
        self.ogeler.extend(o.ogeler)
        return self

    def __eq__(self, o):
        kaydet("__eq__")
        return isinstance(o, Izci) and self.ogeler == o.ogeler

    def __sub__(self, o):                      # eklendi
        kaydet("__sub__")
        return Izci([x for x in self.ogeler if x not in o.ogeler])

    def __mul__(self, k):                      # eklendi
        kaydet("__mul__")
        return Izci(self.ogeler * k)

    def __rmul__(self, k):                     # eklendi
        kaydet("__rmul__")
        return Izci(self.ogeler * k)

    def __lt__(self, o):                       # eklendi
        kaydet("__lt__")
        return len(self.ogeler) < len(o.ogeler)

    def __neg__(self):                         # eklendi
        kaydet("__neg__")
        return Izci([-x for x in self.ogeler])

    def __and__(self, o):                      # eklendi
        kaydet("__and__")
        return Izci([x for x in self.ogeler if x in o.ogeler])


def olc(islev):
    """Bir islec bicimini calistirir ve cagrilan protokolleri dondurur."""
    KAYIT.clear()
    try:
        islev()
    except Exception as e:
        KAYIT.append(f"!{type(e).__name__}")
    return list(KAYIT)


def yerinde_toplama():
    a = Izci()
    a += Izci()


BICIMLER = (
    ("n + m", lambda: Izci() + Izci()),
    ("n += m", yerinde_toplama),
    ("n - m", lambda: Izci() - Izci()),
    ("n * 2", lambda: Izci() * 2),
    ("2 * n", lambda: 2 * Izci()),
    ("-n", lambda: -Izci()),
    ("n < m", lambda: Izci() < Izci()),
    ("n == m", lambda: Izci() == Izci()),
    ("n & m", lambda: Izci() & Izci()),
    ("x in n", lambda: 9 in Izci()),
    ("n / m", lambda: Izci() / Izci()),
)

print(f"{'işleç biçimi':<14s} {'çağrı':>5s}  protokol")
for ad, islev in BICIMLER:
    c = olc(islev)
    print(f"{ad:<14s} {len(c):5d}  {' '.join(c)}")

kayitlar = [olc(f) for _, f in BICIMLER]
tekil = sorted({p for c in kayitlar for p in c if not p.startswith("!")})
print(f"\n{len(BICIMLER)} biçim, ayrı protokol {len(tekil)}, "
      f"toplam çağrı {sum(len(c) for c in kayitlar)}, "
      f"karşılıksız biçim {sum(1 for c in kayitlar if any(p.startswith('!') for p in c))}")
```

```
işleç biçimi   çağrı  protokol
n + m              1  __add__
n += m             1  __iadd__
n - m              1  __sub__
n * 2              1  __mul__
2 * n              1  __rmul__
-n                 1  __neg__
n < m              1  __lt__
n == m             1  __eq__
n & m              1  __and__
x in n             1  __contains__
n / m              1  !TypeError

11 biçim, ayrı protokol 10, toplam çağrı 11, karşılıksız biçim 1
```

On bir biçim **10 ayrı protokol** çağırıyor ve **1** biçim karşılıksız kalıyor. Tablonun
okunuşu tektir: solda yazılan simge, sağdaki adın kısaltmasıdır. `-` işleci `__sub__`,
`*` işleci `__mul__`, `<` işleci `__lt__` demektir. `&` satırı bu okumanın en açık örneğidir
— işlecin ne yaptığı tipe kalmıştır: tam sayılarda bit düzeyinde birleştirme yapar, burada
`Izci` onu kesişim olarak tanımlamıştır. İşleç bir anlam taşımaz, bir **ad** taşır.

Son satır bunun öteki yüzü. `Izci` bölme için hiçbir yöntem tanımlamadığından `n / m` biçimi
`TypeError` ile düşüyor. Kaynak kusursuzdur ve derleme aşamasını geçmiştir; kusur, çağrılacak
adın bulunamamasıdır. Bir işlecin "desteklenmemesi" diye ayrı bir durum yoktur — yalnız
tanımlanmamış bir yöntem vardır. Bir tipe bölme kazandırmanın yolu da bu yüzden tektir:
`__truediv__` adında bir yöntem yazmak.

## Toplama ile Yerinde Toplama

Tablonun ilk iki satırı kursun ikinci iddiasını ödüyor. `n + m` **`__add__`**, `n += m`
**`__iadd__`** çağırıyor. İkisi birbirinin kısaltması değildir; ayrı adlar, ayrı
sözleşmelerdir.

`__add__` işlenenlerine dokunmaz, **yeni bir nesne** üretip döndürür. `__iadd__` var olan
nesneyi **yerinde değiştirip kendisini** döndürür. Önceki dersin ölçtüğü ayrım budur: bir
tip `__iadd__` tanımlamışsa `+=` nesneyi değiştirir ve ona bağlı ikinci ad da değişikliği
görür; tanımlamamışsa `+=` yazımı `__add__`'e düşer, yeni nesne üretilir ve **yalnız soldaki
ad** ona bağlanır. Tam sayı, dizgi ve demetin geçen derste "adı yeniden bağladı" çıkmasının
nedeni tam olarak budur — bu tipler değiştirilemez olduğu için `__iadd__` tanımlayamazlar.

Buradan pratik bir sonuç çıkar: `a += b` yazımı, `a` başkalarıyla paylaşılan bir nesneye
bağlıysa **onların gördüğü değeri de değiştirir**. `a = a + b` yazımı ise asla değiştirmez.
İki satır aynı işi yapmaz ve aralarındaki seçim üslup değildir.

## Sağdaki İşlenene Düşme

`n * 2` ile `2 * n` satırları ayrı yöntemler çağırıyor: `__mul__` ve `__rmul__`. Nedeni
şudur: `2 * n` yazıldığında önce **soldaki** işlenenin tipine sorulur. Tam sayı, bir `Izci`
ile nasıl çarpılacağını bilmez ve "bilmiyorum" yanıtı verir. Bunun üzerine **sağdaki**
işlenene, işlemin ters yönü sorulur — aranan ad `__mul__` değil, başında `r` olan
`__rmul__`'dır.

Bu düşüş, dilin genişletilebilirliğinin temelidir. Yerleşik bir tipe yeni bir davranış
eklenemez; ama yeni bir tip, yerleşik tiplerin solda durduğu ifadelere `__radd__`, `__rmul__`
gibi yöntemlerle katılabilir. Aranan yöntem iki işlenende de yoksa `n / m` satırındaki sonuç
alınır.

## Önceliğin Belirlediği Çağrı Sırası

Bir ifadede birden çok işleç varsa hangisinin önce çağrıldığını **öncelik** belirler.
Programlama Temelleri kursu öncelik ve birleşme yönü tablosunu kurmuştu; burada ölçülen şey
o tablonun kod düzeyindeki karşılığıdır. Öncelik bir okuma kolaylığı değildir: ikinci dersin
gösterdiği ağacı biçimlendirir ve ağaç da **yöntemlerin çağrılma sırasını** verir.

- **DT26** — Üç ifade aynı üç nesne üzerinde kurulur ve yalnız işleçleri ile parantezleriyle
  ayrılır; ölçülen şey kayıt dizisinin **sırasıdır**, uzunluğu değil.

```python
def carpim_once():
    Izci((1,)) + Izci((2,)) * 2


def toplama_once():
    (Izci((1,)) + Izci((2,))) * 2


def karsilastirma_sonra():
    Izci((1,)) + Izci((2,)) == Izci((1, 2))


print(f"{'ifade':<16s} çağrı sırası")
for ad, islev in (("n + m * 2", carpim_once),
                  ("(n + m) * 2", toplama_once),
                  ("n + m == k", karsilastirma_sonra)):
    print(f"{ad:<16s} {' '.join(olc(islev))}")
```

```
ifade            çağrı sırası
n + m * 2        __mul__ __add__
(n + m) * 2      __add__ __mul__
n + m == k       __add__ __eq__
```

İlk iki satırda aynı iki yöntem çağrılıyor, sıraları ters. Çarpma toplamadan önce geldiği
için `__mul__` önce çalışıyor ve ürettiği nesne `__add__`'e işlenen olarak giriyor;
parantez konduğunda sıra tersine dönüyor. Üçüncü satır aritmetiğin karşılaştırmadan önce
geldiğini gösteriyor: toplama tamamlanmadan eşitlik sorulamaz, çünkü karşılaştırılacak
nesne henüz yoktur. Öncelik kuralları, hangi yöntemin hangi nesneyi işlenen olarak
göreceğini belirler.

## Üyelik Sınaması Yöntemini Bulamazsa

İkinci iddianın en görünür örneği üyelik sınamasıdır. `x in n` biçimi önce `__contains__`
arar; bulamazsa **yineleme protokolüne düşer** ve nesneyi baştan sona tarayarak öğeleri
tek tek karşılaştırır. Sözdizim aynıdır, bedel değildir.

- **DT27** — Karşılaştırılan iki nesne aynı üç öğeyi taşır; tek fark `Izci`'nin
  `__contains__` tanımlaması, `Yinelenir`'in tanımlamamasıdır.
- **DT28** — Her sınama bir kez bulunan ve bir kez bulunmayan bir değerle çalıştırılır;
  ölçülen şey sonuç ile çağrı sayısıdır.

```python
class Yinelenir:
    """__contains__ tanimlamaz; yalniz yineleme protokolunu tasir."""

    def __init__(self, ogeler=(1, 2, 3)):
        self.ogeler = list(ogeler)

    def __iter__(self):
        kaydet("__iter__")
        self._i = 0
        return self

    def __next__(self):
        kaydet("__next__")
        if self._i >= len(self.ogeler):
            raise StopIteration
        deger = self.ogeler[self._i]
        self._i += 1
        return deger


UYELIK = (
    ("2 in Izci()      ", lambda: 2 in Izci()),
    ("9 in Izci()      ", lambda: 9 in Izci()),
    ("2 in Yinelenir() ", lambda: 2 in Yinelenir()),
    ("9 in Yinelenir() ", lambda: 9 in Yinelenir()),
)

print(f"{'üyelik sınaması':<18s} {'sonuç':>5s} {'çağrı':>5s}  protokol")
for ad, islev in UYELIK:
    KAYIT.clear()
    sonuc = islev()
    c = list(KAYIT)
    print(f"{ad:<18s} {str(sonuc):>5s} {len(c):5d}  {' '.join(c)}")
```

```
üyelik sınaması    sonuç çağrı  protokol
2 in Izci()         True     1  __contains__
9 in Izci()        False     1  __contains__
2 in Yinelenir()    True     3  __iter__ __next__ __next__
9 in Yinelenir()   False     5  __iter__ __next__ __next__ __next__ __next__
```

`Izci` iki durumda da **1** çağrı ödüyor: sorulan soru doğrudan nesneye gidiyor ve nesne
yanıtı kendi biliyor. `Yinelenir` aynı sonuçları veriyor ama bedeli ayrı — bulunan değerde
**3**, bulunmayan değerde **5** çağrı.

Sayıların dağılımı düşüşün nasıl işlediğini de gösteriyor. Bulunan değerde tarama ikinci
öğede duruyor: bir `__iter__` ve iki `__next__`. Bulunmayan değerde ise üç öğenin üçü de
okunuyor ve dördüncü `__next__` `StopIteration` fırlatarak taramayı bitiriyor — ilk dersteki
"öğe sayısından bir fazla" örüntüsünün aynısı. **Bulunmayan bir değer her zaman tam tarama
ödettirir.**

`Izci`'nin `__iter__` de tanımlaması bir noktayı daha kapatıyor: iki yöntem de tanımlıysa
`in` biçimi **`__contains__`'i seçer**, çünkü düşüş ancak arananın yokluğunda olur. Sonuç
ikisinde de aynı çıkabilir; ödenen çağrı sayısı çıkmaz.

## Zincirlenen Karşılaştırma ve Mantıksal İşleçler

İki yazım kaldı ve ikisi de tabloya sığmıyor, çünkü çağırdıkları yöntem sayısı sabit değil.

- **DT29** — Ortadaki işlenen bir işlev çağrısıyla üretilir ve bu işlev de kayda katılır;
  böylece işlenenin **kaç kez** üretildiği sayılabilir.
- **DT30** — `Izci` doğruluk sınaması için hiçbir yöntem tanımlamaz; mantıksal satırlarda
  kaydın boş çıkması bu yüzden anlamlıdır. Sınamanın hangi yöntemi çağırdığı akış konusunun
  ilk dersinde ölçülür.

```python
def orta():
    kaydet("orta()")                 # ortadaki islenen kac kez uretildi?
    return Izci((1, 2))


def zincir_iki_dogru():
    Izci((1,)) < orta() < Izci((1, 2, 3))


def acik_yazim():
    Izci((1,)) < orta() and orta() < Izci((1, 2, 3))


def zincir_ilk_yanlis():
    Izci((1, 2, 3)) < orta() < Izci((1, 2, 3, 4))


print(f"{'yazım':<32s} {'çağrı':>5s}  protokol")
for ad, islev in (("a < orta() < c  (ikisi de doğru)", zincir_iki_dogru),
                  ("a < orta() and orta() < c", acik_yazim),
                  ("a < orta() < c  (ilki yanlış)", zincir_ilk_yanlis)):
    c = olc(islev)
    print(f"{ad:<32s} {len(c):5d}  {' '.join(c)}")

print()
print(f"{'mantıksal yazım':<12s} {'çağrı':>5s}  protokol")
for ad, islev in (("n and m", lambda: Izci() and Izci()),
                  ("n or m", lambda: Izci() or Izci()),
                  ("not n", lambda: not Izci()),
                  ("n & m", lambda: Izci() & Izci())):
    c = olc(islev)
    print(f"{ad:<12s} {len(c):5d}  {' '.join(c) if c else '(hiçbiri)'}")

print()
print("and/or ne döndürür:", repr("ol" and "cum"), repr("" or "cum"),
      repr(0 or []), "| not ne döndürür:", repr(not "ol"))
```

```
yazım                            çağrı  protokol
a < orta() < c  (ikisi de doğru)     3  orta() __lt__ __lt__
a < orta() and orta() < c            4  orta() __lt__ orta() __lt__
a < orta() < c  (ilki yanlış)        2  orta() __lt__

mantıksal yazım çağrı  protokol
n and m          0  (hiçbiri)
n or m           0  (hiçbiri)
not n            0  (hiçbiri)
n & m            1  __and__

and/or ne döndürür: 'cum' 'cum' [] | not ne döndürür: False
```

Zincirlenen karşılaştırma iki `__lt__` çağırıyor ama ortadaki işleneni **bir kez** üretiyor.
Elle açılmış yazım aynı iki karşılaştırmayı yapıyor ve ortayı **iki kez** üretiyor: dört
çağrıya karşı üç. Fark yalnız sayıda değil, doğrulukta da olabilir — ortadaki ifade bir yan
etki taşıyorsa iki yazım aynı programı vermez. Üçüncü satır zincirin ikinci yarısını da
gösteriyor: ilk karşılaştırma yanlış çıktığında ikincisi hiç yapılmıyor, çağrı **2**'de
kalıyor.

Alt tablo bir sınır çiziyor. `and`, `or` ve `not` hiçbir özel yöntem çağırmıyor; kayıtları
boş. Bunlar aşırı yüklenemez, çünkü yaptıkları iş bir işlem değil, bir **seçimdir**: işleneni
bir doğruluk sınamasından geçirip işlenenlerden **birini olduğu gibi** döndürürler. Son satır
bunu doğruluyor — `"ol" and "cum"` bir doğruluk değeri değil, `'cum'` dizgisini veriyor;
`0 or []` boş listeyi veriyor. Yalnız `not` bir doğruluk değeri üretir. Karıştırılmaya en
elverişli çift `and` ile `&`'dir: ilki bir seçim, ikincisi `__and__` çağıran bir işleçtir.

## Özet

- Her işleç adı belli bir özel yöntemin kısaltmasıdır; on bir biçim **10 ayrı protokol**
  çağırır ve karşılığı olmayan biçim `TypeError` ile düşer. Öncelik, bu yöntemlerin hangi
  sırayla çağrılacağını belirler.
- `n + m` **`__add__`**, `n += m` **`__iadd__`** çağırır; ilki yeni nesne üretir, ikincisi
  var olanı değiştirir ve paylaşılan bir nesnede bu fark gözlenir.
- Soldaki işlenen işlemi karşılayamazsa sağdakinin ters yöndeki yöntemi (`__rmul__`)
  aranır; ikisi de yoksa işleç karşılıksız kalır.
- `x in n` önce `__contains__` arar (**1** çağrı); bulamazsa yineleme protokolüne düşer ve
  bulunmayan bir değer için **5** çağrı öder.
- Zincirlenen karşılaştırma ortadaki işleneni bir kez üretir ve ilk karşılaştırma yanlışsa
  ikincisini hiç yapmaz.
- `and`, `or` ve `not` özel yöntem çağırmaz; işlenenlerden birini olduğu gibi döndürürler ve
  bit düzeyi karşılıkları olan `&` ile `|`'dan ayrıdırlar.

## Sonraki Adım

Bu derste `+` işlecinin `Izci` üzerinde yeni bir nesne ürettiği görüldü. Aynı işleç dizgiler
üzerinde de çalışır ve orada seçenek bile yoktur: dizgi değiştirilemez bir tiptir, dolayısıyla
`__iadd__` tanımlayamaz. Sonraki ders bunun bedelini sayar — bir dizginin küçük harfe
çevrilmesi, boşluklarının atılması ya da bir parçasının değiştirilmesi neden her seferinde
**yeni bir nesne** üretmek zorundadır, biçimlendirme yazımları bu üretimi nasıl tek adıma
indirir ve ard arda yapılan değişikliklerde kaç nesne ortaya çıkar?
