Python'da modül, içinde tanımlar ve deyimler bulunan sıradan bir .py dosyasıdır; dosya adı (uzantısız) modülün adıdır. Başka bir dosyadaki kodu kullanmak için import modul_adi yazarsınız ve içindeki isimlere modul_adi.isim biçiminde erişirsiniz. Paket ise modülleri bir klasörde toplayan yapıdır; klasördeki __init__.py dosyası onu normal bir paket yapar ve import paket.modul gibi noktalı isimlerle erişim sağlar.
Bu yazıda import deyiminin tüm biçimlerini, bir modülün neden yalnızca bir kez çalıştığını, if __name__ == "__main__": kalıbının ne işe yaradığını, Python'un modülleri hangi klasörlerde aradığını (sys.path), paket ve göreli içe aktarmayı, python -m ile çalıştırmayı, döngüsel içe aktarma ve standart kütüphaneyle aynı adı taşıyan dosya gibi sık sorunları Python 3.15.2 ile çalıştırılmış örneklerle anlatıyoruz. Sonda Python 3.15'in yeni lazy import özelliğine kısaca değiniyoruz. Konu sırasını Python öğrenme rehberi sayfasında bulabilirsiniz.
İlk modülünüz ve import deyimi
Aynı klasörde iki dosya olduğunu düşünün. selam.py bir değişken, iki fonksiyon ve yüklendiğini gösteren bir print içeriyor:
"""Selamlama yardımcıları."""
print("selam modülü yükleniyor")
VARSAYILAN = "Merhaba"
def selamla(ad, kalip=VARSAYILAN):
return f"{kalip}, {ad}!"
def _yardimci():
return "dışarıya açık değil"main.py bu modülü içe aktarıyor. python main.py komutuyla çalıştırdığınızda çıktı şöyle olur:
import selam
import selam # ikinci import modülü yeniden çalıştırmaz
print(selam.selamla("Ayşe"))
print(selam.VARSAYILAN)
print(selam.__name__)
import sys
print("selam" in sys.modules)
# çıktı: selam modülü yükleniyor
# çıktı: Merhaba, Ayşe!
# çıktı: Merhaba
# çıktı: selam
# çıktı: TrueBurada üç önemli davranış var. Birincisi, import selam modülün içindeki isimleri sizin isim alanınıza (namespace) kopyalamaz; yalnızca selam adını ekler ve içeriğe bu ad üzerinden erişirsiniz. Her modülün kendi özel isim alanı vardır, bu yüzden iki modülde aynı adlı bir değişken olması sorun çıkarmaz. İkincisi, modülün en üst düzeydeki kodu (buradaki print) yalnızca ilk içe aktarmada çalışır. Python yüklediği her modülü sys.modules sözlüğünde önbelleğe alır; sonraki import deyimleri bu önbellekteki aynı nesneyi kullanır. Üçüncüsü, modülün adı içindeki __name__ değişkeninde saklanır.
import biçimleri: from, as ve yıldız

import deyiminin birkaç biçimi vardır ve hepsi aynı yükleme mekanizmasını kullanır; fark yalnızca hangi ismin sizin isim alanınıza bağlandığındadır:
| Yazım | İsim alanına eklenen | Kullanım |
|---|---|---|
| import selam | selam | selam.selamla("Ali") |
| import selam as s | s | s.selamla("Ali") |
| from selam import selamla | selamla | selamla("Ali") |
| from selam import selamla as sl | sl | sl("Ali") |
| from selam import * | Alt çizgiyle başlamayan tüm isimler | selamla("Ali") |
from selam import selamla as sl
from selam import *
print(sl("Mehmet", "Selam"))
print(VARSAYILAN)
print("_yardimci" in dir(), "selam" in dir())
# çıktı: selam modülü yükleniyor
# çıktı: Selam, Mehmet!
# çıktı: Merhaba
# çıktı: False FalseSon satır iki şeyi gösterir: from selam import * alt çizgiyle başlayan _yardimci fonksiyonunu getirmez ve from biçimleri modülün kendi adını (selam) isim alanına eklemez. Belgeler yıldızlı içe aktarmanın üretim kodunda hoş karşılanmadığını belirtir: hangi isimlerin geldiği belli olmaz ve sizin tanımladığınız bir ismi sessizce ezebilir. Etkileşimli kabukta yazı tasarrufu için kullanılabilir. Bir modülün hangi isimleri tanımladığını görmek için yerleşik dir(modul) fonksiyonu sıralı bir liste döndürür.
if __name__ == "__main__": betik mi, modül mü?
Bir dosyayı python hesap.py ile doğrudan çalıştırdığınızda Python o dosyanın __name__ değişkenine "__main__" değerini verir. Aynı dosya başka bir yerden içe aktarıldığında ise __name__ modülün adı olur. Bu fark sayesinde bir dosya hem içe aktarılabilir bir kütüphane hem de çalıştırılabilir bir program olabilir. Aşağıdaki hesap.py dosyasını doğrudan çalıştıralım:
def topla(a, b):
return a + b
def main():
print("Doğrudan çalıştırıldı, 2 + 3 =", topla(2, 3))
print("hesap içinde __name__ =", __name__)
if __name__ == "__main__":
main()
# çıktı: hesap içinde __name__ = __main__
# çıktı: Doğrudan çalıştırıldı, 2 + 3 = 5Şimdi aynı dosyayı başka bir programdan içe aktaralım. main() bu kez çağrılmaz, çünkü koşul sağlanmaz:
import hesap
print(hesap.topla(10, 20))
# çıktı: hesap içinde __name__ = hesap
# çıktı: 30__main__ belgesi bu blokta olabildiğince az kod tutmayı ve programın asıl işini main adlı bir fonksiyona koymayı önerir. Kodu doğrudan if bloğunun içine yazarsanız oradaki değişkenler modülün genel değişkenleri olur ve diğer fonksiyonlar yanlışlıkla onları kullanabilir. Belgelerdeki örnek ayrıca sys.exit(main()) kalıbını gösterir: main() bir tam sayı döndürürse bu değer programın çıkış kodu olur. Komut satırı argümanlarıyla çalışan programlar için argparse rehberine, bu yapının testlerde nasıl işe yaradığı için unittest yazısına bakabilirsiniz.
Python modülleri nerede arar? sys.path

import spam çalıştığında Python önce sys.modules önbelleğine bakar. Orada yoksa yorumlayıcıya gömülü (built-in) modüller arasında arar; bunların listesi sys.builtin_module_names içindedir. Bulamazsa sys.path listesindeki klasörlerde sırayla spam.py dosyasını ya da spam adlı bir paketi arar. Öğreticiye göre sys.path şu kaynaklardan oluşur:
- Çalıştırılan betiğin bulunduğu klasör (dosya verilmediyse geçerli klasör).
PYTHONPATHortam değişkenindeki klasörler (PATH ile aynı söz dizimi).- Kuruluma bağlı varsayılanlar; geleneksel olarak
site-packagesklasörü de buradadır vepipile kurulan paketler oraya gider.
Betik klasörü listenin en başına, standart kütüphanenin önüne yerleştirilir. Bu bilinçli bir tasarımdır ama sık yapılan bir hataya da kapı açar: kendi dosyanıza standart bir modülün adını verirseniz Python sizin dosyanızı yükler. Python 3.13 ile gelen geliştirilmiş hata mesajı bu durumu fark edip uyarır. Aşağıdaki dosyanın adı random.py olsun:
import random
print(random.randint(1, 6))
# hata: AttributeError: module 'random' has no attribute 'randint' (consider renaming 'random.py' since it has the same name as the standard library module named 'random' and prevents importing that standard library module)Traceback (most recent call last):
File "random.py", line 1, in <module>
import random
File "random.py", line 2, in <module>
print(random.randint(1, 6))
^^^^^^^^^^^^^^
AttributeError: module 'random' has no attribute 'randint' (consider renaming 'random.py' since it has the same name as the standard library module named 'random' and prevents importing that standard library module)Çıktıda dosyanın tam yolunu kısalttık, gerisi Python 3.15.2'nin gerçek çıktısıdır. Dosya kendini random adıyla içe aktardığı için randint bulunamıyor. Çözüm dosyayı yeniden adlandırmak ve aynı klasörde kalmış olabilecek __pycache__ klasörünü silmektir. json.py, email.py, test.py gibi isimler de aynı sorunu yaratır. sys.path çalışma anında normal bir liste gibi değiştirilebilir (sys.path.append("/yol")), ancak kalıcı çözüm genellikle projeyi düzgün bir paket yapısına sokmak ve bağımlılıkları bir sanal ortamda kurmaktır; bunun için venv ve pip rehberine bakabilirsiniz.
Aranan modül hiçbir yerde bulunamazsa ModuleNotFoundError oluşur. Bu sınıf ImportError'ın alt sınıfıdır; from modul import isim yazımında modül bulunup isim bulunamazsa ise doğrudan ImportError alırsınız:
try:
import jsno
except ModuleNotFoundError as e:
print(e, "|", e.name)
try:
from json import yukle
except ImportError as e:
print(type(e).__name__, "|", e.name)
# çıktı: No module named 'jsno' | jsno
# çıktı: ImportError | jsonPaketler ve __init__.py
Paketler, modül isim alanını noktalı isimlerle düzenler: magaza.sepet, magaza paketinin içindeki sepet modülüdür. Öğreticiye göre bir klasörün normal paket sayılması için içinde __init__.py bulunmalıdır. Bu dosya boş olabilir; ama paketin ilk içe aktarılışında çalışır, bu yüzden başlangıç kodu yazmak ya da sık kullanılan isimleri paket düzeyine taşımak için de kullanılır. __init__.py içermeyen klasörler ad alanı paketi (namespace package) olarak ele alınır; bu, birden fazla klasöre dağılmış paketler için kullanılan ileri düzey bir özelliktir. Örnek proje yapımız şöyle:
proje/
main.py
magaza/
__init__.py
__main__.py
fiyat.py
sepet.py# magaza/__init__.py
"""Küçük bir örnek mağaza paketi."""
from .fiyat import kdv_ekle
__all__ = ["kdv_ekle"]
print("magaza paketi başlatıldı")# magaza/fiyat.py
KDV_ORANI = 0.20 # örnek oran
def kdv_ekle(tutar):
return round(tutar * (1 + KDV_ORANI), 2)# magaza/sepet.py
from .fiyat import kdv_ekle
def toplam(fiyatlar):
return kdv_ekle(sum(fiyatlar))sepet.py içindeki from .fiyat import kdv_ekle göreli bir içe aktarmadır: baştaki tek nokta "bu paketin içinden" anlamına gelir. proje klasöründeki main.py paketi farklı biçimlerde kullanıyor:
from magaza import sepet
from magaza.fiyat import KDV_ORANI
import magaza
print(sepet.toplam([100, 50]))
print(KDV_ORANI)
print(magaza.kdv_ekle(10))
print(magaza.__name__, sepet.__name__)
# çıktı: magaza paketi başlatıldı
# çıktı: 180.0
# çıktı: 0.2
# çıktı: 12.0
# çıktı: magaza magaza.sepet__init__.py yalnızca bir kez, ilk içe aktarmada çalıştı. Dil referansına göre import magaza.sepet gibi noktalı bir isim içe aktarılırken Python önce magaza, sonra magaza.sepet yükler ve ikisini de sys.modules'a ekler. import a.b.c yazımında son öğe hariç hepsi paket olmalıdır; son öğe bir modül ya da paket olabilir ama bir fonksiyon veya sınıf olamaz. from paket import ad yazımında ise ad paketin içindeki bir alt modül ya da __init__.py'de tanımlanmış herhangi bir isim olabilir.
__init__.py içindeki __all__ listesi, from magaza import * yazıldığında hangi isimlerin geleceğini belirler. Belgeler bir tuzağa dikkat çeker: __all__ tanımlı değilse yıldızlı içe aktarma paketteki tüm alt modülleri otomatik olarak yüklemez; yalnızca paketin kendisini başlatır ve __init__.py'de tanımlanmış (ya da daha önce açıkça yüklenmiş) isimleri getirir.
Göreli ve mutlak içe aktarma
Paket içindeki bir modül kardeşlerine iki yoldan ulaşabilir. Mutlak içe aktarma tam yolu yazar: from magaza.fiyat import kdv_ekle. Göreli içe aktarma noktalarla konumu belirtir: tek nokta geçerli paketi, iki nokta bir üst paketi gösterir (from . import fiyat, from ..araclar import yardimci). Göreli biçim yalnızca from ... import ... yazımıyla kullanılabilir; import .fiyat geçersizdir. Göreli içe aktarmalar modülün bağlı olduğu paketin adına dayanır. Ana modülün (doğrudan çalıştırılan dosyanın) bir paketi olmadığı için, paket içindeki bir dosyayı yoluyla çalıştırırsanız göreli içe aktarma başarısız olur:
from .fiyat import kdv_ekle
def toplam(fiyatlar):
return kdv_ekle(sum(fiyatlar))
# hata: ImportError: attempted relative import with no known parent packageBu komut python magaza/sepet.py ile çalıştırıldığında son satırda ImportError: attempted relative import with no known parent package görünür. Öğretici bu yüzden ana modül olarak çalışacak dosyalarda her zaman mutlak içe aktarma kullanılmasını önerir. Paket içindeki bir modülü çalıştırmanın doğru yolu ise -m seçeneğidir.
python -m ve __main__.py
python -m modul_adi komutu, modülü sys.path üzerinden bulur ve "__main__" adıyla çalıştırır; paket bağlamı korunduğu için göreli içe aktarmalar çalışır. Bir paketin kendisini -m ile çalıştırırsanız Python paketin içindeki __main__.py dosyasını çalıştırır. Standart kütüphanedeki python -m venv ve python -m json.tool komutları bu mekanizmayı kullanır. Örnek paketimize şu dosyayı ekleyelim:
# magaza/__main__.py
import sys
from .sepet import toplam
fiyatlar = [float(x) for x in sys.argv[1:]] or [100.0]
print("Toplam (KDV dahil):", toplam(fiyatlar))python -m magazaimport sys
from .sepet import toplam
fiyatlar = [float(x) for x in sys.argv[1:]] or [100.0]
print("Toplam (KDV dahil):", toplam(fiyatlar))
# çıktı: magaza paketi başlatıldı
# çıktı: Toplam (KDV dahil): 120.0Belgelere göre __main__.py içeriği genellikle if __name__ == "__main__": ile korunmaz; dosya kısa tutulur ve asıl işi diğer modüllerden içe aktardığı fonksiyonlara bırakır. Böylece o fonksiyonlar test edilebilir ve yeniden kullanılabilir kalır.
Modül önbelleği, reload ve __pycache__
Bir modül oturum başına yalnızca bir kez içe aktarıldığı için, etkileşimli kabukta çalışırken modül dosyasını değiştirirseniz değişiklik kendiliğinden görünmez. Öğretici iki yol önerir: yorumlayıcıyı yeniden başlatmak ya da tek bir modül için importlib.reload() çağırmak. reload() aynı modül nesnesini kullanır ve modülün kodunu yeniden çalıştırarak içeriğini tazeler:
import importlib
import sys
from pathlib import Path
sys.dont_write_bytecode = True
Path("ayar.py").write_text("SURUM = 1\n", encoding="utf-8")
import ayar
print(ayar.SURUM)
Path("ayar.py").write_text("SURUM = 2\n", encoding="utf-8")
import ayar
print(ayar.SURUM)
importlib.reload(ayar)
print(ayar.SURUM)
# çıktı: 1
# çıktı: 1
# çıktı: 2İkinci import ayar önbellekteki eski nesneyi döndürdüğü için 1 yazdı; reload() sonrasında değer 2 oldu. from ayar import SURUM ile alınmış isimler ise reload() ile güncellenmez, çünkü onlar eski değere bağlanmış ayrı isimlerdir. Örnekteki sys.dont_write_bytecode satırı yalnızca bu deneme için önbellek dosyası üretimini kapatıyor.
Normalde Python, yüklemeyi hızlandırmak için her modülün derlenmiş hâlini __pycache__ klasöründe modul.cpython-315.pyc gibi sürüme özel bir adla saklar ve kaynak dosyanın değişip değişmediğini kendisi kontrol eder. Öğretici iki ayrıntının altını çizer: komut satırından doğrudan çalıştırılan dosya için önbellek yazılmaz ve .pyc dosyasından okumak programı daha hızlı çalıştırmaz, yalnızca yüklemeyi hızlandırır. __pycache__ klasörlerini sürüm kontrolüne eklemeyin; bunun için .gitignore rehberine bakabilirsiniz.
Döngüsel içe aktarma
İki modül birbirini en üst düzeyde içe aktarıyorsa sorun çıkabilir. Bir paket içinde a.py dosyası önce from paket import b yapıp sonra x = 1 tanımlıyor, b.py ise from paket import a yapıp hemen a.x değerini okumaya çalışıyor olsun. Program a'yı içe aktardığında Python onu sys.modules'a ekler ve kodunu çalıştırmaya başlar. b içe aktarılırken önbellekte yarım kalmış a modülünü bulur; x henüz tanımlanmadığı için hata oluşur:
# paket/a.py
from paket import b
x = 1# paket/b.py
from paket import a
print(a.x)# main.py
from paket import a
# hata: AttributeError: partially initialized module 'paket.a' from 'paket\a.py' has no attribute 'x' (most likely due to a circular import)Hata mesajındaki yolu kısalttık. Python sorunun büyük olasılıkla döngüsel içe aktarmadan kaynaklandığını açıkça söyler. Bir ayrıntıya dikkat edin: aynı iki dosya bir paket içinde değil de doğrudan çalıştırdığınız betiğin yanında dururken (import a ile), Python 3.15.2 bu mesaj yerine dosyayı yeniden adlandırmanızı öneren "consider renaming ... if it has the same name as a library you intended to import" ipucunu gösterdi. Bu ipucu gölgeleme durumları için tasarlanmıştır; mesajda bunu görüp dosya adınız hiçbir kütüphaneyle çakışmıyorsa döngüsel içe aktarmayı kontrol edin. Çözüm yolları: ortak kodu üçüncü bir modüle taşımak, içe aktarmayı kullanıldığı fonksiyonun içine almak ya da modül düzeyinde hemen değer okumak yerine a.x erişimini çalışma anına ertelemek.
Python 3.15: lazy import
Python 3.15, PEP 810 ile açık ertelenmiş içe aktarma getirdi. Yeni lazy yumuşak anahtar kelimesiyle işaretlenen bir içe aktarma, modülü hemen yüklemez; ismi ilk kullandığınız anda yükler. Böylece tüm içe aktarmaları dosyanın başında tutup yalnızca gerçekten kullanılan modüllerin yükleme maliyetini ödersiniz:
import sys
lazy import json
print("json" in sys.modules)
veri = json.loads('{"dil": "tr"}')
print("json" in sys.modules, veri)
# çıktı: False
# çıktı: True {'dil': 'tr'}3.15 belgelerine göre lazy yalnızca modül düzeyinde kullanılabilir; fonksiyon, sınıf gövdesi ya da try/except/finally bloğu içinde kullanmak SyntaxError verir ve yıldızlı içe aktarma ertelenemez. Ertelenmiş bir modül yüklenemezse hata içe aktarma satırında değil, ismin ilk kullanıldığı yerde ortaya çıkar. Bir ertelenmiş içe aktarma kendisini içe aktarmaya çalışırsa yeni ImportCycleError oluşur. Ayrıntılar ve -X lazy_imports gibi seçenekler için Python 3.15 yenilikleri yazısına bakın.
Sık yapılan hatalar

- Dosyaya standart modül adı vermek:
random.py,json.py,csv.pygibi adlar gerçek modülü gölgeler. - Paket içi dosyayı yoluyla çalıştırmak:
python paket/modul.pygöreli içe aktarmaları bozar;python -m paket.modulkullanın. - Modül düzeyinde iş yapan kod yazmak: İçe aktarıldığı anda çalışan yan etkili kod, testlerde ve başka programlarda sürpriz yaratır; bu kodu
main()içine alıp__name__kontrolüyle çağırın. - Değişikliğin görünmesini beklemek: Etkileşimli oturumda modülü düzenledikten sonra yeniden
importyazmak yetmez;importlib.reload()kullanın ya da oturumu yeniden başlatın. - Yıldızlı içe aktarmayı alışkanlık hâline getirmek: İsim çakışmalarını gizler ve kodun nereden geldiğini okuyana anlatmaz.
- Döngüsel bağımlılık kurmak: İki modülün birbirini en üst düzeyde içe aktarması "partially initialized module" hatasına yol açar.
Sık Sorulan Sorular
Modül ile paket arasındaki fark nedir?
Modül tek bir .py dosyasıdır. Paket, modülleri ve alt paketleri içeren bir klasördür; normal paketlerde içinde __init__.py bulunur ve modüllere paket.modul biçiminde noktalı isimlerle erişilir.
__init__.py dosyası boş olabilir mi?
Evet. Boş bir __init__.py klasörü normal paket yapmaya yeter. İsterseniz içine paket başlatma kodu, sık kullanılan isimlerin içe aktarımı ya da __all__ listesi koyabilirsiniz.
Başka klasördeki bir modülü nasıl içe aktarırım?
En temiz yol projeyi bir paket olarak düzenleyip üst klasörden python -m ile çalıştırmaktır. Geçici çözüm olarak klasörü sys.path listesine ekleyebilir ya da PYTHONPATH ortam değişkenine yazabilirsiniz.
import ifadeleri dosyanın başında mı olmalı?
Öğreticiye göre bu bir gelenektir, zorunluluk değildir. Python 3.15'te başta tutulan içe aktarmaları lazy ile erteleyerek açılış süresini kısaltabilirsiniz; daha eski sürümlerde aynı amaçla içe aktarmayı kullanıldığı fonksiyonun içine taşımak gerekiyordu.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Python öğrenme rehberi · Python venv ve pip · Python argparse · Python unittest · Python 3.15 yenilikleri · Python sık hata mesajları · Python raise ve özel istisna