%%writefile basitmodul.py
print("basitmodul çalıştırıldı.")
= 5
x def f(x):
return x**2
def g():
print("Merhaba")
Writing basitmodul.py
Kaan Öztürk
July 25, 2018
Her programlama dilinde olduğu gibi Python’da da tekrar tekrar kullanılabilen fonksiyon ve sınıfların bir kütüphane şeklinde ayrı dosyalarda saklanması ve yeni yazılan programlara entegre edilmesi için bir mekanizma vardır. Standart kütüphaneler, SciPy ve benzeri paketler, veya kendi kişisel fonksiyon kütüphaneniz bu modül sistemiyle inşa edilir.
Dizinin bütün yazılarına erişmek için Python Programlamaya Giriş kategorimize bakabilirsiniz. Bu dizideki yazılar ayrıca Jupyter defterleri halinde GitHub depomuzda da mevcut.
Python’da bir modül yaratmak için özel bir işleme gerek yoktur. Python kodu içeren, .py
uzantılı herhangi bir dosya bir modül olabilir. Sözgelişi, aşağıdaki kodu basitmodul.py
isimli bir dosyaya yazıp kaydettiğinizde, bir modül yaratmış olursunuz.
(Burada dosyayı yaratmak için IPython’un hücre sihirlerinden birini kullanıyoruz. İsterseniz %%writefile ...
satırından sonraki kısmı koyalayıp bir metin editörüne yapıştırarak basitmodul.py
adıyla kaydedebilirsiniz.)
%%writefile basitmodul.py
print("basitmodul çalıştırıldı.")
x = 5
def f(x):
return x**2
def g():
print("Merhaba")
Writing basitmodul.py
Komut satırında veya başka bir program içinde import basitmodul
komutunu vererek (sonunda .py olmayacak) bu modülün içindeki bütün komutların işletilmesini sağlarsınız.
Elbette basitmodul.py
dosyasını nereye kaydettiğiniz önemli. Bu örnekte, dosyanın mevcut çalışma dizininde bulunduğunu varsaydım. Bir import
komutunda yorumlayıcı önce çalışma dizinine, sonra PYTHONPATH
kabuk değişkeninde yazan dizinlere, sonra da kurulum sırasında belirlenmiş dizinlere bakar. İkincisini Linux bash kabuğunda echo $PYTHONPATH
komutu ile görebilirsiniz.
Diyelim yazdığınız modülleri modullerim
isimli bir dizin altında tutmak istiyorsunuz. Python yorumlayıcısı içindeki sys.path
değişkeni bakılacak dizinlerin listesini tutar (önce import sys yazmayı unutmayın). Bu listeye sys.path.append("<ev dizininiz>/modullerim")
komutuyla bir ekleme yaparak, ev dizininizin altındaki modullerim
dizinine bakmasını sağlayabilirsiniz.
Modülü import
etmekle yeni bir isim alanı yaratmış oldunuz. Bir isim alanı bir nesnedir; dolayısıyla modülde tanımlanan değişken ve fonksiyonlara erişmek için nokta notasyonu kullanılır.
Modülden sadece belli isimleri almak istiyorsak from ... import
komutunu kullanabiliriz. O zaman bu değişkenler ana isim alanına aktarılmış olurlar ve onlara nokta işlemi olmadan doğrudan erişebiliriz.
Yukarıdaki işlemde g
fonksiyonunu ana isim alanına aktarmadığımız için doğrudan kullanmaya kalktığımızda hata mesajı alırız.
Modüldeki bütün isimleri ana isim alanına aktarmak için from basitmodul import *
komutu verilebilir. Ancak, böyle yaptığınızda modüldeki isimler daha önce tanımlanmış isimlerin yerine geçebilir. Söz gelişi, yeni bir g()
fonksiyonu tanımlamış olalım.
Bu tanımdan sonra, basitmodul
’deki bütün isimleri aşağıdaki komutla alırsak, bu fonksiyon yerine basitmodul
’deki g()
geçecektir.
Bu tür çatışmalara meydan vermemek için, modülleri import *
ile yüklemek tavsiye edilmez. Modüldeki değişkenlere modül ismi aracılığıyla (meselâ basitmodul.g()
ile) ulaşmak daha emniyetlidir.
Ancak her seferinde modül ismini uzun uzun yazmak epeyce zahmetli olabilir ve kodun okunaklılığını azaltır. Modüle daha kısa bir isim atamak için import ... as
komutunu kullanabiliriz.
from ... import ... as
komutuyla modülden belli bir nesne (değişken, fonksiyon, vs.) alabilir ve onu yeni bir isimle kullanabilirsiniz.
Bir modülde tanımlanmış isimlerin (değişken ve fonksiyon) tam listesini görmek isterseniz dir()
fonksiyonunu kullanabilirsiniz.
['__builtins__',
'__cached__',
'__doc__',
'__file__',
'__loader__',
'__name__',
'__package__',
'__spec__',
'f',
'g',
'x']
dir()
fonksiyonu sadece modüllerde değil, bir sınıf (class) içindeki isimleri almak için de kullanılabilir. Örneğin, bir liste nesnesindeki metodları listelemek için dir(list)
komutu verebiliriz.
['__add__',
'__class__',
'__contains__',
'__delattr__',
'__delitem__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__getitem__',
'__gt__',
'__hash__',
'__iadd__',
'__imul__',
'__init__',
'__init_subclass__',
'__iter__',
'__le__',
'__len__',
'__lt__',
'__mul__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__reversed__',
'__rmul__',
'__setattr__',
'__setitem__',
'__sizeof__',
'__str__',
'__subclasshook__',
'append',
'clear',
'copy',
'count',
'extend',
'index',
'insert',
'pop',
'remove',
'reverse',
'sort']
Bir import
işlemi bir modül dosyasını baştan sona bir kere işletir, ama ikinci bir import
komutu dosyayı baştan çalıştırmaz. Dolayısıyla, etkileşimli çalışırken (sözgelişi Spyder gibi bir IDE ile veya Jupyter defteri ile) modül dosyasında bir değişiklik yaptıysanız, tekrar import
yapmak bu değişikliklerin görülmesini sağlamaz.
Söz gelişi, basitmodul.py
dosyasında g()
fonksiyonunun tanımını değiştirelim:
%%writefile basitmodul.py
print("basitmodul çalıştırıldı.")
x = 5
def f(x):
return x**2
def g():
print("Namaste")
Overwriting basitmodul.py
Bu yeni modül dosyasını import
ile işletmeye çalışalım:
Görüldüğü gibi g()
’nin eski tanımını kullanıyor. Zaten başta "basitmodul çalıştırıldı"
mesajının çıkmaması da modülün işletilmediğine işaret ediyor.
Modülünüzün güncellenmiş olarak yeniden işletilmesini istiyorsanız ya Python yorumlayıcınızı kapatıp açmalısınız (Jupyter’de kernel restart), ya da importlib.reload()
fonksiyonunu kullanmalısınız. Bu fonksiyon bir modül nesnesi döndürür.
Tabii bir modül aslında dinamik bir nesne olduğu için, böyle bir değişiklik yapmak için her zaman dosyayı değiştirip tekrar yüklemek gerekmez; çalışma sırasında bir komutla da değişiklik yapılabilir.
Bir modül belli bir işe dair fonksiyonların ve sınıfların tanımlandığı bir dosyadır. İşlev olarak ilişkili, ama birbirinden ayrı birkaç modülünüz varsa bunları ortak bir dizinde tutmak mantıklı olur. Bunlar bir Python paketi oluşturur.
Python’da bir paket bir dizindir; bu dizinde modüller ve __init__.py
isimli bir dosya mevcut olmalıdır. Paket yüklenirken __init__.py
dosyasının içindeki komutlar çalıştırılır. Paket yüklemesinde ilk olarak yapılmasını istediğimiz işlemleri bu dosyaya koyabiliriz. Paket dizininde mutlaka __init__.py
isimli bir dosya bulunmalıdır; bu boş bir dosya olabilir.
Örnek olarak, çalıştığımız dizinin altında modullerim isimli bir dizin, ve içinde modul_A.py, modul_B.py ve __init__.py isimli üç tane dosya yaratalım.
modullerim/
__init__.py
modul_A.py
modul_B.py
%%writefile modullerim/modul_A.py
print("Modül A yüklendi")
pi = 3.14159
def çevre(yarıçap):
return 2*pi*yarıçap
Writing modullerim/modul_A.py
%%writefile modullerim/modul_B.py
print("Modül B yüklendi")
def fib(n):
"""Fibonacci dizisinin n terimi."""
a,b=1,1
L = [1,1]
for i in range(n-2):
a,b = b,a+b
L += [b]
return L
Writing modullerim/modul_B.py
from ... import
komutu ile doğrudan modul_A
isim alanını yaratabiliriz.
Bu ikinci import ile "Modul A yüklendi"
mesajının çıkmadığına dikkat edin. Bir oturumda modül içeriği sadece ilk import komutunda çalıştırılır. Sonraki import işlemlerinde modül tekrar çalıştırılmaz; yine de yukarıda görüldüğü gibi bu modülle yeni bir isim alanı oluşturmak mümkündür.
Yazma zahmetini kısaltmak için as
kelimesiyle modüle daha kısa bir isim alanı adı verebiliriz.
modul_B içindeki bütün değişken isimlerini mevcut isim alanına ekleyebiliriz. Ama yukarıda açıkladığımız gibi bu pek tavsiye edilmez.
Yukarıdaki örneklerde çevre()
fonksiyonunu bir modülden, fib()
fonksiyonunu başka bir modülden aldık; bunun için de modülleri ayrıca import etme gibi bir adım atmamız icap etti. Bunun yerine bu isimleri doğrudan __init__.py içinde import edersek, modülleri ayrıca yüklememize gerek kalmaz. Bunun için __init__.py dosyasını aşağıdaki gibi değiştirelim:
Overwriting modullerim/__init__.py
Modül adının başındaki noktalar, dosyanın mevcut dizinde bulunduğunu gösterir. Değişikliğin görülebilmesi için yorumlayıcıyı tekrar başlatmak (Jupyter’de “Restart Kernel”), veya yukarıda gördüğümüz gibi reload()
kullanmak gerekir.
Şimdi çevre()
ve fib()
fonksiyonlarını, tanımlandıkları modülleri import etmeye gerek kalmadan doğrudan doğruya kullanabiliriz.
Bir paketin altında başka dizinler de bulunabilir. Bu durumda her bir dizinin altında kendi __init__.py dosyası bulunmalıdır.
İç içe dizinler şeklinde düzenlemiş bir pakete örnek olarak SciPy paketini ele alalım. Bu paketin altındaki dizinlerin bir kısmı şöyledir:
scipy/
integrate/
__init__.py
...
linalg/
__init__.py
...
stats/
__init__.py
distributions.py
...
Alt paketlerdeki fonksiyonları import ederken nokta (.) işlemini kullanabiliriz.
from scipy.integrate import quad
quad(lambda x: x**3, 1, 2) # x^3 fonksiyonunun 1'den 2'ye kadar integrali
(3.7500000000000004, 4.1633363423443377e-14)
(3.7500000000000004, 4.1633363423443377e-14)
(3.7500000000000004, 4.1633363423443377e-14)