---
title: 'Dizgilerle Çalışma'
source: 'https://academia.sh/tr/kurslar/python-temelleri/dizgilerle-calisma'
course: 'Python Temelleri'
language: tr
updated: '2026-08-17T18:10:28+00:00'
license: 'CC BY-SA 4.0'
---

# Dizgilerle Çalışma

Dizgi değiştirilemez olduğu için her üretim yöntemi yeni bir nesne döndürür: yedi parçayı `+` ile birleştirmek 7 ara nesne kurar, `join` 1 kurar ve sekiz biçimlendirme yazımı 3 ayrı özel yöntem çağırır.

Önceki ders `+` işlecinin `__add__`, `+=` yazımının `__iadd__` çağırdığını ölçtü ve
değiştirilemez tiplerin `__iadd__` tanımlayamayacağını söyledi. Dizgi tam olarak böyle bir
tiptir; ondaki her işlem bu tek kısıttan türer.

Bu dersin sorusu dizgi yöntemlerinin listesi değildir. Soru şudur: bir tipin değiştirilemez
olması, üzerinde tanımlanabilecek yöntemleri nasıl **zorlar**, ve bu zorlama sayı olarak ne
kadar tutar?

## Yerinde Yazma Diye Bir Şey Yok

Bir dizginin bir karakterini değiştirmenin yolu yoktur. Sözdizim vardır — köşeli ayraçla
yazma — ama karşılığı olan yöntem tanımlı değildir.

- **DT31** — Ölçümde tek bir başlangıç dizgisi kullanılır ve üzerinde art arda dört işlem
  yapılır; her adımda sonucun bir önceki nesneyle **aynı nesne** olup olmadığı `is` ile
  sorulur. Hiçbir kimlik sayısı basılmaz.
- **DT32** — Seçilen dört işlem içeriği gerçekten değiştirir. Sonucu girdisiyle birebir aynı
  olan bir işlemin (örneğin boşluksuz bir dizgide `strip`) aynı nesneyi döndürüp
  döndürmemesi **gerçekleştirime bağlıdır** ve ölçüme sokulmamıştır.
- **DT33** — İkinci tablodaki yöntemler dizgiyi dönüştürmez, ona bir soru sorar; ölçülen
  şey dönen değerin **tipidir**.

```python
"""Dizgi degistirilemez: uretim yontemleri yeni nesne, sorgu yontemleri baska tip dondurur."""

HAM = "  12; kuzey; 18  "

try:
    HAM[0] = "x"
except TypeError as e:
    print("dizgiye yerinde yazma:", type(e).__name__)

ADIMLAR = (("başlangıç", lambda s: s),
           ("strip()", lambda s: s.strip()),
           ("upper()", lambda s: s.upper()),
           ("replace(';', ',')", lambda s: s.replace(";", ",")),
           ("split(',')", lambda s: s.split(",")))

print(f"\n{'adım':<20s} {'tip':<5s} {'önceki nesne mi':>15s}  değer")
onceki = HAM
for ad, islem in ADIMLAR:
    simdiki = islem(onceki)
    print(f"{ad:<20s} {type(simdiki).__name__:<5s} "
          f"{str(simdiki is onceki):>15s}  {simdiki!r}")
    onceki = simdiki
print("başlangıç dizgisi ölçümden sonra:", repr(HAM))

METIN = "12; kuzey; 18"
SORGULAR = (("startswith('12')", lambda s: s.startswith("12")),
            ("find('kuzey')", lambda s: s.find("kuzey")),
            ("count(';')", lambda s: s.count(";")),
            ("isdigit()", lambda s: s.isdigit()),
            ("len(s)", lambda s: len(s)))

print(f"\n{'sorgu yöntemi':<20s} {'dönen tip':<5s} değer")
for ad, islev in SORGULAR:
    deger = islev(METIN)
    print(f"{ad:<20s} {type(deger).__name__:<5s} {deger!r}")
print("sorgulardan sonra dizgi:", repr(METIN))
```

```
dizgiye yerinde yazma: TypeError

adım                 tip   önceki nesne mi  değer
başlangıç            str              True  '  12; kuzey; 18  '
strip()              str             False  '12; kuzey; 18'
upper()              str             False  '12; KUZEY; 18'
replace(';', ',')    str             False  '12, KUZEY, 18'
split(',')           list            False  ['12', ' KUZEY', ' 18']
başlangıç dizgisi ölçümden sonra: '  12; kuzey; 18  '

sorgu yöntemi        dönen tip değer
startswith('12')     bool  True
find('kuzey')        int   4
count(';')           int   2
isdigit()            bool  False
len(s)               int   13
sorgulardan sonra dizgi: '12; kuzey; 18'
```

İlk satır kısıtı adıyla koyuyor: köşeli ayraçla yazma `TypeError` veriyor, çünkü dizgi
tipinde bu işlemi karşılayan yöntem yok. Önceki derste bölme işleci için alınan sonucun
aynısıdır — kusur bir yasak değil, bir yokluktur.

Zincirdeki dört adımın dördünde de "önceki nesne mi" sütunu `False`. Her yöntem yeni bir
nesne kurdu ve girdisine dokunmadı; son satır bunu doğruluyor — dört işlemden sonra
başlangıç dizgisi olduğu gibi duruyor. Bunun bir seçim olmadığını görmek önemlidir. Bir
dizgi yöntemi girdisini değiştirmek **isteseydi bile** yapamazdı; tek yapabileceği şey yeni
bir nesne kurup döndürmektir. Değiştirilemezlik, yöntemin sözleşmesini kendisi yazar; bu
yüzden dizgi tipinde listedeki `append` ya da `sort` gibi yerinde çalışan bir yöntem
bulunmaz.

Buradan doğrudan bir kural çıkar: **dizgi yöntemlerinin dönüş değeri kullanılmazsa hiçbir şey
olmamıştır.** `metin.strip()` satırı tek başına yazıldığında yeni bir nesne kurar, ada
bağlamaz ve atar; `metin` eskisi gibi kalır. Bu, dile yeni gelen kodda en sık görülen sessiz
kusurdur.

Alt tablo yöntemlerin ikinci sınıfını ayırıyor. `startswith`, `find`, `count`, `isdigit` ve
`len` bir dizgi üretmez; sırasıyla bir doğruluk değeri, bir konum, bir sayım verir. Bunlar
dizgiyi dönüştürmez, ona bir soru sorar — dolayısıyla yeni nesne tartışmasının dışındadır.
`find` yöntemi aranan parçanın konumunu, bulamazsa `-1` döndürür; bir istisna fırlatmaz.
Aynı işi yapan `index` yöntemi ise bulamadığında `ValueError` fırlatır. İki yöntem arasındaki
seçim üslup değildir: bulunamamak beklenen bir durumsa dönüş değeriyle çalışan yazım, olmaması
gereken bir durumsa istisna fırlatan yazım seçilir. Aynı ayrımın dönüşüm tarafındaki
karşılığı sonraki dersin konusudur.

## Dizgi Bir Dizidir

Üçüncü dersin yerleşik tip tablosunda `str` beş sütunun beşini de doldurmuştu: uzunluğu
sorulabiliyor, üzerinde döngü kurulabiliyor, üyelik sınanabiliyor, dilimlenebiliyor ve
toplanabiliyordu. Bu, dizginin yalnız bir metin değil, aynı zamanda bir **dizi** olduğu
anlamına gelir — ve dizi olmasının iki gözlenebilir sonucu vardır.

- **DT34** — Dilim ölçümünde her sonuç kaynak dizgiyle `is` ile karşılaştırılır; kaynağın
  tamamını isteyen bir dilim kullanılmaz, çünkü onun aynı nesneyi döndürüp döndürmemesi
  gerçekleştirime bağlıdır.
- **DT35** — Kaçış ölçümünde yazılan üç yazımın **karakter sayısı** okunur; sayı, yazımın
  kaç karakter ürettiğini gösterir, kaynakta kaç karakter yazıldığını değil.

```python
"""Dizgi bir dizidir: dilim, yineleme ve uyelik ayni protokollere dayanir."""

METIN = "12; kuzey; 18"

DILIMLER = (("[0]", lambda s: s[0]),
            ("[-1]", lambda s: s[-1]),
            ("[4:9]", lambda s: s[4:9]),
            ("[:2]", lambda s: s[:2]),
            ("[::-1]", lambda s: s[::-1]),
            ("[::4]", lambda s: s[::4]))

print(f"{'dilim':<8s} {'kaynakla aynı nesne mi':>22s}  değer")
for ad, islev in DILIMLER:
    parca = islev(METIN)
    print(f"{ad:<8s} {str(parca is METIN):>22s}  {parca!r}")

print(f"\nuzunluk {len(METIN)}, ilk dört öğe {list(METIN)[:4]}")
print("üyelik:", "'kuz' in metin ->", "kuz" in METIN,
      "| 'zey' in metin ->", "zey" in METIN,
      "| 'yez' in metin ->", "yez" in METIN)
print("listede aynı soru:", "[1, 2] in [1, 2, 3] ->", [1, 2] in [1, 2, 3],
      "| 2 in [1, 2, 3] ->", 2 in [1, 2, 3])

KACIS = (("'a\\tb'", "a\tb"), ("r'a\\tb'", r"a\tb"),
         ("'''iki\\nsatır'''", "iki\nsatır"))
print(f"\n{'yazım':<18s} {'karakter':>8s}  gerçek içerik")
for ad, deger in KACIS:
    print(f"{ad:<18s} {len(deger):8d}  {deger!r}")
```

```
dilim    kaynakla aynı nesne mi  değer
[0]                       False  '1'
[-1]                      False  '8'
[4:9]                     False  'kuzey'
[:2]                      False  '12'
[::-1]                    False  '81 ;yezuk ;21'
[::4]                     False  '1ky8'

uzunluk 13, ilk dört öğe ['1', '2', ';', ' ']
üyelik: 'kuz' in metin -> True | 'zey' in metin -> True | 'yez' in metin -> False
listede aynı soru: [1, 2] in [1, 2, 3] -> False | 2 in [1, 2, 3] -> True

yazım              karakter  gerçek içerik
'a\tb'                    3  'a\tb'
r'a\tb'                   4  'a\\tb'
'''iki\nsatır'''          9  'iki\nsatır'
```

Altı dilimin altısı da kaynaktan ayrı bir nesne veriyor. Dilimleme ilk derste ölçülen
`__getitem__` çağrısıdır ve dizgide de aynı kuralı taşır: yeni bir nesne kurulur, kaynak
olduğu gibi kalır. Eksi konum sondan sayar, üçüncü sayı adımı belirler; `[::-1]` yazımının
metni ters çevirmesi ayrı bir yöntemin işi değil, adımın eksi olmasının sonucudur.

Yineleme satırı dizginin öğelerinin **karakter** olduğunu gösteriyor: on üç uzunluğundaki
metin on üç öğe verir ve her öğe yine bir dizgidir. Python'da tek bir karakteri temsil eden
ayrı bir tip yoktur; bir karakter, uzunluğu bir olan dizgidir.

Üyelik satırı ise dizgiyi öteki dizilerden ayırıyor. `"kuz" in metin` **True** veriyor —
oysa aranan şey bir öğe değil, bir öğe **dizisi**. Aynı soru listede sorulduğunda
`[1, 2] in [1, 2, 3]` **False** verir, çünkü listede `in` yalnız öğe arar. Dizginin
`__contains__` yöntemi bilerek başka türlü yazılmıştır: alt dizgi arar. Sözdizim iki tipte
aynıdır, sözleşme değildir.

Son tablo yazım biçimlerini ayırıyor. Ters bölülü kaçış dizileri kaynakta iki karakterle
yazılır ama **tek** karakter üretir: `'a\tb'` yazımı üç karakterlik bir dizgidir. Başına
`r` konan ham yazımda kaçış çözülmez ve aynı kaynak dört karakter verir. Üç tırnaklı yazım
ise satır sonunu olduğu gibi taşır. Üçünde de kaynakta yazılan karakter sayısı ile üretilen
dizginin uzunluğu ayrıdır; dosya yolu ya da örüntü metni yazarken ham yazımın tercih edilme
nedeni budur.

## Birleştirmenin Bedeli

Değiştirilemezliğin ölçülebilir bedeli, parça parça metin kurarken ortaya çıkar. Her `+`
işlemi yeni bir nesne kurduğu için, bir döngü içinde büyütülen dizgi yol boyunca kurulup
atılan ara nesneler bırakır.

- **DT36** — İki yöntem aynı yedi parçadan aynı metni kurar; ölçülen şey yol boyunca
  **kurulan ara nesne sayısıdır**, geçen süre değil. Artırmalı yöntemde her adımın sonucu
  `is` ile bir öncekiyle karşılaştırılarak sayılır; birleştirme tek nesne kurduğu için
  sayısı doğrudan yazılır.

```python
"""Birlestirmenin bedeli: kac ara nesne kuruluyor?"""

PARCALAR = ("kuzey", "; ", "12", "; ", "yamac", "; ", "18")


def artirmali(parcalar):
    metin = ""
    ara = 0
    for p in parcalar:
        onceki = metin
        metin = metin + p          # her adimda yeni bir dizgi
        ara += metin is not onceki
    return metin, ara


def birlestirerek(parcalar):
    metin = "".join(parcalar)      # tek adimda tek nesne
    return metin, 1


print(f"{'yöntem':<14s} {'parça':>5s} {'kurulan ara nesne':>18s}  sonuç")
for ad, islev in (("+ ile artırma", artirmali), ("join ile", birlestirerek)):
    sonuc, ara = islev(PARCALAR)
    print(f"{ad:<14s} {len(PARCALAR):5d} {ara:18d}  {sonuc!r}")

print("iki sonuç eşit mi:", artirmali(PARCALAR)[0] == birlestirerek(PARCALAR)[0])
```

```
yöntem         parça  kurulan ara nesne  sonuç
+ ile artırma      7                  7  'kuzey; 12; yamac; 18'
join ile           7                  1  'kuzey; 12; yamac; 18'
iki sonuç eşit mi: True
```

Yedi parça, artırmalı yöntemde **7** ara nesne kuruyor; `join` **1** kuruyor. Sonuçlar
birebir eşit. Örüntü parça sayısıyla doğrusal büyür: parça sayısı ikiye katlandığında
artırmalı yöntemin kurduğu nesne de ikiye katlanır, `join`'inki **1**'de kalır.

Farkın nedeni yöntemin daha iyi yazılmış olması değil, **bilgiye önceden sahip olmasıdır**.
`join` bütün parçaları başta görür, toplam uzunluğu hesaplayabilir ve sonucu tek seferde
kurar. `+` işleci ise her çağrıda yalnız iki parça görür; sonraki adımda ne geleceğini
bilemediği için o adımın sonucunu tam olarak kurmak zorundadır.

Aynı gerekçe, listeye toplayıp sonunda birleştirmenin neden yaygın bir kalıp olduğunu da
açıklar: liste **değiştirilebilir** bir tiptir, ona ekleme yapmak yeni nesne kurmaz. Metin
yalnız en sonda, bir kez üretilir.

`join` yönteminin bir koşulu vardır ve gözden kaçmaya elverişlidir: parçaların **hepsi
dizgi olmak zorundadır**. Aralarına bir sayı karışırsa yöntem onu kendiliğinden metne
çevirmez, `TypeError` fırlatır. Yöntemin çağrıldığı dizgi de parçalardan biri değil,
**ayraçtır**: `"; ".join(...)` yazımında araya giren metin, çağrının yapıldığı dizgidir.

## Biçimlendirme Hangi Protokolü Çağırır

Metin üretmenin yazımları birden çoktur ve aynı görünürler. Kursun ölçüsü burada da işler:
her yazımın çağırdığı yöntem ayrıdır ve ürettikleri metin bu yüzden ayrışabilir.

- **DT37** — Ölçülen nesne üç metin protokolüne de katılır: `__str__`, `__repr__` ve
  `__format__`. Üçü ayrı metin üretir, böylece hangisinin çağrıldığı çıktıdan da okunabilir.
- **DT38** — `__format__` yönteminin kendisi hiçbir başka protokolü çağırmaz; kalıp
  boşsa kendi metnini kurar, doluysa kalıbı taşıdığı sayıya uygular. Böylece kayıt yalnız
  yazımın çağırdığını gösterir.

```python
"""Bicimlendirme yazimlari hangi ozel yontemi cagiriyor?"""

KAYIT = []


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


class Olcum:
    """Uc metin protokolune de katilir ve her katilimini kaydeder."""

    def __init__(self, deger=12):
        self.deger = deger

    def __str__(self):
        kaydet("__str__")
        return f"olcum {self.deger}"

    def __repr__(self):
        kaydet("__repr__")
        return f"Olcum({self.deger})"

    def __format__(self, kalip):
        kaydet("__format__")
        return format(self.deger, kalip) if kalip else f"olcum {self.deger}"


BICIMLER = (
    ('f"{n}"', lambda n: f"{n}"),
    ('f"{n:>6}"', lambda n: f"{n:>6}"),
    ('f"{n!s}"', lambda n: f"{n!s}"),
    ('f"{n!r}"', lambda n: f"{n!r}"),
    ('"{}".format(n)', lambda n: "{}".format(n)),
    ('"%s" % n', lambda n: "%s" % n),
    ('str(n)', lambda n: str(n)),
    ('repr(n)', lambda n: repr(n)),
)

tekil = set()
print(f"{'yazım':<16s} {'protokol':<12s} ürettiği metin")
for ad, islev in BICIMLER:
    KAYIT.clear()
    metin = islev(Olcum())
    tekil.update(KAYIT)
    print(f"{ad:<16s} {' '.join(KAYIT):<12s} {metin!r}")

print(f"\n{len(BICIMLER)} yazım, ayrı protokol {len(tekil)}: "
      f"{', '.join(sorted(tekil))}")
```

```
yazım            protokol     ürettiği metin
f"{n}"           __format__   'olcum 12'
f"{n:>6}"        __format__   '    12'
f"{n!s}"         __str__      'olcum 12'
f"{n!r}"         __repr__     'Olcum(12)'
"{}".format(n)   __format__   'olcum 12'
"%s" % n         __str__      'olcum 12'
str(n)           __str__      'olcum 12'
repr(n)          __repr__     'Olcum(12)'

8 yazım, ayrı protokol 3: __format__, __repr__, __str__
```

Sekiz yazım **3 ayrı protokole** dağılıyor. Süslü ayraçlı yazım ile `format` yöntemi
`__format__` çağırıyor; yüzde işaretli yazım ile `str` yerleşiği `__str__` çağırıyor;
`repr` yerleşiği ile `!r` işaretçisi `__repr__` çağırıyor. Aynı nesne üç yazımda üç ayrı
metin veriyor ve hiçbiri ötekinin yerine geçmiyor.

İki nokta ayrıca okunmalı. Birincisi, `__format__` **kalıbı da alır**: ikinci satırda
`>6` kalıbı yönteme geçmiş ve yöntem onu kendi taşıdığı sayıya uygulamıştır. Hizalama,
basamak sayısı ve genişlik kuralları bu yolla nesnenin kendisine bırakılır. İkincisi,
işaretçiler bir kısayol değil, **protokol seçicidir**: `!r` yazımı çağrılacak yöntemi
`__format__`'tan `__repr__`'a çevirir.

Süslü ayraçlı yazımın ayrı bir yanı daha var: ayraçların içine bir değer değil, bir
**ifade** yazılabilir ve ifade orada değerlendirilir. İkinci dersin deyim–ifade ayrımı bu
yüzden burada da işler; ayraç içine bir atama konamaz, çünkü atama bir ifade değildir.
`format` yöntemi ise değerleri argüman olarak alır ve kalıbı ayrı tutar.

Üç yöntemin sözleşmeleri de ayrıdır. `__str__` okunabilir metin verir; `__repr__` nesneyi
tanıtan, çoğunlukla onu yeniden kuracak biçimde yazılmış metin verir — ölçümdeki
`'Olcum(12)'` bunun örneğidir. Bir nesne yalnız `__repr__` tanımlarsa `str` çağrısı da ona
düşer; ilk derste çıplak nesnede görülen davranışın kaynağı budur.

## Özet

- Dizginin bir karakterine yazmak `TypeError` verir; karşılığı olan yöntem tanımlı değildir.
- Üretim yöntemlerinin dördünde de sonuç yeni bir nesnedir ve girdi olduğu gibi kalır;
  dönüş değeri kullanılmazsa hiçbir şey olmamıştır.
- Sorgu yöntemleri dizgi üretmez, başka tipte bir yanıt verir; `find` bulamadığında `-1`
  döndürür, istisna fırlatmaz.
- Dizgi bir dizidir: dilim `__getitem__` çağırır ve yeni nesne verir, öğeleri karakterdir,
  `in` sınaması ise listeden farklı olarak **alt dizgi** arar.
- Yedi parçayı artırmalı birleştirmek **7** ara nesne kurar, `join` **1** kurar; fark
  `join`'in bütün parçaları başta görmesinden gelir.
- Sekiz biçimlendirme yazımı **3 ayrı protokole** dağılır: `__format__`, `__str__` ve
  `__repr__`; `!r` gibi işaretçiler çağrılacak yöntemi değiştirir.

## Sonraki Adım

Bu derste `str(n)` yazımının `__str__` çağırdığı ölçüldü — yani bir nesneyi metne çevirmek
de bir protokol çağrısıymış. Aynı soru ters yönde de sorulabilir: `int("12")` yazıldığında
ne çağrılıyor, `float(n)` bir nesneden neyi istiyor ve `int("kuzey")` gibi karşılığı olmayan
bir dönüşüm istendiğinde ne oluyor? Sonraki ders dönüşümün hangi özel yöntemlere dayandığını
sayar ve başarısız dönüşümün bir dönüş değeri değil, bir **istisna** ürettiğini gösterir.
