27 Ağustos 2008 Çarşamba

Turşular ve Birkaç Os Fonksiyonu - 1

Python kodlaması yaparken, terminalde çok sık kullandığım birçok komutu birer parametreyle halledebileceğim bir program yazmıştım ve geçenlerde de duyurmuştum: Tiss. Parametresiz haliyle, sadece uzantılarına göre silmesini istediğim dosyaları, boş dizinleri ve python programı inşa ederken ortaya çıkan inşa dizinini siliyor. Örneğin, hede.txt dosyasını Vim'de yazıp tekrar kaydettiğimde, aynı dizinde hede.txt~ dosyası oluşuyor. Bir tane olsa hadi neyse; ama bazen .pyc, #, ~, .db gibi bir sürü gereksiz dosya öyle birikiyor ki, gerçekten tek bir komuta ihtiyacınız olabiliyor. Hangi parametreyle, ne yaptığını sitesindeki[1] kodlara bakarak görebilirsiniz. Yakında ana sayfasına parametreler hakkında bilgi gireceğim.

Sonra dedim ki, ben parametresiz haliyle belli uzantılara sahip dosyaların silmesini sağlıyor olabilirim; ama ya kullanıcı bu uzantıları değiştirmek isterse? Ya benim öntanımlı olarak yazdığım uzantıların birkaçını kaldırmak istiyorsa? Bu halde kullanıcıyı, koda elle müdahale etmeye ve sonra tekrar kurmaya zorlamış olmuyor muyum? Oluyorum tabi ki..

Bu nedenle, birçok program gibi, Tiss'in de ev dizininde, ayar dosyalarının saklandığı bir dizin olacaktı: .tiss. Kodu açıp baktığımda, uzantıları bir listede tuttuğumu gördüm:


backup_extensions = ['*~', '#*#', '*.db', '*.lay', '*.pyc', '*.log', '*.ini']


Hmm. Şimdi şöyle yapabiliriz. Eğer kullanıcı programı ilk defa çalıştırıyorsa veya ayarlarda hiçbir değişiklik yapmamışsa, öntanımlı olarak yukarıdaki listeyi silme fonksiyonunda kullanacağız. Hadi yapalım. Önce bize lazım olacak modülleri içe aktaralım:


import os, glob, pickle


Şimdi de lazım olacak değişkenleri tanımlayalım:


class files:
def __init__(self):
self.home = os.environ['HOME']
self.tiss_home = os.path.join(self.home, '.tiss')
self.data_remove = os.path.join(self.tiss_home, 'remove.pickle')


os.environ['HOME'] ile, kullanıcının ev dizininin çıktısını alıyoruz. os.path.join'i kullanarak ev dizin yoluna '.tiss' ekliyoruz. Üçüncü değişkeninse ikinci değişkenden bir farkı yok. Şimdi bu değişkenleri kullanarak .tiss dizininin varlığını kontrol edelim:


def create_dir_files(self):
if not os.path.exists(self.tiss_home):
os.mkdir(self.tiss_home)
self.backups()


Yani kod diyor ki, eğer kullanıcının ev dizininde .tiss diye bir dizin yoksa, önce onu oluştur, sonra da self.backups() fonksiyonunu çalıştır. Eee, o fonksiyon nerede ve neye yarar? Hemen söylüyorum. O fonksiyon, Tiss'in ana görevi olan gereksiz uzantıları silme görevi için kullanıcının belirlediği herhangi bir ayar dosyası yoksa, öntanımlı backup_extensions listesini remove.pickle isimli bir dosyada turşu gibi saklamaya yarar. İşte kodumuz:


def backups(self):
if not os.path.exists(self.data_remove):
backup_extensions = ['*~', '#*#', '*.db', '*.lay', '*.pyc', '*.log', '*.ini']
pickle_file = file(self.data_remove, 'w')
pickle.dump(backup_extensions, pickle_file)
del pickle_file


İyi de, niye böyle bir şey yaptık? Doğrudan backup_extensions değişkenini, gereksiz uzantıları silmeye yarayan fonksiyonda kullanmayı neden denemedik? Deneyebilirdik; ama kullanıcıya bu değişkeni değiştirmekten mahrum da bırakırdık. O nedenle turşu olarak bir dosyaya yazdırma gereği duyduk. Ha, illa ki turşu mu? Elbette değil, dileseydik, doğrudan bir dosya açıp oraya satır satır yazdırmayı deneyebilirdik; ama satırı çağırmak, “\n”lerle uğraşmak, split'lemek, sonra kullanıcının isteği doğrultusunda satırı değiştirmek, sonra yazdırıp dosyayı tekrar kapatmak.. İnanın, bana turşu denen bir şey varken bu amaç için başka yolu seçmek, css ile siteyi bölümlere ayırmak varken tablolarla bölmek, yani tabloları amacı dışında kullanarak siteyi ve kodu ağırlaştırmak gibi geliyor. Şimdi, turşuda saklanan değişken lazım olduğunda, nasıl çağrılır ve kullanılır onu gösterelim. Önce kod, sonra açıklaması:


def remove_files():
pickle_file = file(self.data_remove)
backup_extensions = pickle.load(pickle_file)
for remove in backup_extensions:
backup_files = glob.glob('%s/' % os.getcwd() + remove)
for backup in backup_files:
os.remove(backup)


Kodda da görüldüğü gibi, bir öncekinden farklı olarak, yazma izni vermeden pickle_file değişkenine self.data_remove yolunu atıyoruz. Sonra turşumuzda duran listeyi backup_extensions ile çağırıyoruz. Çağırdıkdan sonra da, bu listedeki her eleman için, bulunduğumuz dizinde tarama yapıyoruz. Eğer bulunduğumuz dizinde, backup_extensions'taki uzantılardan herhangi birine sahip dosyalar bulunursa, onun için de ikinci bir döngü başlıyor: O uzantılara sahip dosyaları sil!

Evet. Buraya kadar öntanımlı listemizi nasıl turşu olarak saklayabileceğimizi, sonra lazım olduğunda nasıl çağırabileceğimizi görmüş olduk. Ama bizim amacımız neydi? Kullanıcıya bu listeyi değiştirme hakkı vermekti. Bunun için izlenecek adımlar da basit:


  • Silme için bir ayar fonksiyonu oluştur.

  • Bu fonksiyon için bir parametre ata.

  • Fonksiyonda bir değişkene düzenlenebilir olarak turşuyu çağır.

  • Kullanıcıya hangi uzantıların silinmesini istediği üzerine soru sor.

  • Eklenen ve çıkarılan uzantı elemanlarını tekrar turşuya kaydet.

  • Ve kapat.



Bu fonksiyonu da daha sonraki tarihte anlatırım. Eğer bu yazı ile ilgili bir yanlışlık veya daha iyi yöntem bulursanız, buraya mutlaka yorum olarak girmenizi istiyorum. Biz neydik? Python öğrenmeye ve onu tüm esnekliğiyle kullanmaya hevesli öğrencilerdik. Şimdiden teşekkürler.




[1] http://tiss.googlecode.com

25 Ağustos 2008 Pazartesi

Set

Set fonksiyonu bir veri tipi içerisinde yer alan değerleri bulup ayrıştırmaya yarar. Bir kaç örnekle daha iyi anlaşılacaktır:
In ile başlayan satırlar verdiğimiz komutu, out ile başlayanlar ise o komutun çıktısını belirtiyor.

In [7]: a=set("Antalya")
In [8]: a
Out[8]: set(['A', 'a', 'l', 'n', 't', 'y'])

Büyük ve küçük harfe duyarlı olduğuna dikkat ediniz.


In [17]: list=["antalya","adana","antalya","izmir","ankara","izmir"] #liste,tüp ve sözlükler üzerinde de kullanabilirsiniz.
In [18]: list2=set(list)
In [19]: list2
Out[19]: set(['ankara', 'antalya', 'adana', 'izmir'])
In [21]: sozluk={"fenerbahce":"sarikanaryalar","galatasaray":"aslanlar","besiktas":"karakartal","fenerbahce":"fb","galatasaray":"gs"}
In [22]: s2=set(sozluk)
In [23]: s2
Out[23]: set(['fenerbahce', 'besiktas', 'galatasaray'])


Şimdi bir kaç faydalı özellik daha gösterelim.

In [30]: a=set("adanaAntalya")
In [31]: b=set("afyonAnkara")

In [32]: a-b #a'nın b'den farklı elemanları.
Out[32]: set(['t', 'd', 'l'])

In [33]: a|b # Tüm elemanlar
Out[33]: set(['a', 'A', 'd', 'f', 'k', 'l', 'o', 'n', 'r', 't', 'y'])

In [34]: a&b # Ortak elemanlar
Out[34]: set(['a', 'A', 'y', 'n'])

In [35]: a^b #a ya da b'de olup fakat ortak olmayan elemanlar.
Out[35]: set(['d', 'f', 'k', 'l', 'o', 'r', 't'])

List Comprehension

Türkçesi için uygun bir karşılık bulamadığım için python dökümantasyonun da olduğu gibi yazdım. (Uygun bir karşılığı varsa ya da uydurursanız yorum belirtirseniz değiştirmek taraftarıyım.)

Daha önce Mirat Can Bayrak'ın anlatmış olduğu map() ve filter() fonksiyonlarının yaptığı işleri ve çok daha fazlasını yapabilmemize olanak sağlar. Anlaşılması biraz zor gelebilir fakat alıştıktan sonra hayat kurtarıcı olabiliyor. Sözü fazla uzatmadan hemen örnekler üzerinde açıklamaya başlayalım.

İlk örneğimiz map() fonksiyonunun anlatıldığı örneğin aynısı olsun


In [65]: def sayhello(to_who):
print "hello %s" %to_who
....:
....:

In [67]: people = ('Ahmet','Mehmet','Hale','Jale','Bülent')
In [68]: [sayhello(i) for i in people] #lc (List Comprehension)'ın olayı burada başlıyor. Kolay anlayabilmek için sağdan-sola doğru okuyunuz. "i İnsanların içerisinde olduğu sürece sayhello() fonksiyonunu çalıştır"
hello Ahmet
hello Mehmet
hello Hale
hello Jale
hello Bülent


Örneklere yine Mirat Can Bayrak'ın filter()
fonksiyonunu anlattırken kullandığı tek sayilar üzerinden gidelim.

In [72]: [i for i in range(10) if i%2!=0] #Gördüğünüz gibi lc içerisinde if koşullarınıda kullanabiliyoruz.
Out[72]: [1, 3, 5, 7, 9]


Biraz abartıp iç içe döngüleri kullarak sadece TEK satırda amatör bir çarpım tablosu bile yapabiliriz.

In [77]: [[i*j for i in range(1,10)] for j in range(1,11)]
Out[77]:
[[1, 2, 3, 4, 5, 6, 7, 8, 9],
[2, 4, 6, 8, 10, 12, 14, 16, 18],
[3, 6, 9, 12, 15, 18, 21, 24, 27],
[4, 8, 12, 16, 20, 24, 28, 32, 36],
[5, 10, 15, 20, 25, 30, 35, 40, 45],
[6, 12, 18, 24, 30, 36, 42, 48, 54],
[7, 14, 21, 28, 35, 42, 49, 56, 63],
[8, 16, 24, 32, 40, 48, 56, 64, 72],
[9, 18, 27, 36, 45, 54, 63, 72, 81],
[10, 20, 30, 40, 50, 60, 70, 80, 90]]

Lc ile yapabilecekleriniz sizin yaratıcılığınıza kalmış

>>> [i for i in range(3,100,2) if (i%3!=0 and i%5!=0 and i%7!=0)]
[11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97] #asal sayılar.

In [90]: us_al=dict([(i,i**2) for i in range(2,10)]) #dict() ile lc'den aldığımız veriyi bir sözlüğüde aktarabiliyoruz.
In [91]: us_al
Out[91]: {2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}