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

Öğrenirken birkaç sınıfı tek dosyaya yazmak iş görür. Kod büyüdükçe her işi ayrı bir dosyaya taşımak gerekir. 4. haftada bir fonksiyonu başka bir dosyadan import ile almıştınız. Bu hafta bir mağaza programının sınıflarını modüllere (her biri bir .py dosyası) ve bu modülleri bir arada tutan bir pakete (klasöre) böleceğiz. Tip ipucu (type hint) ile de bir fonksiyonun hangi türde değer aldığını ve döndürdüğünü imzasına yazacağız. Fonksiyonun imzası def satırıdır: fonksiyonun adını, parametrelerini ve tip ipucu yazıldıysa dönüş türünü gösterir.

Bu sayfadaki kod hücreleri kendiliğinden çalışmaz. Her hücrede önce çıktıyı tahmin edin, sonra Run Code düğmesine basın. Kodu değiştirip yeniden çalıştırabilir, Start Over ile ilk hâline döndürebilirsiniz. Paket örnekleri birden çok dosya istediği için hücrede çalışmaz. Onları kendi bilgisayarınızda VS Code ve terminalle deneyeceksiniz.

Bu bölümün kapsamı

NoteSınavda sorulur
  • Basit paket düzeni ve __init__.py dosyasının rolü
  • ModuleNotFoundError karşısında çalışma dizini denetimi
  • python app.py ile python -m paket.modul ayrımı
  • Kodu modüllere sorumluluğa göre ayırma
  • Tip ipucunun çalışma zamanında türü denetlememesi
  • T | None yazımı ve çağıranın None kontrolü
  • Sınıf ve metot imzalarındaki tip ipuçlarından nesnenin nasıl kullanılacağını okuma
  • İleri referans ve from __future__ import annotations
  • dataclass ve Enum
  • Paket yayımlama, pyproject.toml, sanal ortam yönetimi
  • mypy gibi statik tür denetleyicilerinin ayarları

Bu başlıklar konunun devamıdır. İleride karşınıza çıkar, ama bu derste ezberlemeniz beklenmiyor.

Büyüyen mağaza programını C’de nasıl bölerdiniz?

Önceki haftalarda yazdığınız mağaza programı tek bir dosyada büyüdü: Product, Customer ve Order sınıfları da, 12. haftadaki OutOfStockError da aynı dosyada. Programı birkaç dosyaya bölüp şu beş işi yapacaksınız:

  1. Ürün, müşteri ve sipariş kodunu ayrı dosyalara koymak
  2. Bir dosyada yazılan sınıfı başka bir dosyada kullanmak
  3. Programı terminalden başlatmak, “bulunamadı” hatası gelince nedenini bulmak
  4. Bir fonksiyonun hangi türde değer alıp döndürdüğünü gövdesini okumadan öğrenmek
  5. Aranan ürün bulunamadığında bunu çağıran koda bildirmek

Programlama Temelleri’nde C ile birden çok dosyalı bir program yazdığınızı düşünün: .c ve .h dosyaları, #include satırları, derleyiciye verilen dosya adları. Bu işlerin her birini C’de nasıl yapardınız? 4. haftada Python’da gördüğünüz modülleri de kullanabilirsiniz. Okumaya devam etmeden önce her iş için bir satır yazın. “product.c ve product.h diye iki dosya açarım” da bir cevap.

Bölümde bu beş işin Python’da nasıl yapıldığını göreceğiz. Bölümün sonunda listenize döneceğiz.

Basit paket düzeni

C’de her .c dosyasının yanında, başka dosyaların kullanacağı fonksiyonların bildirimlerini tutan bir .h başlık dosyası vardı. Python’da başlık dosyası yoktur. Her .py dosyası bir modüldür ve başka bir dosya içindeki adları doğrudan import ile alabilir. Birbiriyle ilgili modülleri bir klasörde toplarsanız o klasör bir paket olur:

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

Paket ve modül yapısı
project/
├── app.py
└── shop/
    ├── __init__.py
    ├── product.py
    ├── customer.py
    ├── order.py
    └── errors.py

Bir klasörün Python paketi olduğunu göstermenin alışılmış yolu, içine __init__.py dosyası koymaktır. Bu dosya boş olabilir. İsterseniz içine paket ilk kez import edildiğinde çalışacak kodu ya da paketin dışarıya açacağı adları yazabilirsiniz. Bu derste boş bırakacağız.

app.py paketin dışında durur ve programı başlatan dosyadır. En üstteki project/ klasörüne proje kökü denir.

Modülleri sorumluluğa göre ayırın

Kodu dosyalara ayırırken her sınıfa ayrı bir dosya açmanız gerekmez. Hangi sınıfların aynı modülde duracağına şu sorularla karar verin:

  • Bu sınıflar aynı işle mi ilgili?
  • Bir modüldeki değişiklik başka kaç modülü etkiliyor?
  • Modülün dışındaki kod hangi adları kullanmalı?

Yukarıdaki düzende ürünle ilgili kod product.py, siparişle ilgili kod order.py içinde duruyor. Özel istisnalar errors.py içinde toplandı, çünkü birden çok modül onları kullanıyor.

_round_money gibi tek alt çizgiyle başlayan adlar, sınıflarda olduğu gibi modüllerde de “bu, modülün içinde kullanılan bir yardımcıdır” anlamına gelir. Python bu adlara dışarıdan erişimi engellemez.

Bir modülden ötekine import

C’de başka bir dosyadaki fonksiyonu kullanmak için onun başlık dosyasını #include "product.h" ile eklerdiniz. Python’da import satırı yazarsınız. Paketin dışındaki app.py, paketin adıyla başlayan tam yolu yazar:

from shop.product import Product

Paketin içindeki order.py, aynı paketteki product.py dosyasını şöyle alır:

from .product import Product

Baştaki nokta “bu modülle aynı paketteki” demektir. Noktayla başlayan bu yazıma göreli import denir. product.py de hata sınıfını aynı yolla alır:

from .errors import OutOfStockError

Döngüsel import riski

a.py b.py’yi, b.py de a.py’yi import ederse iki modül arasında döngüsel bir bağımlılık oluşabilir. Bu çoğu zaman iki modülün işlerinin birbirine fazla karıştığını gösterir. Çözüm tekniklerine bakmadan önce kendinize şunu sorun: “Bu iki modülün gerçekten birbirini import etmesi gerekiyor mu?”

Çalışma dizini neden önemlidir?

C’de derleyiciye dosya adlarını siz verirdiniz: gcc main.c product.c. Derleyici product.h dosyasını bulamazsa No such file or directory hatası verirdi. Python’da import satırındaki modülü Python kendisi arar. Önce nereye bakacağı, programı nasıl başlattığınıza bağlıdır:

  • py app.py gibi bir dosyayı başlatırsanız önce o dosyanın bulunduğu klasöre bakar.
  • py -m shop.demo ile başlatırsanız önce terminalin açık olduğu klasöre bakar. Bu klasöre çalışma dizini denir.

Python modülü orada bulamazsa standart kütüphaneye ve kurulu paketlere bakar. Orada da yoksa ModuleNotFoundError verir.

En güvenli yol, terminali proje kökünde açıp programı oradan başlatmaktır. VS Code’da File > Open Folder ile project/ klasörünü açarsanız Terminal > New Terminal ile açılan terminal zaten orada başlar:

py app.py

macOS ve Linux’ta py yerine python3 yazın. Terminal başka bir klasörde açıksa bu komut app.py dosyasını bulamaz ve can't open file ... No such file or directory hatası verir. Bu bir import hatası değildir: Python başlatılacak dosyayı bulamamıştır.

WarningModuleNotFoundError görünce hemen paket kurmayın

Önce şunları kontrol edin:

  1. Terminal şu an hangi klasörde? PowerShell’de Get-Location (kısaca pwd), macOS ve Linux’ta pwd yazın.
  2. Programı hangi dosyayla ya da hangi komutla başlattınız? Paketin içindeki bir dosyayı doğrudan başlatırsanız Python paketin kendisini bulamaz.
  3. app.py ile paket klasörü aynı proje kökü altında mı?
  4. Dosya ya da paket adında yazım hatası var mı?
  5. 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 çözüm çoğu zaman PyPI’dan (pip install ile paket indirilen ortak depo) bir şey kurmak değil, programı proje kökünden doğru komutla çalıştırmaktır.

python app.py ile python -m package.module

Paketin modüllerini denemek için paketin içinde bir demo.py dosyası olsun:

# shop/demo.py
from .product import Product

print(Product("P1", "Klavye", 2))

Bu dosyayı python shop/demo.py diye yolunu vererek başlatırsanız Python onu paketten bağımsız bir script (doğrudan başlatılan program dosyası) olarak çalıştırır ve dosyanın shop paketine ait olduğunu bilmez. Göreli import bu yüzden çalışmaz, ImportError: attempted relative import with no known parent package hatası alırsınız. Göreli import yerine from shop.product import Product yazsanız da sonuç değişmez. Python önce başlatılan dosyanın klasörüne, yani shop/ klasörüne bakar ve orada shop adlı bir paket bulamaz: ModuleNotFoundError.

Bir modülü paketin parçası olarak çalıştırmak için proje kökünden şunu yazarsınız:

python -m shop.demo

-m seçeneği dosya yolu yerine modülün paket içindeki tam adını (shop.demo) alır. Python modülü çalışma dizininden başlayarak bulur ve shop paketinin parçası olarak çalıştırır. Göreli import da böylece çözülür. Windows’ta python komutu çalışmazsa aynı komutları py ile yazın: py -m shop.demo.

Komutları ezberlemeniz gerekmiyor. Bir import hatasını teşhis ederken şu soruyu sormanız yeterli:

“Bu dosya bağımsız script olarak mı, yoksa bir paketin modülü olarak mı çalışıyor?”

app.py ve main guard

4. haftadaki main guard kalıbı (if __name__ == "__main__":, dosya doğrudan çalıştırıldığında __name__ değeri "__main__" olur) çok dosyalı programda da kullanılır. app.py, programı başlatan kodu bir main() fonksiyonuna koyar:

# app.py
from shop.product import Product

def main():
    keyboard = Product("P1", "Klavye", 2)
    print(keyboard)

if __name__ == "__main__":
    main()

python -m shop.demo ile çalıştırılan modülde de __name__ değeri "__main__" olur. Başka bir dosyanın import ettiği modül ise kendi adını alır: shop.product gibi. Bu yüzden product.py içindeki deneme kodunu main guard altına koyarsanız app.py o modülü import ettiğinde deneme kodu çalışmaz.

Tip ipucu nedir?

C’de bir fonksiyonun ne aldığını ve ne döndürdüğünü başlık dosyasındaki bildirimden okurdunuz: double calculate_total(double prices[], int n);. Türleri yazmak zorundaydınız ve derleyici çağrıları bu türlere göre denetlerdi. Python’da tür yazmak zorunlu değildir, ama isterseniz tip ipucu ile yazabilirsiniz. Aşağıda prices: list[float], fonksiyonun float değerlerden oluşan bir liste beklediğini söyler. -> float ise bir float döndüreceğini söyler. Hücre ne yazar?

İpucuna uymayan bir değer verirsek ne olur? add iki int bekliyor, ona iki metin veriyoruz. Python bu çağrıyı reddeder mi? Hücre ne yazar?

Tip ipucu:

  • Kodu okuyana fonksiyonun ne beklediğini söyler.
  • Editöre ve statik analiz araçlarına (kodu çalıştırmadan inceleyen mypy gibi araçlar) bilgi verir.
  • Program çalışırken türü denetlemez. C derleyicisinin yaptığı tür denetimini Python yapmaz.

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ı

T | None ve eski sürüm söz dizimi

C’de aranan ürün bulunamayınca fonksiyon NULL döndürürdü, çağıran kod da NULL kontrolü yapardı. Python’da bunun karşılığı None döndürmektir. T | None tip ipucu “ya T türünde bir değer ya da None” demektir. T yerine herhangi bir tür yazılır. Aranan ürün bulunamazsa None döndüren bir metodun imzası:

def find(self, code: str) -> Product | None:
    ...

Bu yazım Python 3.10 ile geldi. Daha önce yazılmış kodlarda aynı şey şöyle yazılır:

from typing import Optional

def find(self, code: str) -> Optional[Product]:
    ...

Python 3.9’dan eski kaynaklarda list[str] yerine List[str], dict[str, int] yerine Dict[str, int] de görebilirsiniz. Bu derste güncel yazımı kullanıyoruz.

Sınıflarda tip ipucu

Tip ipuçları sınıflarda da yazılır. Aşağıdaki Catalog sınıfının imzalarını okuyun: add bir Product alır ve değer döndürmez (-> None). find bir metin kod alır, Product ya da None döndürür. Katalogda P9 kodlu ürün yok. Hücre ne yazar?

self.products: dict[str, Product] satırı niteliğe de ipucu yazar: anahtarlar metin, değerler Product. find sözlüğün .get() metodunu (3. hafta) kullandığı için anahtar yoksa None döndürür. İmzayı okuyan biri, gövdeye bakmadan metodu nasıl çağıracağını ve sonucu nasıl denetleyeceğini bilir.

ImportantTip ipucu None kontrolünü sizin yerinize yapmaz

Product | None yazmak metodun None döndürmesini engellemez. result.price satırına gelmeden önce result değerinin None olup olmadığını kontrol etmek yine programcının işidir. Kontrolü unutursanız ürün bulunamadığında AttributeError alırsınız.

İleri referans (sınav dışı)

Bir metot kendi sınıfının türünde bir değer döndürecekse, ipucunda tanımı henüz bitmemiş sınıfın adı geçer:

class Member:
    def choose_friend(self) -> "Member | None":
        ...

Tırnak içine yazılan ipucu sonradan çözülür. Bu kullanıma ileri referans (forward reference) denir. Dosyanın başına from __future__ import annotations satırını eklerseniz Python ipuçlarını hemen değerlendirmez, sonraya bırakır. Böylece tırnak gerekmez:

from __future__ import annotations

class Member:
    def choose_friend(self) -> Member | None:
        ...

Tanımanız yeterli: dataclass ve Enum

  • dataclass: çoğunlukla veri tutan sınıflarda her seferinde elle yazdığınız __init__, __repr__ ve __eq__ kodunu azaltır.
  • Enum: yalnız birkaç değer alabilen bir durumu (örneğin bir siparişin “beklemede” ve “gönderildi” hâlleri) adlarla yazmanızı sağlar.

İki yapı da sınavda sorulmaz.

Soruya dönelim: beş iş Python’da

Bölümün başında mağaza programını dosyalara bölerken beş işi C’de nasıl yapacağınızı sormuştuk. Python’daki karşılıkları şöyle:

İş C’de ya da önceki haftalarda Bu haftaki Python yolu Dikkat
Kodu ayrı dosyalara koymak product.c ve product.h gibi dosya çiftleri Her sınıf grubu bir .py modülüne, modüller __init__.py bulunan shop/ paketine Başlık dosyası yok. Her sınıfa ayrı dosya da gerekmez, sınıflar işlerine göre gruplanır
Başka dosyadaki sınıfı kullanmak #include "product.h", 4. haftada from pricing import final_price Paket dışından from shop.product import Product, paket içinden from .product import Product İki modül birbirini import ediyorsa işler iyi bölünmemiş olabilir
Programı başlatıp “bulunamadı” hatasını çözmek gcc main.c product.c yazmak, dosya bulunamazsa yolu kontrol etmek Proje kökünden py app.py ya da py -m shop.demo ModuleNotFoundError görünce paket kurmayın, önce çalışma dizinine ve başlatılan dosyaya bakın
Fonksiyonun ne alıp ne döndürdüğünü öğrenmek Başlık dosyasındaki bildirim: double calculate_total(double prices[], int n); Tip ipucu: def calculate_total(prices: list[float]) -> float: Python çalışırken türü denetlemez
Bulunamayan ürünü bildirmek NULL döndürmek, çağıranın NULL kontrolü yapması Dönüş ipucuna None eklenir, çağıran if result is not None: yazar İpucu None gelmesini engellemez, kontrol çağıranın işidir

Listenizi tabloyla karşılaştırın. C’de dosyaları derleyiciye siz verirdiniz. Python’da import satırındaki modülü Python kendisi arar, nereden aramaya başlayacağı da programı hangi klasörden ve hangi komutla başlattığınıza bağlıdır. Üçüncü işe “dosya yolunu kontrol ederim” yazdıysanız doğru yoldaydınız: Python’da bakacağınız yer, terminalin açık olduğu klasör ve başlattığınız dosyadır. C’deki tür bildirimini derleyici denetliyordu, Python’daki tip ipucunu ise okur ve editör kullanır. Beşinci iş için 12. haftadaki gibi bir istisna yükseltmeyi düşünmüş olabilirsiniz. Aranan ürünün katalogda olmaması olağan bir sonuçsa None döndürmek yeter. Stok yetmemesi gibi bir kural bozulduysa istisna yükseltilir.

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

Fonksiyonun ne aldığını ve ne döndürdüğünü dict[int, str], int ve str | None ipuçlarıyla imzaya yazın.

Alıştırma: Çalışma dizini hatasını teşhis et

Kendi bilgisayarınızda project/app.py, project/shop/__init__.py, project/shop/product.py ve project/shop/demo.py dosyalarını oluşturun. demo.py içinde from shop.product import Product satırı olsun. Sonra şu üç denemeyi yapın:

  1. Terminal proje kökündeyken py app.py ve py -m shop.demo
  2. Terminal bir üst klasördeyken py -m shop.demo
  3. Terminal proje kökündeyken py shop/demo.py

Her denemede Get-Location çıktısına ve hata mesajına bakın. Hata veren denemelerde Python’ın shop paketini hangi klasörde aradığını yazın.

ImportantPython sürümünü kontrol edin

Sıradaki görevde Book | None gibi ipuçlarını kendi bilgisayarınızda çalıştıracaksınız. list[str] ve dict[str, Product] yazımı Python 3.9, Product | None yazımı Python 3.10 ister. Bu derste en az Python 3.10 gerekir. Mümkünse desteği süren 3.11 ya da daha yeni bir sürüm kullanın. 1. haftada kurduğunuz sürümü terminalde yeniden kontrol edin:

py --version

macOS ve Linux’ta python3 --version yazın. Yanlış yorumlayıcı seçiliyse VS Code’da Python: Select Interpreter komutunu kullanın.

Sıra sizde: Üç modüllü sistem

Yerel ortamınızda şu yapıyı oluşturun:

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

Library.find_book(isbn) metodu Book | None döndürsün. app.py kitabın bulunamadığı, yani None döndüğü durumda kullanıcıya bir mesaj yazdırsın.

Çalışır kod görevi: Python sürümünüzü not edin ve 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. app.py programı main guard altındaki main() fonksiyonundan başlatsın. Ayrıca küçük bir library/demo.py oluşturup proje kökünden py -m library.demo ile çalıştırın.

Sınav provası

Önce kendi cevabınızı seçin; sonra cevap anahtarında her şıkkın neden doğru veya yanlış olduğunu okuyun. Çeldiriciler uydurma değil, bu konuda gerçekten yapılan hatalardır. Sınav maddeleri de bu mantıkla yazılır.

Madde 1. Bir paket dizinindeki __init__.py dosyası hakkında doğru olan hangisidir?

  • A. İçi boş bırakılırsa paketteki modüller import edilemez
  • B. Yalnızca PyPI’ya yüklenecek paketlerde gerekir
  • C. Paketin bütün sınıflarını içermek zorundadır
  • D. Paketin ana giriş noktasıdır ve python paket ile çalıştırılır
  • E. Klasörü paket olarak işaretler ve boş bırakılabilir

Madde 2. Kendi yazdığınız shop paketi için ModuleNotFoundError alıyorsanız ilk olarak ne yapmalısınız?

  • A. Bütün import satırlarını try/except içine almak
  • B. pip install shop komutunu çalıştırmak
  • C. Terminalin proje kökünde açık olup olmadığını denetlemek
  • D. Python’ı kaldırıp güncel sürümle yeniden kurmak
  • E. Paketteki __init__.py dosyasını silip yeniden denemek

Madde 3. Terminalin hangi dizinde olduğunu öğrenmek için ne kullanılır?

  • A. macOS/Linux’ta pwd, PowerShell’de Get-Location
  • B. Python içinde import os ve ardından os.name
  • C. pip list ile kurulu paketleri listelemek
  • D. python --version ile yorumlayıcıyı sorgulamak
  • E. macOS/Linux’ta ls, Windows PowerShell’de dir

Madde 4. python -m shop.demo ile python shop/demo.py arasındaki fark nedir?

  • A. -m yalnız __init__.py dosyalarını çalıştırır
  • B. -m yalnızca kurulu PyPI paketleri için çalışır
  • C. -m dosyayı derleyip çalıştırır, diğeri yorumlar
  • D. İkisi tümüyle aynıdır; -m yalnız daha kısadır
  • E. -m modülü paketin bir parçası olarak çalıştırır

Madde 5. Kodu dosyalara ayırırken hangi ölçüt kullanılmalıdır?

  • A. Sınıf adlarının alfabetik sırası
  • B. Sınıfların yazılma tarihine göre gruplanması
  • C. Dosya başına en fazla 100 satır kuralı
  • D. Sınıfların aynı işle ilgili olup olmadığı
  • E. Her sınıf mutlaka kendi dosyasında olmalıdır

Madde 6. a.py b.py’yi, b.py de a.py’yi import ediyorsa ilk sorulacak soru nedir?

  • A. “İki dosyadan hangisi önce yazıldı, hangisi ona bağlı?”
  • B. “Bu iki modülün gerçekten birbirini import etmesi gerekiyor mu?”
  • C. “Import satırlarını fonksiyon içine taşısam çözülür mü?”
  • D. “Daha yeni bir Python sürümü döngüyü kendisi çözer mi?”
  • E. “Dosyaları tek dosyada birleştirsem olur mu?”

Madde 7. Aşağıdaki program ne yazdırır?

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

print(add("a", "b"))
  • A. None
  • B. 0
  • C. ab; tip ipucu çalışma zamanında türü zorlamaz
  • D. TypeError; ipucu int dışındaki türleri reddeder
  • E. Söz dizimi hatası

Madde 8. Tip ipucu ne işe yarar?

  • A. Beklenen türü okura ve editör araçlarına bildirir
  • B. Programı daha hızlı çalıştırır
  • C. Sınıfların otomatik olarak __eq__ kazanmasını sağlar
  • D. Değerleri kendiliğinden belirtilen türe dönüştürür
  • E. Yanlış türde argüman verilmesini engeller

Madde 9. def find(self, code: str) -> Product | None: imzası çağırana ne söyler?

  • A. Metodun hiçbir zaman None döndürmeyeceğini
  • B. Çağıranın None durumunu kontrol etmesi gerektiğini
  • C. Python’ın None dönüşünde kendiliğinden hata yükselteceğini
  • D. Metodun iki değer birden döndürdüğünü
  • E. code parametresinin isteğe bağlı olduğunu

Madde 10. Bir sınıfın imzaları şöyle:

class Catalog:
    def __init__(self) -> None:
        self.products: dict[str, Product] = {}

    def add(self, product: Product) -> None: ...

    def find(self, code: str) -> Product | None: ...

Aşağıdaki kullanımlardan hangisi bu sözleşmeye uyar?

  • A. catalog.add("P1")
  • B. result = catalog.add(p) ve ardından print(result.code)
  • C. print(catalog.find("P1").price)
  • D. p = catalog.find("P1") ve ardından if p is not None: print(p.price)
  • E. catalog.find(Product("P1", 10.0))

Madde 11. Aşağıdaki imza çağırana ne söyler?

class Inventory:
    def reserve(self, code: str, amount: int) -> bool: ...
  • A. code parametresi isteğe bağlıdır
  • B. Ayrılan ürünlerin listesini döndürür
  • C. Metin kod ve tamsayı miktar alır, sonucu bool olarak bildirir
  • D. Başarısız olursa mutlaka bir istisna yükseltir
  • E. amount yerine metin verilirse Python çağrıyı çalıştırmadan reddeder

Madde 1. Doğru: E. Klasörün bir Python paketi olduğunu göstermenin alışılmış ve açık yoludur. İsterseniz içine paket ilk import edildiğinde çalışacak kodu ya da paketin dışarıya açacağı adları yazabilirsiniz. Şimdilik boş bırakmak yeterli.

  • A yanlış: Boş bir dosya işe yaramıyormuş gibi görünür. Ama boş bir __init__.py de klasörü paket olarak işaretlemeye yeter.
  • B yanlış: “Paket” sözcüğü pip install ile kurulan paketleri çağrıştırır. Ama __init__.py yerel paketlerde de kullanılır.
  • C yanlış: Dosya paketin kendisini temsil ediyormuş gibi göründüğü için paketin bütün kodunun burada toplandığı sanılabilir. Ama sınıflar ayrı modüllerde durur.
  • D yanlış: __init__ adı, kurucu gibi ilk çalışan şeyi çağrıştırır. Ama bir paketi python -m paket ile çalıştırmak için __main__.py kullanılır.

Madde 2. Doğru: C. Terminalin hangi klasörde olduğu ve app.py ile paketin aynı proje kökü altında bulunup bulunmadığı denetlenir. Kendi yazdığınız bir paket bulunamıyorsa eksik olan çoğu zaman bir kurulum değildir; program proje kökünden çalıştırılmamıştır.

  • A yanlış: Hata mesajı kaybolduğu için sorun çözülmüş gibi görünür. Ama hatayı gizler; nedeni ortadan kaldırmaz.
  • B yanlış: Hata mesajı “modül bulunamadı” dediği için eksik bir kurulum akla gelir. Ama paket sizin yazdığınızsa PyPI’da yoktur; aynı adlı yanlış bir paket kurulabilir.
  • D yanlış: Hata Python’dan geldiği için yorumlayıcının bozuk olduğu düşünülebilir. Ama yorumlayıcıyla ilgili bir sorun değildir.
  • E yanlış: Paket yapısını sadeleştirmek sorunu çözecekmiş gibi görünebilir. Ama klasörü paket olarak işaretleyen dosyayı silersiniz; sorun büyür.

Madde 3. Doğru: A. pwd ve Get-Location çalışma dizininin tam yolunu yazar. Python’ın paketleri nerede arayacağı çalışma dizinine bağlıdır; bu yüzden teşhise buradan başlanır.

  • B yanlış: os modülü dosya sistemiyle ilgili olduğu için doğru yer gibi görünür. Ama os.name işletim sistemi adını verir; çalışma dizinini değil.
  • C yanlış: Import hatası kurulumla ilgili sanıldığında bu komut akla gelir. Ama kurulu paketleri listeler; terminalin nerede olduğunu söylemez.
  • D yanlış: Sorun Python’da göründüğü için önce yorumlayıcıya bakmak akla gelir. Ama bu komut yorumlayıcı sürümünü verir; dizini değil.
  • E yanlış: Klasörün içeriğini görmek nerede olduğunuzu anlamaya yardım eder. Ama bu komutlar klasörün içindekileri listeler, klasörün tam yolunu söylemez.

Madde 4. Doğru: E. -m modülü paket içindeki tam adıyla bulup paketin parçası olarak çalıştırır; dosya yolu verildiğinde Python dosyanın hangi pakete ait olduğunu bilmeyebilir. Fark en çok göreli import kullanan modüllerde ortaya çıkar: proje kökünden -m ile çalıştırılan modül, from .product import ... satırını paketin içinden çözer.

  • A yanlış: Komutta paket adı geçtiği için yalnız paketin __init__.py dosyasının çalıştığı sanılabilir. Paket import edilirken o dosya da çalışır ama -m asıl olarak belirtilen modülü çalıştırır.
  • B yanlış: -m çoğu zaman python -m pip gibi kurulu araçlarla görüldüğü için yalnız onlara özgü sanılabilir. Ama yerel paketler için de kullanılır.
  • C yanlış: C’deki derleme ve çalıştırma ayrımı buraya taşınabilir. Ama ikisinde de Python kodu aynı biçimde çalıştırır.
  • D yanlış: Basit bir dosyada iki komut da aynı çıktıyı verdiği için aynı sanılabilir. Ama modülü farklı biçimde başlatırlar; fark göreli import’larda ortaya çıkar.

Madde 5. Doğru: D. Bir modül, aynı işle ilgili ve çoğu zaman birlikte değişen sınıfları bir arada tutar. Bir modüldeki değişikliğin kaç modülü etkilediğine de bakılır. Her sınıfa ayrı dosya açmak gerekmez.

  • A yanlış: Dosyaları bulmayı kolaylaştırdığı için düzenli görünür. Ama sıralamaya yarar, hangi sınıfların birlikte durması gerektiğini söylemez.
  • B yanlış: Proje adım adım büyüdüğü için her aşamanın kodunu ayrı tutmak akla gelebilir. Ama yazılma tarihinin sınıfların ne iş yaptığıyla ilgisi yoktur.
  • C yanlış: Kısa dosyalar okunaklı olduğu için sayıya dayalı bir kural cazip görünür. Ama satır sayısı sorumluluğu ölçmez.
  • E yanlış: Düzenli ve uygulaması kolay bir kural gibi görünür. Ama mekanik bir kuraldır; birlikte değişen sınıfları gereksizce ayırır.

Madde 6. Doğru: B. Döngüsel bağımlılık çoğu zaman iki modülün işlerinin birbirine fazla karıştığını gösterir. Çözüm tekniklerine geçmeden önce işi modüllere doğru bölüp bölmediğinize bakılmalıdır.

  • A yanlış: Bağımlılığın yazılış sırasından doğduğu sanılabilir. Ama yazım sırası bağımlılığı açıklamaz.
  • C yanlış: Bu teknik hata mesajını gerçekten ortadan kaldırdığı için çekicidir. Ama hatayı bastırır, modüllerin iş bölümünü düzeltmez.
  • D yanlış: Yeni sürümlerin eski sorunları giderdiğini gören öğrenci bunu da bekleyebilir. Ama sürümle ilgili bir sorun değildir; döngü tasarımdan gelir.
  • E yanlış: Tek dosyada döngü kalmadığı için kesin çözüm gibi görünür. Döngü kalkar ama iki modülü ayırmanın anlamı da kalmaz.

Madde 7. Doğru: C. İpucu kodu okuyana ve statik analiz araçlarına bilgi verir; yorumlayıcı çağrıyı engellemez. İki str değeri + ile toplanınca birleşir.

  • A yanlış: Fonksiyon birleştirme sonucunu döndürür.
  • B yanlış: Python bu türü kendiliğinden dönüştürmez.
  • D yanlış: Böyle bir çalışma zamanı denetimi yoktur.
  • E yanlış: Tip ipucu yazımı geçerli Python söz dizimidir.

Madde 8. Doğru: A. Tip ipucu okuyucuya niyeti gösterir, editör ve statik analiz araçlarına bilgi verir. Varsayılan olarak program çalışırken türü denetlemez; yararı, beklenen türün kodda yazılı olmasıdır.

  • B yanlış: C’de tür bildirimi derleyiciye yardım ettiği için Python’da da hız kazandıracağı sanılabilir. Ama tip ipucu yürütme hızını değiştirmez.
  • C yanlış: dataclass örneklerinde tip ipuçlarıyla birlikte __eq__ de geldiği için bu bağ kurulabilir. Ama bunu tip ipucu değil, dataclass gibi araçlar sağlar.
  • D yanlış: int(...) gibi dönüşümlere alışan öğrenci, : int yazımının da dönüştürdüğünü sanabilir. Ama dönüşüm yapılmaz.
  • E yanlış: Tür yazıldığına göre yanlış türün reddedileceği beklenir. Ama engellemez; yalnız editör ya da statik denetleyici uyarabilir.

Madde 9. Doğru: B. | None aramanın başarısız olabileceğini söyler. Tip ipucu sözleşmeyi açıkça yazar ama metodun None döndürmesini engellemez. product.code satırından önce None kontrolünü yine programcı yapar.

  • A yanlış: Dönüş türünde Product yazdığı için metodun hep ürün döndüreceği sanılabilir. Ama | None yazımı bu olasılığı açıkça bildirir.
  • C yanlış: Tür yazıldığı için Python’ın dönüşü denetleyeceği düşünülebilir. Ama böyle bir otomatik denetim yoktur.
  • D yanlış: | işareti iki türü yan yana yazdığı için iki değer dönüyormuş gibi okunabilir. Ama tek değer döner; imza bu değerin iki türden biri olabileceğini söyler.
  • E yanlış: None çoğu zaman varsayılan parametre değeri olarak görüldüğü için parametreyle ilişkilendirilebilir. Ama ipucu dönüş değerine aittir; code parametresi zorunludur.

Madde 10. Doğru: D. find bir metin kod alır ve Product ya da None döndürür. Çağıran None durumunu kontrol ettikten sonra niteliğe erişiyor.

  • A yanlış: add bir Product nesnesi bekler, ürün kodu değil.
  • B yanlış: -> None, add metodunun bir değer döndürmediğini söyler. result None olur.
  • C yanlış: Product | None aramanın başarısız olabileceğini söylüyor. Kontrolsüz erişim None geldiğinde AttributeError verir.
  • E yanlış: find bir str kod bekler. Nesnenin kendisini vermek sözleşmeye uymaz.

Madde 11. Doğru: C. Metot bir metin kod ve bir tamsayı miktar bekler; işlemin başarılı olup olmadığını True ya da False ile bildirir. Parametre ipuçları ne verileceğini, -> bool ise ne döneceğini söyler. İmza sayesinde gövdeyi okumadan metodu doğru çağırabilirsiniz.

  • A yanlış: İmzada code için ayrı bir işaret görülmediği için zorunlu olmadığı sanılabilir. Ama varsayılan değer verilmemiş; iki parametre de zorunludur.
  • B yanlış: Ayırma işi ürünlerle ilgili olduğu için metodun ürünleri döndüreceği düşünülebilir. Ama dönüş türü bool; liste dönseydi list[...] yazardı.
  • D yanlış: Önceki bölümlerde kurallar raise ile korunduğu için başarısızlığın da istisnayla bildirileceği sanılabilir. Ama imza sonucu bool olarak bildiriyor; başarısızlığın istisnayla bildirileceğini söylemez.
  • E yanlış: amount: int yazımı bir kural gibi okunabilir. Ama tip ipucu çalışma zamanında türü zorlamaz. Yanlış türü editör ya da statik denetleyici fark edebilir.

Tek sayfa özet

  • Her .py dosyası bir modüldür. __init__.py bulunan klasör bir pakettir, bu dosya boş olabilir.
  • Kodu modüllere işine göre ayırın. Her sınıfa ayrı dosya gerekmez. İki modül birbirini import ediyorsa iş bölümünü gözden geçirin.
  • Paket dışından from shop.product import Product, paket içinden göreli import ile from .product import Product yazılır.
  • Python modülü önce bir dosyayı başlattıysanız o dosyanın klasöründe, -m ile başlattıysanız çalışma dizininde arar.
  • ModuleNotFoundError görünce önce terminalin hangi klasörde olduğuna, hangi dosyayı başlattığınıza ve seçili yorumlayıcıya bakın.
  • python -m shop.demo modülü paketin parçası olarak çalıştırır. python shop/demo.py dosyayı paketten bağımsız bir script olarak başlatır, göreli import çalışmaz.
  • Tip ipuçları beklenen türü okura ve editöre söyler, program çalışırken türü denetlemez.
  • T | None sonucun bulunamayabileceğini imzada söyler. None kontrolünü yine çağıran yapar.
  • Metot imzası, metodun nasıl kullanılacağını söyler: ne verilir, ne döner, dönüş None olabilir mi.
  • Bu ders için Python 3.10 ya da daha yeni bir sürüm kullanın.

Bu bölümün kazanımları

Bu bölümü bitiren öğrenci:

  • Basit bir paket düzeni kurar ve __init__.py dosyasının rolünü açıklar.
  • ModuleNotFoundError karşısında önce çalışma dizinini ve proje kökünü denetler.
  • python app.py ile python -m paket.modul arasındaki farkı açıklar.
  • Kodu modüllere sorumluluklarına göre ayırır.
  • Tip ipucunun çalışma zamanında türü denetlemediğini açıklar.
  • T | None yazımını kullanır ve çağıranın None kontrolünü hâlâ yapması gerektiğini bilir.
  • Sınıf ve metot imzalarındaki tip ipuçlarından bir nesnenin nasıl kullanılacağını okur.
Back to top