Neden önemli?
Kod bir kez yazılır, onlarca kez okunur: kod incelemesinde (code review), hata ayıklarken, altı ay sonra yeni özellik eklerken. Ilgaz Bank'ın faiz modülünü yazan kişi izne çıktığında, def f(a, b, c) ve içinde * 0.0175 gibi açıklamasız sayılar gören ekip arkadaşı bir günü yalnızca "bu ne yapıyor" sorusuna harcar. Temiz kod (clean code), çalışan kodun bir üst basamağıdır: doğru çalışan ve kolay anlaşılan kod.
Python topluluğunun ortak stil kılavuzu PEP 8'dir (Python Enhancement Proposal 8). Kuralların çoğu zevk meselesi gibi görünür ama asıl amaç tutarlılıktır: herkes aynı biçimde yazınca göz biçime değil içeriğe odaklanır. Değişken adlandırmanın temellerini "Değişkenler" dersinde gördün; burada bütün bir programa bakıyoruz.
Adlandırma kuralları
| Ne? | Biçim | Örnek |
|---|---|---|
| Değişken, fonksiyon, parametre, modül | snake_case | aylik_taksit, hesap_ozeti(), rapor.py |
| Sınıf | PascalCase (CapWords) | KrediBasvurusu, Siparis |
| Sabit (modül düzeyinde, değişmeyen) | UPPER_CASE | KDV_ORANI, MAX_DENEME |
| "İçeride kullan" işareti | _ ile başla | _yardimci(), _onbellek |
| Kaçınılacak | l, O, I tek harfleri; yerleşik adlar | l = 1 (1 mi l mi?), list = [...] |
Anlamlı ad ise kuraldan da önemlidir. Adın uzunluğu kapsamıyla orantılı olsun: üç satırlık bir döngüde i yeterlidir, ama modül boyunca yaşayan bir değişken d olamaz. Mantıksal (bool) değerlere soru gibi okunan adlar ver: odendi_mi, stok_var. Fonksiyon adı bir fiil olsun: hesapla, getir, dogrula.
# Önce: ne yaptığını anlamak için kodu çözmen gerekir
def f(l):
t = 0
for x in l:
if x[1]:
t += x[0]
return t
# Sonra: adlar kodu anlatır
def odenen_toplam(faturalar):
toplam = 0
for tutar, odendi_mi in faturalar:
if odendi_mi:
toplam += tutar
return toplam
faturalar = [(1200, True), (450, False), (300, True)]
print(f(faturalar), odenen_toplam(faturalar))
öğrenci_sayısı = 5 çalışır). Yine de çoğu ekip klavye, arama ve araç uyumu için adları ASCII yazar: ogrenci_sayisi. Bu uygulamada da öyle yapıyoruz. Uluslararası ekiplerde adlar genellikle İngilizcedir; önemli olan bir projede tek dil seçmektir.Girinti, boşluk ve satır uzunluğu
- Girinti 4 boşluk; sekme (tab) ile boşluk asla karışmaz.
- Atama ve karşılaştırma operatörlerinin iki yanında birer boşluk:
toplam = a + b. Varsayılan parametrede boşluk yok:def f(oran=0.2). - Virgülden sonra boşluk, parantezin içine boşluk yok:
f(a, b),f( a,b )değil. - Satır en fazla 79 karakter (birçok ekip black'in varsayılanı olan 88'i kullanır). Uzun satırı ters bölü ile değil, parantez içinde böl.
- Üst düzey fonksiyon ve sınıflar arasında iki boş satır, sınıf içindeki metotlar arasında bir boş satır.
KDV_ORANI = 0.20
KARGO_UCRETI = 39.90
INDIRIM_ESIGI = 1000
def sepet_toplami(ara_toplam, uye_mi):
indirim = 0.05 if uye_mi and ara_toplam >= INDIRIM_ESIGI else 0
genel_toplam = (
ara_toplam * (1 - indirim) * (1 + KDV_ORANI)
+ KARGO_UCRETI
)
return round(genel_toplam, 2)
print(sepet_toplami(1200, uye_mi=True))
print(sepet_toplami(400, uye_mi=False))
Operatörün satır başında olması (+ KARGO_UCRETI) PEP 8'in güncel önerisidir: matematikte olduğu gibi gözün hangi işlemin yapıldığını satır başında görür.
Import sırası
Dosyanın en üstünde, docstring'den hemen sonra, üç grup hâlinde ve aralarında birer boş satırla:
"""Anadolu Market günlük ciro raporu.""" import csv # 1) standart kütüphane import datetime as dt from pathlib import Path import pandas as pd # 2) üçüncü taraf (pip ile kurulanlar) from rapor.ayarlar import KDV_ORANI # 3) projenin kendi modülleri
Her grup kendi içinde alfabetiktir; her satırda tek modül içe aktarılır (import os, sys değil). from modul import * hangi adların geldiğini gizlediği için kullanılmaz. Bu sırayı elle tutmana gerek yok: ruff (ruff check --select I --fix) ya da isort senin yerine dizer.
Yorum ve docstring
İyi yorum kodun ne yaptığını değil neden öyle yaptığını anlatır. # i'yi 1 artır gürültüdür; # Banka hafta sonu çalışmadığı için valörü cumaya çekiyoruz altın değerindedir. Kod değişip yorum değişmezse yorum yalan söylemeye başlar; bu yüzden kodu kendini anlatacak kadar açık yaz, yorumu iş kuralı ve tuhaflıklar için sakla.
Docstring, fonksiyonun, sınıfın ya da modülün ilk satırına yazılan üç tırnaklı metindir. Yorumdan farkı: Python onu saklar, help() ve editörler gösterir. İlk satır tek cümlelik özettir; ardından boş satır ve ayrıntılar gelir. İki yaygın biçim vardır:
def aylik_taksit(anapara, yillik_faiz, vade_ay):
"""Eşit taksitli kredinin aylık taksitini hesaplar.
Args:
anapara: Çekilen kredi tutarı (TL).
yillik_faiz: Yıllık faiz oranı, 0.42 gibi ondalık.
vade_ay: Taksit sayısı.
Returns:
2 ondalığa yuvarlanmış aylık taksit.
Raises:
ValueError: vade_ay pozitif değilse.
"""
if vade_ay <= 0:
raise ValueError("vade pozitif olmalı")
r = yillik_faiz / 12
if r == 0:
return round(anapara / vade_ay, 2)
return round(anapara * r / (1 - (1 + r) ** -vade_ay), 2)
print(aylik_taksit(100_000, 0.42, 12))
print(aylik_taksit.__doc__.splitlines()[0])
Yukarıdaki Google stilidir (Args / Returns / Raises). Veri bilimi kütüphanelerinde (numpy, pandas) yaygın olan NumPy stili başlıkları çizgiyle ayırır:
def ortalama_sepet(tutarlar):
"""Ortalama sepet tutarını döndürür.
Parameters
----------
tutarlar : list of float
Sipariş tutarları.
Returns
-------
float
Ortalama; liste boşsa 0.0.
"""
Hangisini seçtiğin değil, projede tek birini tutarlı kullanman önemlidir. Tip ipuçları (type hints) da belgelemenin parçasıdır: def aylik_taksit(anapara: float, yillik_faiz: float, vade_ay: int) -> float yazınca docstring'de türleri tekrar etmene gerek kalmaz.
Sihirli sayılardan kaçın
Kodun ortasındaki açıklamasız sayı "sihirli sayı"dır (magic number). if puan > 1250 satırında 1250'nin ne olduğunu bilen tek kişi yazandır. Sayıyı adı olan bir sabite taşı: hem anlam kazanır hem de değiştiğinde tek yerden güncellenir.
# Önce
def kart_limiti(gelir):
if gelir > 85000:
return gelir * 3
return gelir * 1.5
# Sonra
YUKSEK_GELIR_ESIGI = 85_000
YUKSEK_GELIR_CARPANI = 3
STANDART_CARPAN = 1.5
def kart_limiti_temiz(aylik_gelir):
if aylik_gelir > YUKSEK_GELIR_ESIGI:
return aylik_gelir * YUKSEK_GELIR_CARPANI
return aylik_gelir * STANDART_CARPAN
print(kart_limiti(90000) == kart_limiti_temiz(90000), kart_limiti_temiz(40000))
0, 1, -1 gibi bariz değerler ve range(len(x)) gibi kullanımlar sabite çevrilmez; kural "anlamı açık olmayan sayı"lar içindir. 85_000 yazımı da bir stil inceliğidir: alt çizgi sayıyı okunur yapar, değerini değiştirmez.
Küçük fonksiyon, DRY, erken dönüş
Tek sorumluluk: bir fonksiyon tek iş yapsın ve adı o işi söylesin. Adında "ve" geçiyorsa (oku_ve_hesapla_ve_yazdir) bölme zamanıdır. Kabaca ekrana sığmayan fonksiyon, büyük olasılıkla fazla iş yapıyordur.
DRY (Don't Repeat Yourself, kendini tekrar etme): aynı mantık iki yerde yazılırsa bir gün yalnızca birinde güncellenir ve iki yer birbirini tutmaz. Üç kez kopyalanmış bir blok, bir fonksiyonun habercisidir.
Erken dönüş (early return, guard clause): iç içe if merdiveni yerine geçersiz durumları başta eleyip ana yolu düz bırak.
# Önce: iç içe merdiven
def kredi_karari_eski(yas, gelir, borc_orani):
if yas >= 18:
if gelir >= 20000:
if borc_orani <= 0.5:
return "ön onay"
else:
return "red: borç"
else:
return "red: gelir"
else:
return "red: yaş"
# Sonra: erken dönüş
def kredi_karari(yas, gelir, borc_orani):
if yas < 18:
return "red: yaş"
if gelir < 20000:
return "red: gelir"
if borc_orani > 0.5:
return "red: borç"
return "ön onay"
for basvuru in [(17, 50000, 0.1), (30, 15000, 0.1), (30, 40000, 0.7), (30, 40000, 0.2)]:
print(kredi_karari(*basvuru), kredi_karari_eski(*basvuru) == kredi_karari(*basvuru))
Zen of Python
Python'ın tasarım felsefesi 19 kısa ilkeyle yorumlayıcının içine gömülüdür. Çalıştır ve oku:
import this
En çok alıntılananlar: Beautiful is better than ugly (güzel, çirkinden iyidir), Explicit is better than implicit (açık olan örtük olandan iyidir: from x import * bu yüzden kötüdür), Readability counts (okunabilirlik önemlidir), Errors should never pass silently (hatalar sessizce geçmemeli: çıplak except: pass bu yüzden kötüdür), There should be one obvious way to do it (bir işi yapmanın tek bariz yolu olmalı).
ruff ve black: stili makineye bırak
Kuralları ezberlemek yerine araçlara uygulat. black bir biçimlendiricidir (formatter): kodu tek ve tartışmasız bir stile sokar. ruff ise çok hızlı bir denetleyicidir (linter): kullanılmayan import, tanımsız ad, etkisiz ifade, import sırası gibi yüzlerce kuralı denetler; ruff format ile black uyumlu biçimlendirme de yapar.
pip install ruff black ruff check . # sorunları listele ruff check . --fix # düzeltilebilenleri düzelt ruff format . # biçimlendir (black uyumlu) black rapor.py # ya da yalnızca black
Ayarlar proje kökündeki pyproject.toml içinde tutulur, böylece ekipteki herkes aynı kuralları kullanır:
[tool.ruff] line-length = 88 [tool.ruff.lint] select = ["E", "F", "I", "N", "B"] # pycodestyle, pyflakes, import, adlandırma, bugbear
pip install ruff ile kur. VS Code'da "Ruff" eklentisini kurup "Format on Save" açarsan her kayıtta dosya kendiliğinden düzelir. Bu derste bazı denetimleri Python'ın kendi re ve ast modülleriyle nasıl yapabileceğini alıştırmalarda göreceksin.Tutarlılık, kuraldan önce gelir
PEP 8'in kendisi bir bölümüne "A Foolish Consistency is the Hobgoblin of Little Minds" başlığını koyar: proje içi tutarlılık PEP 8'den, fonksiyon içi tutarlılık proje kuralından önemlidir. Eski bir kod tabanına camelCase ile yazılmışsa, tek fonksiyonu snake_case'e çevirmek karmaşayı artırır; ya hepsini (bir araçla, ayrı bir değişiklikte) çevirirsin ya da mevcut stile uyarsın.
Bilişsel karmaşıklık
Kodun zorluğunu satır sayısı değil, okurun aklında aynı anda tutması gereken şey sayısı belirler. Her iç içe if, her döngü, her bayrak (flag) değişkeni bu yükü artırır. Erken dönüş, küçük yardımcı fonksiyonlar ve anlamlı ara değişkenler (uygun_mu = yas >= 18 and gelir >= ESIK) yükü azaltır. ruff'ın C901 kuralı karmaşıklığı sayıp sınırı aşan fonksiyonları işaretler.
Yeniden düzenleme (refactoring) güvenli yapılır
Refactor, davranışı değiştirmeden yapıyı iyileştirmektir. Güvenli yolu: önce mevcut davranışı test et, küçük bir adım at, testleri çalıştır, tekrarla. Bu dersin refactor alıştırmaları tam olarak bunu yapar: testler hem eski davranışın korunduğunu hem de yapının düzeldiğini denetler. Orta seviyedeki "Test yazma" dersinde bu güvenlik ağını kendin kuracaksın.