Altblümler

 
6. Modüller

Python yorumlayıcısını kapatıp tekrar açarsanız yaptığınız tanımlar (fonksiyonlar ve değişkenler) kaybolur. Uzunca bir program yazmak isterseniz bunun için programınızı bir metin editörü ile hazırlayıp yarattığınız dosyayı yorumlayıcı girişi olarak kullanırsanız daha iyi olur. Bu işleme betik (script) yazmak denir. Programınız uzadıkça bunu daha kolay idare etmek için birkaç dosyaya bölmek isteyebilirsiniz. Yazdığınız bir fonksiyonu tanımını kopyalamaya ihtiyaç duymaksızın birkaç programda kullanmayı da isteyebilirsiniz.

Bu iş için Python'da modül denen dosyalar var. Bunlara yazılan tanımlar diğer modüllere ya da etkileşimli kipteki yorumlayıcıya import deyimi ile yüklenebilirler.

Modüller .py uzantılı metin dosyalarıdır ve içlerinde Python deyimleri ve tanımları bulur. Bir modül içerisinde __name__ global değişkeninin değeri (bir karakter dizisi) o modülün adını verir. Örneğin, favori metin editörünüz ile fibo.py adlı bir dosya yaratıp Python yorumlayıcısının bulabileceği bir dizine kaydedin. Dosyanın içeriği de şu olsun:

# Fibonacci sayıları modülü

def fib(n):    # n e kadar Fibonacci serisini yazdır
    a, b = 0, 1
    while b < n:
        print b,
        a, b = b, a+b

def fib2(n): # n e kadar Fibonacci serisi geri döndürür
    sonuc = []
    a, b = 0, 1
    while b < n:
        sonuc.append(b)
        a, b = b, a+b
    return sonuc

Yorumlayıcıyı açıp bu modülü şu komut ile yükleyin:

>>> import fibo

Bu fibo içindeki fonksiyon tanımlarını yürürlükte olan simge tablosuna eklemez; sadece modül adı fibo tabloya eklenir. Fonksiyonlara modül adı kullanarak erişilebilir:

>>> fibo.fib(1000)
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
>>> fibo.fib2(100)
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
>>> fibo.__name__
'fibo'

Bir fonksiyonu sık sık kullanmak isterseniz bunu yerel bir isme atayabilirsiniz:

>>> fib = fibo.fib
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377

 
6.1 Modüller Üzerine Daha Fazla Bilgi

Fonksiyon tanımlarının yanısıra modül içinde çalıştırılabilir ifadeler de olabilir. Bu ifadeler modülün ilk kullanıma hazırlanması için kullanılabilirler ve sadece modülün ilk yüklenişinde çalışırlar.6.1

Her modülün o modül içindeki bütün fonksiyonlar tarafından global simge tablosu olarak kullanılan kendi simge tablosu vardır. Bu özellik sayesinde modülü yazan kişi rahatlıkla modül içinde global değişkenler kullanabilir. Modülü kullanan diğer kişilerin global değişkenleri ile isim çakışması olmaz. Modul içindeki global değişkenlere de modulAdi.degiskenAdi şeklinde ulaşmak ve istenirse bunları değiştirmek mümkündür.

Modüller diğer modülleri yükleyebilirler. Bütün import ifadelerinin modülün (ya da betiğin) başına konması gelenektendir; ancak şart değildir. Yüklenen modüller kendilerini yükleyen modülün global simge tablosuna ekleniriler.

import deyiminin bir modüldeki isimleri doğrudan yükleyen modülün simge tablosuna ekleyen kullanım şekli var. Örnek:

>>> from fibo import fib, fib2
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377

Bu kullanım şekinde yüklemenin yapıldığı modül adı yerel simge tablosuna eklenmez (yani örnekteki codefibo tanımlı değildir).

Bir modülde tanımlanmış bütün isimleri de yüklemek şu şekilde mümkündür:

>>> from fibo import *
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377

Bu altçizgi (_) ile başlayanlar dışındaki bütün isimleri yükler.

 
6.1.1 Modül Arama Yolu

 

spam isimli bir modül yüklenmek istendiğinde yorumlayıcı önce çalıştırıldığı dizinde ve sonra PYTHONPATH ortam değişkenince tanımlanan dizinler içinde spam.py isimli bir dosya arar. PYTHONPATH dizin isimlerinden oluşan bir listedir ( PATH gibi). Aranan dosya bulunmazsa arama kuruluma bağlı başka bir yolda da aranabilir. Unix işletim sisteminde bu .:/usr/local/lib/python dizinidir.

Aslında modüller sys.path değişkeninde bulunan dizin listesinde aranırlar. Bu değişken değerini betiğin çalıştırıldığı dizin, PYTHONPATH ve kuruluma bağlı diğer dizinlerden alır. sys.path değişkeni sayesinde Python programları modül arama yolunu değiştirebilirler.

6.1.2 ``Derlenmiş'' Python Dosyaları

Derlenmiş Python dosyaları programların çalışmaya başlaması için gereken süreyi kısaltırlar. Örneğin spam.py adlı dosyanın bulunduğu dizinde spam.pyc adlı bir dosya varsa bu modüle spam modülünün ``bayta derlenmiş'' (byte-compiled) halidir. spam.py dosyasının son değiştirilme tarihi spam.pyc dosyasının içinde de kayıtlıdır ve bu tarihler aynı değil ise .pyc dosyası dikkate alınmaz.

spam.pyc dosaysının oluşması için bir şey yapmanız gerekmez. spam.py her ne zaman başarılı olarak derlenirse programın derlenmiş hali spam.pyc dosyasına kaydedilir. Bunun yapılamaması bir hata değildir; herhangi bir nedenle .pyc dosyası tam olarak yazılamazsa geçersiz sayılır ve dikkate alınmaz. .pyc dosyalarının içeriği platformdan bağımsızdır. Bu sayede bir Python modülü dizini farklı mimarideki makineler tarafından paylaşılabilir.

Uzmanlar için birkaç ip ucu:

 
6.2 Standart Modüller

Python zengin bir standart modül kütüphanesine sahiptir. Bazı modüller yorumlayıcı ile bütünleşiktir. Bu modüller dilin parçası olmadıkları halde verimlerini artırmak ya da sistem çağrıları gibi işletim sistemine ait özelliklere erişim için yrumlayıcı içine dahil edilmişlerdir. Bunlara iyi bir örnek her Python yorumlayıcısına dahil edilen sys modülüdür. sys.ps1 ve sys.ps2 değişkenleri de birincil ve ikincil komut satırı olarak kullanılan karakter dizilerini belirlerler:

>>> import sys
>>> sys.ps1
'>>> '
>>> sys.ps2
'... '
>>> sys.ps1 = 'C> '
C> print 'Böö !'
Böö !
C>

Bu iki değişken yorumlayıcı sadece etkileşimli kipte iken tanımlıdırlar.

sys.path değişkeni de yorumlayıcının modül arama yolunu belirler. Bu değerini ortam değişkeni PYTHONPATH belirler. PYTHONPATH değişkenine değer atanmadıysa sys.path varsayılan (default) değerini alır. Bunun değeri listelere uygulana işlemler ile değiştirilebilir:

>>> import sys
>>> sys.path.append('/ufs/guido/lib/python')

 
6.3 dir() Fonksiyonu

Yerleşik fonksiyon dir() bir modülün hangi isimleri tanımladığını bulmak içik kullanılır. Bu fonksiyon karakter dizilerinden oluşan bir liste geri döndürür:

>>> import fibo, sys
>>> dir(fibo)
['__name__', 'fib', 'fib2']
>>> dir(sys)
['__displayhook__', '__doc__', '__excepthook__', '__name__', '__stderr__',
 '__stdin__', '__stdout__', '_getframe', 'argv', 'builtin_module_names',
 'byteorder', 'copyright', 'displayhook', 'exc_info', 'exc_type',
 'excepthook', 'exec_prefix', 'executable', 'exit', 'getdefaultencoding',
 'getdlopenflags', 'getrecursionlimit', 'getrefcount', 'hexversion',
 'maxint', 'maxunicode', 'modules', 'path', 'platform', 'prefix', 'ps1',
 'ps2', 'setcheckinterval', 'setdlopenflags', 'setprofile',
 'setrecursionlimit', 'settrace', 'stderr', 'stdin', 'stdout', 'version',
 'version_info', 'warnoptions']

Argüman kullanmadan çağırılan dir() fonksiyonu o anda tanımlamış olduğunuz isimleri geri döndürür:

>>> a = [1, 2, 3, 4, 5]
>>> import fibo, sys
>>> fib = fibo.fib
>>> dir()
['__name__', 'a', 'fib', 'fibo', 'sys']

Bunun değişken, modül, fonksiyon vs. gibi her tip ismi listelediğine dikkat ediniz.

dir() yerleşik fonksiyon ve değişkenlerin isimlerini listelemez. Bunların bir listesini isterseniz, standart modül __builtin__ içinde bulabilirsiniz:

>>> import __builtin__
>>> dir(__builtin__)
['ArithmeticError', 'AssertionError', 'AttributeError',
 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError',
 'Exception', 'False', 'FloatingPointError', 'IOError', 'ImportError',
 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt',
 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented',
 'NotImplementedError', 'OSError', 'OverflowError', 'OverflowWarning',
 'PendingDeprecationWarning', 'ReferenceError',
 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration',
 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError',
 'True', 'TypeError', 'UnboundLocalError', 'UnicodeError', 'UserWarning',
 'ValueError', 'Warning', 'ZeroDivisionError', '__debug__', '__doc__',
 '__import__', '__name__', 'abs', 'apply', 'bool', 'buffer',
 'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'compile', 'complex',
 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod',
 'enumerate', 'eval', 'execfile', 'exit', 'file', 'filter', 'float',
 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id',
 'input', 'int', 'intern', 'isinstance', 'issubclass', 'iter',
 'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'min',
 'object', 'oct', 'open', 'ord', 'pow', 'property', 'quit',
 'range', 'raw_input', 'reduce', 'reload', 'repr', 'round',
 'setattr', 'slice', 'staticmethod', 'str', 'string', 'super',
 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip']

 
6.4 Paketler

Paketler ``noktalı modül isimleri'' kullanarak Python'un modül isim alanının düzenlenmesinde kullanılırlar. Örneğin modül adı A.B adı "A" olan bir paket içindeki "B" adlı alt modülü gösterir. Nasıl modüller farklı modül yazarlarını birbirilerinin kullandığı global değişkenleri dert etmekten kurtarıyorsa, paketler de NumPy ya da PyOpenGL gibi çok sayıda modül içeren paketlerin biribirilerinin modül isimlerinin çakışması tehlikesinden kurtarır.

Ses dosyaları ve ses verisi üzerinde işlem yapacak bir modül kolleksiyonu (bir ``paket'') geliştirmek istediğinizi düşünelim. Farklı formatlardaki ses dosyalarını (.wav, .aiff, .au gibi dosya uzantıları olan) biribirine dönüştürmek, seslere efektler uygulamak veya sesleri filtrelemek için pek çok modüle ihtiyacınız olacak. Paketinizin muhtemel dizin yapısı şöyle olabilir:

Sound/                          Paketin en üst seviyesi
      __init__.py               paketi ilk kullanıma hazırlama
      Formats/                  Farklı dosya formatları için alt paket
              __init__.py
              wavread.py
              wavwrite.py
              aiffread.py
              aiffwrite.py
              auread.py
              auwrite.py
              ...
      Effects/                  ses efektleri alt paketi
              __init__.py
              echo.py
              surround.py
              reverse.py
              ...
      Filters/                  filtre alt paketi
              __init__.py
              equalizer.py
              vocoder.py
              karaoke.py
              ...

__init__.py dosyaları Python'un bu dizinleri paket içeren dizinler olarak algılaması için gereklidirler. Bunlar aynı isimli dizinlerin modül arama yolunda bulunacak diğer geçerli modülleri istem dışı saklamasını engeller. __init__.py boş bir dosya olabileceği gibi paketi ilk çalışmaya hazırlayabilir ya da daha sonra açıklanacak olan __all__ değişkenine değer atıyor olabilir.

Paketin kullanıcısı paketten dilediği bir modülü yükleyebilir.

import Sound.Effects.echo

Bu Sound.Effects.echo modülünü yükler. Modüle tüm ismi ile atıfta bulunulmalı:

Sound.Effects.echo.echofilter(input, output, delay=0.7, atten=4)

Aynı modülü yüklemenin bir diğer yolu:

from Sound.Effects import echo

Bu da echo alt modülünü yükler; ancak bunu paket adı verilmeden erişilebilir kılar ve modül şu şekilde kullanılabilir:

echo.echofilter(input, output, delay=0.7, atten=4)

Bir diğer yol da istene fonksiyon ya da değişkeni doğrudan yüklemektir:

from Sound.Effects.echo import echofilter

Bu da echo modülünü yükler; ancak echofilter() fonksiyonnu doğrudan erişilebilir kılar:

echofilter(input, output, delay=0.7, atten=4)

from paket import isim kullanılırken isim bir alt modül, alt paket ya da paket içinde tanımlı bir fonksiyon, sınıf veya değişken ifade eden herhangi bir isim olabilir. import deyimi önce ismin pakette tanımlı olup olmadığına bakar; tanımlı değil ise bunun bir modül olduğunu varsayar ve bunu yüklemeye teşebbüs eder. Modülü bulamaz ise ImportError istisnası oluşur.

import item.subitem.subsubitem ifadesinde ise son ismin dışındaki isimler paket olmalıdır. Son isim bir modül veya paket olabilir; ancak bir önceki ismin içinde tanımlanan bir fonksiyon ya da değişken olamaz.

 
6.4.1 Bir paketten * yüklemek

Kullanıcı from Sound.Effects import * yazdığında ne olur ? Dosya sistemine ulaşılıp paketin içinde hangi alt paketlerin olduğunun bulunması ve hepsinin yüklenmesi beklenir. Ne yazık ki bu işlem küçük/ büyük harf ayrımının olmadığı Windows ve Mac işetim sistemlerinde pek iyi çalışmaz. Bu işletim sistemlerinde ECHO.PY gibi bir dosyanın echo, Echo veya ECHO isimlerinden hangisi ile yüklenmesi gerektiğini belirlemenin garantili bir yolu yoktur. Örneğin, Windows 95 dosya adlarının ilk harfini daima büyük harf ile gösterir. DOS'un 8+3 harflik dosya adı uzunluğu kısıtlaması da uzun modül isimleri için sorun olmaktadır.

Tek çözüm paket yazarının açık bir paket indeksi hazırlamasıdır. Bir paketin __init__.py dosyası __all__ adlı bir liste tanımlıyorsa bu liste from package import * ifadesi kullanıldığında yüklenecek modül isimlerinin listesi olarak kullanılır. Pedein yeni bir sürümü hazırlandığında bu listenin uygun şekilde güncellenmesi paket yazarının sorumluğundadır. Eğer paketten * yüklemeye ihtiyaç duyulmayacağına karar verilirse bu özellik kullanılmayabilir. Örneğin Sounds/Effects/__init__.py dosyasının içeriği şöyle olabilir:

__all__ = ["echo", "surround", "reverse"]

Bu from Sound.Effects import * ifadesinin Sound paketinden isimleri __all__ içinde geçen üç modülün yüklemesini sağlar.

__all__ tanımlanmamış ise from Sound.Effects import * ifadesi Sound.Effects paketindeki bütün alt modülleri yürülükte olan isim alanına yüklemez; sadece Sound.Effects paketinin ve içindeki isimlerin yüklenmesini sağlar ( muhtemelen __init__.py) dosyasını çalıştırdıktan sonra). Bundan önceki import deyimlerince yüklenen alt paketler de yüklenir. Şu koda bir bakalım:

import Sound.Effects.echo
import Sound.Effects.surround
from Sound.Effects import *

Bu örnekte echo ve surround modülleri from...import ifadesi çalıştırıldığında Sound.Effects paketinde tanımlı oldukları için yürürlükte olan isim alanına yüklenirler. Bu __all__ tanımlı olduğunda da bu çalışır.

Genel olarak bir modül ya da paketten * yüklemek hoş karşılanmaz; çünkü çoğunlukla zor okunan koda neden olur. Bunun etkileşimli kipte kullanılmasının bir sakıncası yoktur. Ayrıca bazı modüller sadece belirli bir kalıba uyan isimleri verecek şekilde tasarlanmışlardır.

from Paket import gerekli_altmodül ifadesini kullanmanın hiç bir kötü tarafı yoktur. Yükleyen modül farklı paketlerden aynı isimli modüller yüklemeye gereksinim duymadığı sürece tavsiye edilen kullanım şekli de budur.

6.4.2 Biribirilerini Yükleyen Modüller

Alt modüller çoğu kez birbirilerine atıfta bulunurlar. Örneğin surround modülü echo modülüne ihtiyaç duyabilir. Aslında bu türden atıflar öyle yaygındır ki import deyimi standart modül arama yoluna bakmadan önce çağırıldığı paketin içinde arama yapar. Bu şekilde surround modülü import echo veya from echo import echofilter ifadeleri ile kolayca echo modülüne kavuşabilir. Yüklenmek istenen modül içinde bulunan pakette (yükleme yağmaya çalışan modülün bulunduğu paket) bulunamaz ise import deyimi aynı isimli üst seviyeli bir modül arar.

Paketler Sound paketindeki gibi alt paketler şeklinde düzenlenmişler ise farklı alt paketler içindeki modüllerin birbirilerine atıfta bulunmasının kısa bir yolu yoktur; paketin tam adı kullanılmalıdır. Örneğin, Sound.Filters.vocoder modülünün echo modülünü kullanması gerekiyor ise from Sound.Effects import echo ifadesi ile buna erişebilir.



Footnotes

... çalışırlar.6.1
Aslında fonksiyon tanımları da `çalıştırılan' ifadelerdir; fonksiyon adını modülün global simge tablosuna eklerler.