Modül/paket düzeni ve temel tip ipuçları
Tek dosyada birkaç 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 bölümde kullandığımız:
list[str]/dict[str, Product]biçimi Python 3.9+,Product | Nonebiçimi Python 3.10+ gerektirir.
Bu ders için minimum Python 3.10, tercihen güncel desteklenen 3.11+ sürümü kullanın. 1. haftada kurduğunuz ortamı terminalde yeniden doğrulayın:
# Windows
py --version
# macOS / Linux
python3 --version
Yanlış yorumlayıcı seçiliyse VS Code’da Python: Select Interpreter komutunu kullanın.
Bu bölümün kapsamı
- basit paket düzeni ve
__init__.pydosyasının rolü ModuleNotFoundErrorkarşısında çalışma dizini denetimipython app.pyilepython -m paket.modulayrımı- modül sınırının sorumluluk sınırı olması
- tip ipucunun çalışma zamanında tür zorlamaması
T | Noneyazımı ve çağıranınNonekontrolü- ileri referans ve
from __future__ import annotationsetkisi
dataclassveEnum- paket yayımlama,
pyproject.toml, sanal ortam yönetimi mypygibi statik tür denetleyicilerinin ayarları
Bu başlıklar konunun devamıdır; ileride karşınıza çıkar ama bu derste ezberlemeniz beklenmiyor.
Bu dosya bağımsız script olarak mı, bir paketin modülü olarak mı çalışıyor?
Aynı kod, hangi dizinden ve hangi komutla başlatıldığına göre farklı import davranışı gösterir. ModuleNotFoundError gördüğünüzde ilk bakılacak yer bu yüzden paket listesi değil, çalıştığınız dizindir.
Basit paket düzeni
project/
├── app.py
└── shop/
├── __init__.py
├── product.py
├── customer.py
├── order.py
└── errors.py
__init__.py, bu dizinin bir Python paketi olarak kullanılmasında geleneksel ve açık sınır oluşturur. Boş olabilir. İstenirse paket yüklenirken çalışacak başlangıç kodunu veya dışarıya sunulacak adları da içerebilir; fakat başlangıç düzeyinde boş bırakmak çoğu zaman yeterlidir.
Paket içindeki product.py:
from .errors import OutOfStockErrororder.py:
from .product import ProductPaketin dışındaki app.py:
from shop.product import ProductBu hafta ileri paketleme/PyPI ayrıntılarını ezberlemiyoruz; ancak import’un hangi paket bağlamından ve hangi çalışma dizininden yürütüldüğünü anlamamız gerekir.
Çalışma dizini neden önemlidir?
Yukarıdaki yapı için terminalinizi project/ dizininde açın:
project/
├── app.py
└── shop/
ve buradan:
py app.py
veya macOS/Linux’ta:
python3 app.py
çalıştırın. Terminal başka bir dizindeyse shop paketi beklediğiniz import yolunda olmayabilir ve ModuleNotFoundError görebilirsiniz.
ModuleNotFoundError görünce hemen paket kurmayın
Önce şunları kontrol edin:
- Terminal şu an hangi klasörde? (
pwd; Windows PowerShell’deGet-Location) app.pyile paket klasörü gerçekten aynı proje kökü altında mı?- Dosya/paket adında yazım hatası var mı?
- VS Code doğru klasörü mü açtı ve doğru Python yorumlayıcısını mı kullanıyor?
Kendi yazdığınız shop gibi bir paket bulunamıyorsa sorun çoğu zaman PyPI’dan bir şey yüklememek, proje kökünden doğru biçimde çalıştırmaktır.
python app.py ile python -m package.module
Bir dosyayı doğrudan çalıştırdığınızda Python onu bir dosya/script bağlamında başlatır:
python app.py
Bir modülü paket bağlamında çalıştırmak için:
python -m shop.demo
kullanılabilir. -m, modülü import sistemi üzerinden paket içindeki tam adıyla bulup çalıştırır. Özellikle göreli import kullanan paket modüllerini python shop/demo.py diye doğrudan çalıştırmak yerine proje kökünden python -m shop.demo çalıştırmak doğru paket bağlamını koruyabilir.
Bu haftada amaç komutları ezberlemek değil, şu teşhis sorusunu öğrenmektir:
“Bu dosya bağımsız script olarak mı, yoksa bir paketin modülü olarak mı çalışıyor?”
Modül sınırı = sorumluluk sınırı
Dosyaya ayırma “her sınıf ayrı dosyada olsun” demek değildir. Şunları sorun:
- 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ı?
Python’da _round_money gibi tek alt çizgili adlar yine “modül içi yardımcı” niyeti belirtir; teknik erişim yasağı değildir.
Tip ipucu nedir?
Tip ipucu:
- okuyucuya niyet gösterir,
- editör ve statik analiz araçlarına bilgi verir,
- ancak varsayılan olarak çalışma zamanında türü zorlamaz.
def add(a: int, b: int) -> int:
return a + b
print(add("a", "b")) # type hint bunu otomatik engellemezT | None ve eski sürüm sözdizimi
Modern sözdizimi:
def find(self, code: str) -> Product | None:
...Python 3.10 öncesi kodlarda benzer niyet şu biçimde görülebilir:
from typing import Optional
def find(self, code: str) -> Optional[Product]:
...Python 3.9 öncesindeki eski kaynaklarda ayrıca list[str] yerine List[str], dict[str, int] yerine Dict[str, int] görebilirsiniz. Bu dersin kodunu geriye uyumluluk için karmaşıklaştırmayacağız; güncel ortam kullanacağız.
İleri referans (forward reference)
Bir tip ipucunda henüz tanımı tamamlanmamış sınıf adına ihtiyaç duyabilirsiniz:
class Member:
def choose_friend(self) -> "Member | None":
...Tırnak içindeki annotation daha sonra çözümlenebilen bir ileri referans olarak kullanılabilir. Modern projelerde dosyanın başına:
from __future__ import annotationseklemek annotation’ların değerlendirilmesini erteleyerek birçok ileri referansı daha rahat yazmayı sağlar:
from __future__ import annotations
class Member:
def choose_friend(self) -> Member | None:
...from __future__ import annotations eski Python’a yeni sözdizimi eklemez
Bu özellik annotation değerlendirmesini erteler; örneğin Python 3.9 yorumlayıcısının X | None sözdizimini 3.10 gibi parse etmesini sağlamaz. Bu yüzden ders ortamı yine Python 3.10+ olmalıdır.
Sınıflarda tip ipucu
Product | None, aramanın başarısız olabileceğini görünür kılar. Çağıran yine None durumunu kontrol etmelidir.
Product | None yazmak None değerini ortadan kaldırmaz. product.code erişiminden önce uygun kontrol yine programcının sorumluluğundadır.
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 iki modülün sorumluluklarının fazla iç içe geçtiğini gösterir. İleri çözüm tekniklerinden önce “bu iki modül gerçekten birbirini bilmek zorunda mı?” sorusunu sorun.
dataclass ve Enum kavram radarı
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 geçiş sürümünde tanıma düzeyindedir.
Alıştırma — Tip ipuçlarını ekle
Sözleşmeyi dict[int, str], int ve str | None ile görünür hâle getirin.
Alıştırma — Çalışma dizini hatasını teşhis et
project/shop/product.py ve project/app.py yapısını oluşturun. Önce proje kökünden çalıştırın. Sonra terminali üst klasöre taşıyıp aynı komutu deneyin. Oluşan farkı pwd/Get-Location çıktısıyla açıklayın. Amacımız hatayı ezberden düzeltmek değil, import bağlamını teşhis etmektir.
Küçük üretim görevi — Üç modüllü sistem
Yerel ortamınızda:
library-project/
├── app.py
└── library/
├── __init__.py
├── book.py
├── member.py
└── library.py
oluşturun. Library.find_book(isbn) metodu Book | None döndürsün. app.py içinde None durumunu yönetin.
Çalışır kod teslimi: Python sürümünüzü kaydedin; programı proje kökünden çalıştırın; en az bir paket içi göreli import, bir paket dışı import ve bir Book | None dönüşü kullanın. Ayrıca küçük bir library/demo.py oluşturup proje kökünden python -m library.demo (Windows’ta gerekirse py -m library.demo) ile çalıştırın.
Tek sayfa özet
- Bu ders için Python 3.10+ kullanın; sürümü ve seçili yorumlayıcıyı doğrulayın.
__init__.pypaket sınırını görünür kılar ve boş olabilir.- Import hatalarında önce çalışma dizini, proje yapısı ve yorumlayıcıyı kontrol edin.
python -mbir modülü paket/import bağlamında çalıştırır.- Tip ipuçları sözleşme ve araç desteğidir; runtime doğrulaması değildir.
T | Nonebulunamama durumunu görünür kılar.- İleri referanslar sınıfların birbirine/self’e tip düzeyinde referans vermesinde kullanılır.
Bu bölümün kazanımları
Bu bölümü bitiren öğrenci:
- Basit bir paket düzeni kurar ve
__init__.pydosyasının rolünü açıklar. ModuleNotFoundErrorkarşısında önce çalışma dizinini ve proje kökünü denetler.python app.pyilepython -m paket.modulçalıştırmalarını ayırır.- Modül sınırını sorumluluk sınırı olarak kurar.
- Tip ipucunun çalışma zamanında tür zorlamadığını belirler.
T | Noneyazımını kullanır ve çağıranınNonekontrolünü hâlâ yapması gerektiğini bilir.- İleri referansı tanır ve
from __future__ import annotationsetkisini açıklar.