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.

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]veT | Nonetip 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:
ProductCustomerOrderLineOrderOutOfStockError
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 ProductBu 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 engellemezDolayı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ı")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 NoneKüçü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
- Modüle ayırmanın tek ölçütü “bir sınıf = bir dosya” mıdır?
- Paket içindeki
.errorsgibi göreli import ile paket dışındakishop.productgibi açık import hangi farklı bağlamlarda kullanılır? - Type hint çalışma zamanında yanlış türü otomatik engeller mi?
T | Noneneyi ifade eder?_helperadı ne tür bir niyet belirtir?- 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.