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.

Çok dosyalı bir Python kod tabanında paket, modül ve sorumluluk ayrımını gösteren diyagram.

Paket ve modül yapısı
ImportantPython sürümünü önce kontrol edin

Bu bölümde kullandığımız:

  • list[str] / dict[str, Product] biçimi Python 3.9+,
  • Product | None biç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 hafta neleri yapabilmelisiniz?

  • Sınıfları sorumluluğa göre farklı modüllere ayırabilmeli,
  • Küçük bir Python paket yapısını okuyup çalıştırabilmeli,
  • __init__.py dosyasının rolünü açıklayabilmeli,
  • Paket içi ve paket dışı import kullanımını ayırt edebilmeli,
  • Çalışma dizininin import davranışını nasıl etkilediğini fark edebilmeli,
  • python -m ... ile dosyayı doğrudan çalıştırma arasındaki temel farkı açıklayabilmeli,
  • Temel str, int, list[T], dict[K, V] ve T | None tip ipuçlarını kullanabilmeli,
  • İleri referans gerektiren tip ipuçlarını tanıyabilmeli,
  • Type hint’in çalışma zamanı doğrulaması olmadığını açıklayabilmelisiniz.

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 OutOfStockError

order.py:

from .product import Product

Paketin dışındaki app.py:

from shop.product import Product

Bu 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.

WarningModuleNotFoundError görünce hemen paket kurmayın

Önce şunları kontrol edin:

  1. Terminal şu an hangi klasörde? (pwd; Windows PowerShell’de Get-Location)
  2. app.py ile paket klasörü gerçekten aynı proje kökü altında mı?
  3. Dosya/paket adında yazım hatası var mı?
  4. 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?

Python tip ipuçlarının geliştirme araçlarına bilgi verdiğini ancak çalışma zamanında tür zorlaması yapmadığını gösteren diyagram.

Tip ipucu ve çalışma zamanı

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 engellemez

T | 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 annotations

eklemek 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:
        ...
Notefrom __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.

ImportantTip ipucu sözleşmeyi görünür kılar, hatayı otomatik çözmez

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.

Kendinizi kontrol edin

  1. list[T] ve T | None için hangi Python sürümleri gerekir?
  2. __init__.py ne işe yarar; boş olabilir mi?
  3. Çalışma dizini ModuleNotFoundError ile nasıl ilişkili olabilir?
  4. python -m package.module hangi bağlamı korur?
  5. Type hint çalışma zamanında yanlış türü otomatik engeller mi?
  6. T | None neyi ifade eder?
  7. İleri referans nedir?
  8. from __future__ import annotations neyi yapar, neyi yapmaz?

Bu haftadan akılda kalması gerekenler

  • Bu ders için Python 3.10+ kullanın; sürümü ve seçili yorumlayıcıyı doğrulayın.
  • __init__.py paket 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 -m bir 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 | None bulunamama durumunu görünür kılar.
  • İleri referanslar sınıfların birbirine/self’e tip düzeyinde referans vermesinde kullanılır.
Back to top