Modül/paket düzeni ve temel tip ipuçları

Tek dosyada üç-beş sınıfla başlamak öğrenme için uygundur; ancak kod büyüdükçe sorumlulukları dosyalara ayırmak gerekir. Bu hafta küçük bir OOP sistemini modüllere/pakete bölecek ve temel tip ipucu (type hint) kullanımıyla arayüzleri daha okunabilir hâle getireceğiz.

Bir Python paketinin modüllere ayrılması ve fonksiyon imzasında parametre ile dönüş tipi ipuçlarının gösterilmesi.

Bu hafta neleri yapabilmelisiniz?

  • sınıfları sorumluluğa göre farklı modüllere ayırabilmeli,
  • küçük bir Python paket yapısını okuyabilmeli,
  • paket içi ve paket dışı import kullanımını ayırt edebilmeli,
  • modülün public arayüzü fikrini açıklayabilmeli,
  • temel str, int, list[T], dict[K, V] ve T | None tip ipuçlarını kullanabilmeli,
  • type hint’in çalışma zamanı doğrulaması olmadığını açıklayabilmelisiniz.

Tek dosya ne zaman büyür?

Aşağıdaki sınıfların hepsini app.py içinde tuttuğumuzu düşünün:

  • Product
  • Customer
  • OrderLine
  • Order
  • OutOfStockError

Başlangıçta sorun olmayabilir. Fakat dosya büyüdükçe sınıfları bulmak, bağımlılıkları görmek ve değişiklik kapsamını sınırlamak zorlaşır.

Basit bir düzen:

shop/
├── __init__.py
├── product.py
├── customer.py
├── order.py
└── errors.py
app.py

Paket içindeki modüller birbirine göreli import ile erişebilir. Örneğin product.py:

from .errors import OutOfStockError

class Product:
    ...

order.py:

from .product import Product

class Order:
    ...

Paketin dışındaki app.py ise paketi açık adıyla kullanabilir:

from shop.product import Product

Bu geçiş sürümünde göreli/absolute import ayrıntılarını ezber konusu yapmıyoruz. Ama bir import ifadesinin hangi paket bağlamından çalıştığını okuyabilmeniz gerekir. İleri paketleme, build sistemi veya PyPI yayımlama konularına girmiyoruz.

Modül sınırı = sorumluluk sınırı

Dosyaya ayırma yalnızca “her sınıf ayrı dosyada olsun” kuralı değildir. Çok küçük ve birlikte değişen sınıflar aynı modülde kalabilir. Daha önemli sorular:

  • Bu sınıflar aynı kavramsal sorumluluğa mı ait?
  • Bir modüldeki değişiklik başka kaç modülü etkiliyor?
  • Dışarıdaki kod hangi adları kullanmalı?

Public arayüz

Python’da tek alt çizgi yine niyet belirtir:

# pricing.py

def calculate_total(...):
    ...

def _round_money(...):
    ...

calculate_total dış kullanım için, _round_money modül içi yardımcı olarak düşünülebilir. Bu teknik bir güvenlik sınırı değil, bakım sözleşmesidir.

Tip ipucu (type hint) nedir?

list[float] ve -> float kodun beklenen kullanımını açıklar. Python bunları varsayılan olarak çalışma zamanında zorunlu kılmaz.

Şu çağrı sözdizimsel olarak mümkündür:

def add(a: int, b: int) -> int:
    return a + b

print(add("a", "b"))  # type hint bunu çalışma anında otomatik engellemez

Dolayısıyla type hint:

  • okuyucuya niyet gösterir,
  • editör ve statik analiz araçlarına bilgi verir,
  • ancak kendi başına doğrulama değildir.

Sınıflarda tip ipucu

Product | None, aramanın ürün bulamayabileceğini açıkça ifade eder. Dönüş türünde None olasılığı varsa çağıran kod bu durumu hesaba katmalıdır.

None durumunu unutmamak

Aşağıdaki kullanım güvenli değildir:

product = catalog.find("P2")
print(product.code)

find None döndürürse product.code erişimi hata üretir. Tip ipucu bu ihtimali görünür kılar; programcı yine kontrol etmelidir:

product = catalog.find("P2")
if product is not None:
    print(product.code)
else:
    print("Ürün bulunamadı")
ImportantTip ipucu sözleşmeyi görünür kılar, hatayı otomatik çözmez

Product | None yazmak, None değerini ortadan kaldırmaz. Bu bilgi çağıran kodun güvenli kontrol yazmasına yardımcı olur.

Döngüsel import riski

a.py, b.py’yi; b.py de a.py’yi import ederse döngüsel bağımlılık oluşabilir. Bu bazen tasarım sınırlarının birbirine fazla karıştığını gösterir.

Bu derste ileri çözüm tekniklerine girmiyoruz. Öncelikle şu soruyu sorun:

Bu iki modül gerçekten birbirini bilmek zorunda mı, yoksa sorumlulukları yeniden ayırabilir miyiz?

Bu soru Nesne II’deki proje mimarisi için hazırlıktır.

dataclass ve Enum kavram radarı

Kalıcı Nesne I müfredatında dataclass ve Enum daha sistematik işlenecektir. Bu geçiş sürümünde yalnızca ne işe yaradıklarını tanıyın:

  • dataclass: veri ağırlıklı sınıflarda tekrar eden __init__, __repr__, eşitlik gibi kodları azaltabilir.
  • Enum: sınırlı durum değerlerini anlamlı adlarla temsil edebilir.

Bu hafta bunları yazmanız veya sınavda üretmeniz beklenmez.

Alıştırma — Tip ipuçlarını ekle

Aşağıdaki fonksiyona parametre ve dönüş tipi ekleyin:

Beklenen sözleşme:

  • users: dict[int, str]
  • user_id: int
  • sonuç: str | None

Alıştırma — Pakete ayır

Aşağıdaki tek dosyalı sistemi hangi dosyalara ayıracağınızı taslak olarak yazın:

Product
OrderLine
Order
Customer
OutOfStockError
PaymentError

En az iki farklı makul düzen olabilir. Seçiminizi sınıf sayısından çok sorumluluk yakınlığıyla gerekçelendirin.

Alıştırma — None kontrolü

product is None

Küçük üretim görevi — Üç modüllü sistem

Yerel Python ortamınızda küçük bir paket oluşturun:

library/
├── __init__.py
├── book.py
├── member.py
└── library.py
app.py

Her sınıfın temel alanlarına tip ipuçları ekleyin. Library.find_book(isbn) metodu Book | None döndürsün. app.py içinde None durumunu güvenli biçimde yönetin.

Amaç karmaşık paketleme değil; sınıf sorumluluğu, import ve tip sözleşmesini görünür kılmaktır.

Kendinizi kontrol edin

  1. Modüle ayırmanın tek ölçütü “bir sınıf = bir dosya” mıdır?
  2. Paket içindeki .errors gibi göreli import ile paket dışındaki shop.product gibi açık import hangi farklı bağlamlarda kullanılır?
  3. Type hint çalışma zamanında yanlış türü otomatik engeller mi?
  4. T | None neyi ifade eder?
  5. _helper adı ne tür bir niyet belirtir?
  6. Döngüsel import bazen hangi tasarım sorununa işaret edebilir?

Bu haftadan akılda kalması gerekenler

  • Modül/paket sınırları kodun kavramsal sorumluluklarını görünür kılmalıdır.
  • Paket içi ve paket dışı import ifadeleri farklı bağlamlarda okunmalıdır; bu hafta ayrıntılı paketleme kuralları ezberlenmez.
  • Tip ipuçları bir sözleşme ve araç desteğidir; runtime doğrulaması değildir.
  • T | None, bulunamama/boş durumunu açık hâle getirir ve çağıran kodun kontrol sorumluluğunu görünür kılar.
  • Çok dosyalı yapı bağımlılıkları da görünür kılar.
  • Gelecek hafta bütün bu fikirleri hazır bir küçük sistem üzerinde birlikte kullanacağız.
Back to top