Ana içeriğe geç
Kesit Python
Temel · Fonksiyonlar ve modüller

Metin biçimlendirme ve rapor üretme

f-string ile hizalama, genişlik, binlik ayırıcı, yüzde ve ondalık biçimlerini öğrenir; format(), çok satırlı metin ve ljust/rjust ile Ilgaz Bank hesap özeti gibi düzgün tablolu raporlar üretirsin.

Uygulamada aç, 21 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ç.

Neden biçimlendirme?

Ilgaz Bank'ın müşteri hizmetleri ekibi her sabah bir hesap özeti görmek istiyor: hesap adı, bakiye, son işlem. print(ad, bakiye) ile çıkan "Ayşe 12500.5" satırı doğru ama okunmaz; bakiyeler alt alta hizalanmıyor, binlikler ayrılmıyor, kuruşlar bazen bir bazen iki hane. Raporun işe yaraması için metni biçimlendirmen (formatting) gerekir. Python'da bunun ana aracı f-string; bu derste süslü parantezin içindeki iki noktadan sonrasını, yani biçim belirtecini (format spec) öğreneceksin.

f-string hatırlatma ve biçim belirteci

f"..." içinde {ifade} yazarsın; Python ifadeyi hesaplayıp metne gömer. İki nokta ile belirteç eklersin: {deger:belirtec}. Belirtecin genel sırası şöyledir: dolgu + hizalama + genişlik + binlik + .ondalık + tür. Her parça isteğe bağlıdır.

bakiye = 12500.5
print(f"[{bakiye}]")           # ham
print(f"[{bakiye:.2f}]")       # 2 ondalık
print(f"[{bakiye:,.2f}]")      # binlik ayırıcı + 2 ondalık
print(f"[{bakiye:12,.2f}]")    # 12 karakter genişlik, sağa hizalı (sayı varsayılanı)
print(f"[{bakiye:<12,.2f}]")   # sola hizalı
print(f"[{bakiye:^12,.2f}]")   # ortalı
print(f"[{bakiye:*>12,.2f}]")  # yıldızla doldur, sağa hizala

Sayılar varsayılan olarak sağa, metinler sola hizalanır. Bu raporlarda tam istediğin şeydir: sütun başlığı solda, tutarlar sağda alt alta.

BelirteçAnlamıÖrnekSonuç
:.2fSabit 2 ondalıkf"{3.14159:.2f}"3.14
:,Binlik ayırıcı (virgül)f"{1234567:,}"1,234,567
:_Binlik ayırıcı (alt çizgi)f"{1234567:_}"1_234_567
:.1%Yüzde, 1 ondalık (100 ile çarpar)f"{0.1575:.1%}"15.8%
:10En az 10 karakter genişlikf"{'ad':10}"ad ve 8 boşluk
:>10 / :<10 / :^10Sağa / sola / ortaya hizalaf"{'ad':^6}"" ad "
:05dTam sayı, sıfırla 5 haneye doldurf"{42:05d}"00042
:+.1fİşareti her zaman gösterf"{250.0:+.1f}"+250.0
:eBilimsel gösterimf"{1500000:.2e}"1.50e+06
oran = 0.1575
siparis_no = 42
fark = -1250.75
print(f"Faiz oranı: {oran:.2%}")
print(f"Sipariş no: {siparis_no:06d}")
print(f"Değişim: {fark:+,.2f} TL")
print(f"Başlık: {'Özet':-^30}")
print(f"{'Hesap':<12}{'Bakiye':>14}")
print(f"{'Ayşe':<12}{12500.5:>14,.2f}")
print(f"{'Mehmet Can':<12}{830:>14,.2f}")
Hata ayıklama (debugging) kısayoluf"{bakiye=}" yazımı hem adı hem değeri basar: bakiye=12500.5. Değişken takibi için print'e en hızlı yol.
Türkçe binlik ve ondalıkPython'ın , ayırıcısı İngiliz biçimidir (1,234.50). Türkçe "1.234,50" için en pratik yol biçimlendirip karakterleri takas etmektir: f"{x:,.2f}".replace(",", "X").replace(".", ",").replace("X", "."). locale modülü de var ama sistem ayarına bağlıdır, tarayıcıda ve sunucuda farklı davranabilir.

format() ve str.format

f-string Python 3.6'da geldi; eski kodda ve bazı özel durumlarda iki akraba görürsün. format(deger, belirtec) fonksiyonu tek bir değeri biçimlendirir; belirteci bir değişkende tutabildiğin için "kullanıcı hangi ondalık sayısını istediyse" gibi dinamik senaryolarda işe yarar. "...{}...".format(x) yöntemi ise şablonu önceden hazırlayıp sonra doldurmak için kullanılır, örneğin bir ayar dosyasından okunan mesaj kalıbında.

x = 1234.5678
print(format(x, ",.2f"))
print(format(x, ">15.1f"))

ondalik = 3
print(f"{x:.{ondalik}f}")          # f-string içinde iç içe belirteç

sablon = "Sayın {ad}, {tarih} itibarıyla bakiyeniz {bakiye:,.2f} TL'dir."
print(sablon.format(ad="Ayşe Yılmaz", tarih="28.10.2026", bakiye=12500.5))
print("{0} - {1} - {0}".format("A", "B"))

Günlük kodda tercih sırası: önce f-string, şablon dışarıdan geliyorsa str.format, tek değer ve dinamik belirteç için format(). % işaretli eski biçimlendirmeyi ("%s" % ad) yalnızca eski kodda görürsün; yeni kodda kullanma.

Çok satırlı metin

Üç tırnak, satır sonlarını koruyan metin oluşturur ve f-string ile birleşebilir. Bir hesap özetinin başlığı için biçilmiş kaftan:

musteri = "Ayşe Yılmaz"
hesap_no = "TR33 0006 1005 1978 6457 8413 26"
bakiye = 12500.5
baslik = f"""{'ILGAZ BANK':^40}
{'Hesap Özeti':^40}
{'=' * 40}
Müşteri : {musteri}
IBAN    : {hesap_no}
Bakiye  : {bakiye:,.2f} TL
{'=' * 40}"""
print(baslik)

Girintili bir fonksiyon içinde üç tırnaklı metin yazdığında satır başlarındaki boşluklar da metne girer; o durumda textwrap.dedent yardımcı olur. Tek satırlık uzun metinleri bölmek içinse yan yana yazılan tırnaklı parçalar otomatik birleşir: ("a" "b") → "ab".

Basit tablo yazdırma

Gerçek iş: hesap listesinden hizalı bir tablo üret. Kalıp hep aynıdır: başlık satırı, ayraç çizgisi, döngüde her satır aynı genişliklerle, en altta toplam. Genişlikleri bir yerde sabit tutarsan başlık ve satırlar asla kaymaz.

hesaplar = [
    ("Ayşe Yılmaz", "Vadesiz", 12500.5, 3),
    ("Mehmet Can", "Vadeli", 830, 0),
    ("Şirin Öztürk", "Vadesiz", 148200.75, 12),
    ("İlker Ağaoğlu", "Kredi", -5400, 1),
]
G_AD, G_TUR, G_BAK, G_ISL = 16, 9, 14, 6
print(f"{'Müşteri':<{G_AD}}{'Tür':<{G_TUR}}{'Bakiye (TL)':>{G_BAK}}{'İşlem':>{G_ISL}}")
print("-" * (G_AD + G_TUR + G_BAK + G_ISL))
toplam = 0
for ad, tur, bakiye, islem in hesaplar:
    print(f"{ad:<{G_AD}}{tur:<{G_TUR}}{bakiye:>{G_BAK},.2f}{islem:>{G_ISL}}")
    toplam += bakiye
print("-" * (G_AD + G_TUR + G_BAK + G_ISL))
print(f"{'TOPLAM':<{G_AD + G_TUR}}{toplam:>{G_BAK},.2f}")

Genişlikleri de süslü parantezle gömebildiğine dikkat et: {ad:<{G_AD}}. Sütun genişliğini verinin en uzun elemanından hesaplamak istersen max(len(h[0]) for h in hesaplar) + 2 yeter.

Türkçe karakter ve ljust/rjust

str.ljust(n), str.rjust(n) ve str.center(n) f-string hizalamasının yöntem hâlidir; ikinci argümanla dolgu karakteri alırlar. Bir listeyi sütun sütun yazdırırken f-string kadar sık kullanılır.

print("Şube".ljust(10, ".") + "|")
print("İzmir".rjust(10) + "|")
print("Çağrı".center(11, "*") + "|")
print(len("İzmir"), len("Izmir"))
print(len("Şirin Öztürk"), len("Sirin Ozturk"))
for sehir in ["İstanbul", "Çorum", "Iğdır", "Muş"]:
    print(sehir.ljust(10) + "|" + str(len(sehir)).rjust(3))

Python 3 metinleri Unicode olarak tutar; len("İzmir") 5'tir, hizalama Türkçe harflerle de doğru çalışır. İki yerde dikkat: büyük-küçük dönüşümünde "ı".upper() "I", ama "I".lower() "i" verir (Türkçe noktasız ı Unicode'un genel kuralında yoktur), bu yüzden şehir adlarını karşılaştırırken casefold yerine sabit bir sözlükten yararlanmak daha güvenlidir. Ve terminal fontu bazı karakterleri farklı genişlikte çizebilir; hizalama karakter sayısına göredir, piksele göre değil.

Para ile floatRaporda :.2f kuruşu düzgün gösterir ama arka planda 0.1 + 0.2 hâlâ 0.30000000000000004'tür. Gösterim için f-string, hesap için (özellikle bankacılıkta) decimal.Decimal kullanılır. Orta seviyede ayrıntısına gireceğiz; şimdilik biçimlendirmenin hesabı değil görünümü düzelttiğini unutma.
Derinleş

Belirteç dilinin tamamı

Derste belirtecin en sık kullanılan parçalarını gördün. Resmi adıyla biçim belirteci mini dili (format specification mini-language) şu sırayla okunur ve her parça isteğe bağlıdır:

[[dolgu]hizalama][işaret][#][0][genişlik][gruplama][.duyarlık][tür]
ParçaSeçeneklerÖrnekSonuç
Hizalama< > ^ = (işaretten sonra doldur)f"{-3.14:=+9.2f}"- 3.14
İşaret+ (her zaman), - (varsayılan), boşlukf"{12.5:+.1f}"+12.5
#Alternatif biçim: önek eklerf"{255:#x}"0xff
0Sayıyı sıfırla doldurf"{42:06d}"000042
Gruplama, ya da _f"{1234567:_}"1_234_567
Türd f e g % b o x sf"{10:08b}"00001010
degisim = [12.5, -3.25, 0.0]
for d in degisim:
    print(f"{d:+7.2f}%")                # işaret her zaman görünür

siparis_no = 42
print(f"AM-{siparis_no:06d}")           # sıfır dolgulu numara
print(str(siparis_no).zfill(6))          # metin yöntemiyle aynı iş
print(f"{255:#x} {255:#o} {10:08b}")     # onaltılık, sekizlik, ikilik
print(f"{0.256:.1%}", f"{1234.5:g}", f"{0.00001234:g}")

g türü "akıllı" gösterimdir: sayının büyüklüğüne göre sabit ya da bilimsel gösterimi seçer ve gereksiz sıfırları atar. Fatura, sipariş ve müşteri numaralarında ise sıfır dolgusu (06d) standarttır; sıralamada "AM-000042"nin "AM-000100"den önce gelmesini sağlar.

İç içe alanlar: dinamik genişlik

Genişlik ve ondalık sayısı da birer ifade olabilir: belirtecin içine yeni bir {} yazarsın. Sütun genişliğini veriden hesaplayan tablolar böyle kurulur.

urunler = [("çay", 5.5), ("filtre kahve", 120), ("su", 7.25)]
genislik = max(len(ad) for ad, _ in urunler) + 2
ondalik = 2
print(f"{'Ürün':<{genislik}}{'Fiyat':>10}")
print("-" * (genislik + 10))
for ad, fiyat in urunler:
    print(f"{ad:.<{genislik}}{fiyat:>10.{ondalik}f}")

!r, !s ve = ile hata ayıklama

Alanın sonuna !r eklersen değer repr() ile, yani "programcı gözüyle" gösterilir: metinler tırnaklı, görünmeyen boşluklar ve özel karakterler görünür hale gelir. Kullanıcıdan ya da dosyadan gelen bir değerin neden eşleşmediğini anlamanın en hızlı yolu budur. = eki ise ifadenin kendisini de yazar.

sehir = "İzmir "                         # sonda gizli boşluk
print(f"[{sehir}]  [{sehir!r}]")
print(f"{sehir == 'İzmir'=}")
print(f"{len(sehir)=}, {sehir.strip()!r}")
satir = "Ayşe\tİzmir\n"
print(f"{satir!r}")                       # sekme ve satır sonu görünür

Eski % biçimlendirme ve logging

Yeni kodda f-string yazarsın ama % biçimini tanıman gerekir: eski projelerde ve özellikle logging modülünde hâlâ kullanılır. %s metin, %d tam sayı, %.2f ondalık demektir; değerler % işaretinden sonra demet olarak verilir. Tür uyuşmazsa (%d'ye metin vermek) TypeError alırsın.

ad, tutar, adet = "Ayşe", 1250.5, 3
print("%s: %d ürün, %.2f TL" % (ad, adet, tutar))
print("%-8s|%8.1f|" % (ad, tutar))       # - sola hizalar
# logging.info("Sipariş %s alındı", no)  → değer, mesaj gerçekten yazılacaksa birleştirilir

Logging'de logging.info(f"... {no}") yerine logging.info("... %s", no) yazılmasının sebebi budur: mesaj seviyesi kapalıysa birleştirme hiç yapılmaz. Ayrıntısını Loglama dersinde göreceksin.

Dışarıdan gelen şablonlar: string.Template

SMS ya da e-posta metnini pazarlama ekibi yazıyorsa şablon kullanıcıdan gelir. Böyle şablonlarda f-string ve str.format risklidir: format şablonu nesnelerin niteliklerine erişebilir ({musteri.__class__}). string.Template yalnızca $ad biçimindeki basit yer tutucuları doldurur; safe_substitute eksik alanları hata vermeden olduğu gibi bırakır.

from string import Template

sablon = Template("Sayın $ad, $tutar TL tutarlı siparişiniz $kargo ile yola çıktı.")
print(sablon.substitute(ad="Ayşe Kaya", tutar="1.250,00", kargo="Anadolu Kargo"))
print(sablon.safe_substitute(ad="Can"))   # eksikler aynen kalır
try:
    sablon.substitute(ad="Can")
except KeyError as h:
    print("Eksik alan:", h)

Uzun metinler: textwrap

Ürün açıklaması ya da adres gibi uzun metinleri sabit genişlikte satırlara bölmek için textwrap.fill, tek satıra sığdırıp sonunu kısaltmak için textwrap.shorten kullanılır. İkisi de kelimeleri bölmez.

import textwrap
aciklama = "Paslanmaz çelik gövdeli, 1.7 litre kapasiteli, otomatik kapanma özellikli su ısıtıcısı."
print(textwrap.fill(aciklama, width=30))
print(textwrap.shorten(aciklama, width=40, placeholder="..."))
Gerçek işteRaporlama işinin büyük kısmı biçimlendirmedir: muhasebe Türkçe para biçimi ister, banka ekstresi sağa yaslı ve işaretli tutar ister, fatura numaraları sıfır dolguludur, SMS 160 karakteri geçmemelidir. Ekipler bu kuralları bir kez bicim.py gibi küçük bir modülde fonksiyon olarak yazar (tl(), yuzde(), fatura_no()) ve tüm raporlar onları kullanır. Daha büyük HTML/e-posta şablonları için Jinja2'yi Şablonla rapor üretme dersinde göreceksin.
Uzmanlaş

__format__ ve özel biçimler

Belirteçler yalnızca sayı ve metin için değildir. datetime nesneleri kendi belirteçlerini tanır: f"{tarih:%d.%m.%Y}". Kendi sınıfını yazdığında __format__ yöntemini tanımlayarak f"{hesap:kisa}" gibi özel biçimler kurabilirsin. Bu, raporlama kütüphanelerinin arkasındaki mekanizmadır.

Gerçek raporlarda ne kullanılır?

Düz metin tablo, log dosyaları ve terminal araçları için hâlâ standarttır. E-posta ve web raporlarında aynı veriyi HTML tablosuna, Excel'e ya da PDF'e dökersin; pandas'ın to_string, to_markdown ve to_excel yöntemleri bu dersteki kalıbın endüstriyel sürümüdür. tabulate ve rich gibi üçüncü parti paketler de renkli, çerçeveli terminal tabloları üretir. Temel mantık değişmez: genişlik, hizalama, ondalık.

Biçimlendirme ve güvenlik

Kullanıcıdan gelen metni doğrudan str.format şablonu olarak kullanma: kullanici_metni.format(...) yazımı, kötü niyetli bir şablonun nesne özelliklerine erişmesine kapı açar. Şablon senin, veri kullanıcının olsun; f-string bu açıdan daha güvenlidir çünkü şablon kodun içindedir.

Alıştırmalara geç (21)